[med-svn] [mauve] 01/04: Imported Upstream version 2.4.0+4734

Andreas Tille tille at debian.org
Wed Apr 22 12:13:04 UTC 2015


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository mauve.

commit 5e3ea89e4783d5b816519114dbccf57c3c3e6b48
Author: Andreas Tille <tille at debian.org>
Date:   Wed Apr 22 14:09:27 2015 +0200

    Imported Upstream version 2.4.0+4734
---
 .classpath                                         |   20 +
 .project                                           |   17 +
 .settings/org.eclipse.jdt.core.prefs               |   12 +
 .umlproject                                        |    9 +
 COPYING                                            |  340 ++++++
 README                                             |   63 ++
 TODO                                               |   68 ++
 build.xml                                          |  516 +++++++++
 build_support/appbundler-1.0.jar                   |  Bin 0 -> 133599 bytes
 debian/README.Debian                               |    7 -
 debian/bin/mauve                                   |    9 -
 debian/changelog                                   |    5 -
 debian/compat                                      |    1 -
 debian/control                                     |   46 -
 debian/copyright                                   |   21 -
 debian/get-orig-source                             |   39 -
 debian/install                                     |    2 -
 debian/patches/build_xml.patch                     |  279 -----
 debian/patches/debian_classes.patch                |   34 -
 debian/patches/series                              |    2 -
 debian/rules                                       |   38 -
 debian/source/format                               |    1 -
 debian/upstream/metadata                           |   12 -
 debian/watch                                       |    1 -
 doc/overview.txt                                   |   64 ++
 ext/core-1.9.2-SNAPSHOT.jar                        |  Bin 0 -> 2471408 bytes
 ext/goose.jar                                      |  Bin 0 -> 89124 bytes
 ext/gui-1.9.2-SNAPSHOT.jar                         |  Bin 0 -> 228711 bytes
 ext/jnlp.jar                                       |  Bin 0 -> 7042 bytes
 ext/jsc.jar                                        |  Bin 0 -> 1075447 bytes
 ext/unix-0.5.jar                                   |  Bin 0 -> 8482 bytes
 ext/zeus-jscl.jar                                  |  Bin 0 -> 128804 bytes
 jarAlignments.xml                                  |  161 +++
 jws/deployment.txt                                 |   48 +
 jws/index.html                                     |   18 +
 jws/mauve.jnlp.template                            |   29 +
 linux-x64/Mauve                                    |   34 +
 mauve.dox                                          | 1153 ++++++++++++++++++++
 notes/Biojava Renderer Notes.txt                   |   14 +
 notes/Mauve Notes.txt                              |   95 ++
 notes/icon_with_rollover_template.psd              |  Bin 0 -> 149398 bytes
 publish.xml                                        |   77 ++
 src/images/Back16.gif                              |  Bin 0 -> 183 bytes
 src/images/DarkHand16.gif                          |  Bin 0 -> 636 bytes
 src/images/Dcj16.gif                               |  Bin 0 -> 355 bytes
 src/images/Down16.gif                              |  Bin 0 -> 185 bytes
 src/images/DownRollover16.png                      |  Bin 0 -> 351 bytes
 src/images/Forward16.gif                           |  Bin 0 -> 183 bytes
 src/images/Grimm16.gif                             |  Bin 0 -> 345 bytes
 src/images/Hand16.gif                              |  Bin 0 -> 1072 bytes
 src/images/Home16.gif                              |  Bin 0 -> 420 bytes
 src/images/ScoreAssembly.png                       |  Bin 0 -> 284 bytes
 src/images/Up16.gif                                |  Bin 0 -> 184 bytes
 src/images/UpRollover16.png                        |  Bin 0 -> 358 bytes
 src/images/Zoom16.gif                              |  Bin 0 -> 303 bytes
 src/images/ZoomIn16.gif                            |  Bin 0 -> 304 bytes
 src/images/ZoomIn24.gif                            |  Bin 0 -> 484 bytes
 src/images/ZoomOut16.gif                           |  Bin 0 -> 304 bytes
 src/images/ZoomOut24.gif                           |  Bin 0 -> 477 bytes
 src/images/aPlus16.png                             |  Bin 0 -> 357 bytes
 src/images/fist_icon.gif                           |  Bin 0 -> 112 bytes
 src/images/hand_icon.gif                           |  Bin 0 -> 130 bytes
 src/images/mauve_icon.gif                          |  Bin 0 -> 555 bytes
 src/images/mauve_logo.png                          |  Bin 0 -> 18351 bytes
 src/images/minus16.png                             |  Bin 0 -> 181 bytes
 src/images/minusRollover16.png                     |  Bin 0 -> 304 bytes
 src/images/plus16.png                              |  Bin 0 -> 202 bytes
 src/images/plusRollover16.png                      |  Bin 0 -> 323 bytes
 src/images/r16.png                                 |  Bin 0 -> 292 bytes
 src/images/searchBinoculars16.png                  |  Bin 0 -> 992 bytes
 src/images/searchBinoculars24.png                  |  Bin 0 -> 1766 bytes
 src/images/searchBinoculars32.png                  |  Bin 0 -> 1735 bytes
 src/org/gel/air/util/GroupHelpers.java             |   29 +
 src/org/gel/air/util/IOUtils.java                  |  129 +++
 src/org/gel/air/util/MathUtils.java                |   34 +
 src/org/gel/air/util/StatusBox.java                |   40 +
 src/org/gel/mauve/BaseViewerModel.java             |  971 +++++++++++++++++
 src/org/gel/mauve/BrowserLauncher.java             |  824 ++++++++++++++
 src/org/gel/mauve/Chromosome.java                  |   56 +
 src/org/gel/mauve/ColorScheme.java                 |   25 +
 src/org/gel/mauve/DbXrefFactory.java               |  155 +++
 src/org/gel/mauve/DbXrefHandler.java               |   23 +
 src/org/gel/mauve/FilterCacheSpec.java             |   48 +
 src/org/gel/mauve/Genome.java                      |  204 ++++
 src/org/gel/mauve/GenomeBuilder.java               |  213 ++++
 src/org/gel/mauve/HighlightListener.java           |    7 +
 src/org/gel/mauve/LCB.java                         |  172 +++
 src/org/gel/mauve/LCBLeftComparator.java           |   32 +
 src/org/gel/mauve/LCBlist.java                     |  409 +++++++
 src/org/gel/mauve/LcbIdComparator.java             |   15 +
 src/org/gel/mauve/LcbViewerModel.java              |  759 +++++++++++++
 src/org/gel/mauve/Match.java                       |  170 +++
 src/org/gel/mauve/MatchStartComparator.java        |   41 +
 src/org/gel/mauve/MauveConstants.java              |  182 +++
 src/org/gel/mauve/MauveFormatException.java        |   11 +
 src/org/gel/mauve/MauveHelperFunctions.java        |  303 +++++
 src/org/gel/mauve/ModelBuilder.java                |  566 ++++++++++
 src/org/gel/mauve/ModelEvent.java                  |   13 +
 src/org/gel/mauve/ModelListener.java               |   22 +
 src/org/gel/mauve/ModelProgressListener.java       |   15 +
 src/org/gel/mauve/MyConsole.java                   |   47 +
 src/org/gel/mauve/OptionsBuilder.java              |   84 ++
 src/org/gel/mauve/PreferencesDump.java             |   23 +
 src/org/gel/mauve/SeqFeatureData.java              |  315 ++++++
 src/org/gel/mauve/SimilarityIndex.java             |  380 +++++++
 src/org/gel/mauve/SupportedFormat.java             |   90 ++
 src/org/gel/mauve/ViewerMode.java                  |   10 +
 src/org/gel/mauve/XMFAAlignment.java               |  885 +++++++++++++++
 src/org/gel/mauve/XmfaViewerModel.java             |  561 ++++++++++
 src/org/gel/mauve/analysis/BrokenCDS.java          |  208 ++++
 src/org/gel/mauve/analysis/CDSErrorExporter.java   |  640 +++++++++++
 src/org/gel/mauve/analysis/Gap.java                |  240 ++++
 src/org/gel/mauve/analysis/IdentityMatrix.java     |  104 ++
 src/org/gel/mauve/analysis/LiteWeightFeature.java  |  130 +++
 .../mauve/analysis/OneToOneOrthologExporter.java   | 1122 +++++++++++++++++++
 .../gel/mauve/analysis/PermutationExporter.java    |  696 ++++++++++++
 src/org/gel/mauve/analysis/SNP.java                |  331 ++++++
 src/org/gel/mauve/analysis/Segment.java            |  169 +++
 src/org/gel/mauve/analysis/SegmentComparator.java  |   65 ++
 src/org/gel/mauve/analysis/SnpExporter.java        |  502 +++++++++
 src/org/gel/mauve/analysis/TreeAnalysis.java       |   58 +
 src/org/gel/mauve/assembly/AssemblyScorer.java     |  810 ++++++++++++++
 src/org/gel/mauve/assembly/ScoreAssembly.java      |  427 ++++++++
 src/org/gel/mauve/backbone/Backbone.java           |   82 ++
 src/org/gel/mauve/backbone/BackboneList.java       |  150 +++
 .../gel/mauve/backbone/BackboneListBuilder.java    |  121 ++
 src/org/gel/mauve/chado/ChadoDB.java               |   79 ++
 src/org/gel/mauve/chado/ChadoFeatureLoader.java    |   52 +
 src/org/gel/mauve/chado/ChadoReader.java           |   41 +
 src/org/gel/mauve/chado/ChadoWriter.java           |   41 +
 src/org/gel/mauve/chado/DBUtils.java               |   18 +
 src/org/gel/mauve/color/BackboneLcbColor.java      |   34 +
 .../gel/mauve/color/BackboneMultiplicityColor.java |  209 ++++
 src/org/gel/mauve/color/ColorMenu.java             |  126 +++
 src/org/gel/mauve/color/LCBColorScheme.java        |   57 +
 .../gel/mauve/color/MultiplicityColorScheme.java   |   78 ++
 .../mauve/color/MultiplicityTypeColorScheme.java   |   46 +
 .../NormalizedMultiplicityTypeColorScheme.java     |   73 ++
 .../mauve/color/NormalizedOffsetColorScheme.java   |   82 ++
 src/org/gel/mauve/color/OffsetColorScheme.java     |   57 +
 src/org/gel/mauve/contigs/ChangedFastaFormat.java  |   25 +
 .../gel/mauve/contigs/ChangedFeatureWriter.java    |  110 ++
 src/org/gel/mauve/contigs/ContigFeatureWriter.java |  129 +++
 src/org/gel/mauve/contigs/ContigGrouper.java       |  349 ++++++
 src/org/gel/mauve/contigs/ContigHandler.java       |   37 +
 src/org/gel/mauve/contigs/ContigInverter.java      |  496 +++++++++
 .../gel/mauve/contigs/ContigMauveAlignFrame.java   |  168 +++
 src/org/gel/mauve/contigs/ContigOrderer.java       |  345 ++++++
 src/org/gel/mauve/contigs/ContigRenamer.java       |   67 ++
 src/org/gel/mauve/contigs/ContigReorderer.java     |  574 ++++++++++
 .../gel/mauve/contigs/DefaultContigHandler.java    |   60 +
 .../gel/mauve/contigs/FastAContigChangeWriter.java |  175 +++
 src/org/gel/mauve/contigs/FeatureFixer.java        |   84 ++
 .../gel/mauve/contigs/ListSequenceIterator.java    |   31 +
 src/org/gel/mauve/dcj/DCJ.java                     |  758 +++++++++++++
 src/org/gel/mauve/dcj/DCJWindow.java               |  261 +++++
 src/org/gel/mauve/dcjx/Adjacency.java              |  102 ++
 src/org/gel/mauve/dcjx/AdjacencyGraph.java         |  255 +++++
 src/org/gel/mauve/dcjx/Block.java                  |   61 ++
 src/org/gel/mauve/dcjx/BlockEnd.java               |   12 +
 src/org/gel/mauve/dcjx/Constants.java              |   12 +
 src/org/gel/mauve/dcjx/Contig.java                 |  109 ++
 src/org/gel/mauve/dcjx/DCJ.java                    |  116 ++
 src/org/gel/mauve/dcjx/DCJDistance.java            |  231 ++++
 src/org/gel/mauve/dcjx/FastAccessTable.java        |   38 +
 src/org/gel/mauve/dcjx/Head.java                   |   18 +
 src/org/gel/mauve/dcjx/Permutation.java            |  196 ++++
 src/org/gel/mauve/dcjx/Tail.java                   |   18 +
 src/org/gel/mauve/dcjx/VestigialDCJ.java           |   96 ++
 src/org/gel/mauve/format/BaseFormat.java           |  129 +++
 src/org/gel/mauve/format/DelegatingSequence.java   |  429 ++++++++
 src/org/gel/mauve/format/EmblFormat.java           |   26 +
 src/org/gel/mauve/format/FastaFormat.java          |   43 +
 src/org/gel/mauve/format/FileFinder.java           |   73 ++
 src/org/gel/mauve/format/GenbankEmblFormat.java    |  248 +++++
 src/org/gel/mauve/format/GenbankFileFormat.java    |   28 +
 src/org/gel/mauve/format/INSDseqFormat.java        |   33 +
 .../mauve/format/RawFastaBridgeFilterReader.java   |  100 ++
 src/org/gel/mauve/format/RawFormat.java            |   26 +
 .../gel/mauve/format/RichDelegatingSequence.java   |  171 +++
 .../mauve/format/RichStrandedFeatureTemplate.java  |   28 +
 .../gel/mauve/format/SequenceIteratorCache.java    |  116 ++
 .../gel/mauve/format/SupportedFormatFactory.java   |   83 ++
 src/org/gel/mauve/format/TestMain.java             |   12 +
 src/org/gel/mauve/gaggle/SampleGoose.java          |  696 ++++++++++++
 src/org/gel/mauve/gui/AlignFrame.java              |  717 ++++++++++++
 src/org/gel/mauve/gui/AlignWorker.java             |  139 +++
 .../gel/mauve/gui/AlignmentProcessListener.java    |    7 +
 src/org/gel/mauve/gui/AnalysisDisplayWindow.java   |  278 +++++
 src/org/gel/mauve/gui/ConsoleDialog.java           |   35 +
 src/org/gel/mauve/gui/ExportFrame.java             |  464 ++++++++
 src/org/gel/mauve/gui/ExportMenu.java              |  166 +++
 src/org/gel/mauve/gui/FillLayout.java              |   85 ++
 src/org/gel/mauve/gui/FrameLoader.java             |  102 ++
 src/org/gel/mauve/gui/GenomeCellRenderer.java      |   26 +
 src/org/gel/mauve/gui/HintMessageEvent.java        |   17 +
 src/org/gel/mauve/gui/HintMessageListener.java     |    7 +
 src/org/gel/mauve/gui/LCBStatusBar.java            |  195 ++++
 src/org/gel/mauve/gui/LcbLinePanel.java            |  283 +++++
 src/org/gel/mauve/gui/Mauve.java                   |  379 +++++++
 src/org/gel/mauve/gui/MauveAlignFrame.java         |  320 ++++++
 src/org/gel/mauve/gui/MauveFrame.java              |  766 +++++++++++++
 src/org/gel/mauve/gui/MauveRenderingHints.java     |   19 +
 .../gel/mauve/gui/MySymbolSequenceRenderer.java    |  116 ++
 src/org/gel/mauve/gui/PrintPreviewCanvas.java      |   77 ++
 src/org/gel/mauve/gui/PrintPreviewDialog.java      |   51 +
 src/org/gel/mauve/gui/PrintUtilities.java          |   34 +
 .../gel/mauve/gui/ProgressiveMauveAlignFrame.java  |  602 ++++++++++
 src/org/gel/mauve/gui/QualifierPanel.java          |   68 ++
 src/org/gel/mauve/gui/RearrangementPanel.java      |  976 +++++++++++++++++
 src/org/gel/mauve/gui/SequenceNavigator.java       |  745 +++++++++++++
 src/org/gel/mauve/gui/SplashScreen.java            |   91 ++
 src/org/gel/mauve/gui/StyleMenu.java               |  285 +++++
 src/org/gel/mauve/gui/SwingWorker.java             |  137 +++
 src/org/gel/mauve/gui/dnd/DnDList.java             |  264 +++++
 src/org/gel/mauve/gui/dnd/FileDrop.java            |  572 ++++++++++
 src/org/gel/mauve/gui/dnd/TransferableObject.java  |  262 +++++
 .../gui/navigation/AnnotationContainsFilter.java   |  120 ++
 .../gel/mauve/gui/navigation/NavigationPanel.java  |  203 ++++
 .../mauve/gui/navigation/SearchResultPanel.java    |  524 +++++++++
 .../mauve/gui/sequence/AbstractSequencePanel.java  |  151 +++
 src/org/gel/mauve/gui/sequence/ControlPanel.java   |  263 +++++
 .../gel/mauve/gui/sequence/FeatureFilterer.java    |  329 ++++++
 src/org/gel/mauve/gui/sequence/FeaturePanel.java   |  505 +++++++++
 .../gui/sequence/FlatFileFeatureConstants.java     |   68 ++
 .../gui/sequence/FlatFileFeatureImporter.java      |  318 ++++++
 src/org/gel/mauve/gui/sequence/HighlightPanel.java |   99 ++
 src/org/gel/mauve/gui/sequence/HistogramPanel.java |   68 ++
 src/org/gel/mauve/gui/sequence/MatchPanel.java     | 1031 +++++++++++++++++
 src/org/gel/mauve/gui/sequence/MatchPopupMenu.java |   94 ++
 .../MultiGenomeRectangularBeadRenderer.java        |   86 ++
 .../gel/mauve/gui/sequence/RRSequencePanel.java    |  193 ++++
 .../mauve/gui/sequence/RangeHighlightPanel.java    |   92 ++
 src/org/gel/mauve/gui/sequence/RulerPanel.java     |   90 ++
 src/org/gel/mauve/gui/sequence/SeqPanel.java       |  317 ++++++
 .../gui/sequence/ZiggyRectangularBeadRenderer.java |  276 +++++
 src/org/gel/mauve/histogram/HistogramBuilder.java  |  122 +++
 src/org/gel/mauve/histogram/ZoomHistogram.java     |  382 +++++++
 src/org/gel/mauve/module/MauveModule.java          |   22 +
 src/org/gel/mauve/module/MauveModuleFrame.java     |   23 +
 src/org/gel/mauve/module/ModuleListener.java       |    9 +
 .../gel/mauve/recombination/WeakArgDataModel.java  |   18 +
 .../mauve/recombination/WeakArgModelBuilder.java   |  268 +++++
 .../gel/mauve/remote/MauveDisplayCommunicator.java |   17 +
 src/org/gel/mauve/remote/MauveInterface.java       |   14 +
 src/org/gel/mauve/remote/MauveInterfaceImpl.java   |   75 ++
 src/org/gel/mauve/remote/RemoteApplet.java         |  182 +++
 src/org/gel/mauve/remote/RemoteControl.java        |    9 +
 src/org/gel/mauve/remote/RemoteControlImpl.java    |   38 +
 .../gel/mauve/remote/WargDisplayCommunicator.java  |   32 +
 src/org/gel/mauve/remote/WargInterface.java        |    8 +
 src/org/gel/mauve/summary/AnalysisModuleFrame.java |  117 ++
 src/org/gel/mauve/summary/MauveInterfacer.java     |   60 +
 src/org/gel/mauve/summary/ProcessBackboneFile.java |  183 ++++
 .../mauve/summary/output/AbstractIslandWriter.java |   22 +
 .../summary/output/AbstractMatchDataWriter.java    |  185 ++++
 .../summary/output/AbstractTabbedDataWriter.java   |  198 ++++
 .../summary/output/AlignedSequenceWriter.java      |  124 +++
 .../mauve/summary/output/BackboneCompareFile.java  |   37 +
 .../summary/output/IslandCoordinateWriter.java     |   55 +
 .../mauve/summary/output/IslandFeatureWriter.java  |   84 ++
 .../summary/output/IslandGeneFeatureWriter.java    |  208 ++++
 .../mauve/summary/output/OverviewFileWriter.java   |  238 ++++
 .../mauve/summary/output/PartialFastaWriter.java   |   68 ++
 .../mauve/summary/output/SegmentDataProcessor.java |  285 +++++
 .../mauve/summary/output/TroubleMatchWriter.java   |   98 ++
 src/org/gel/mauve/tree/FileKey.java                |   71 ++
 src/org/gel/mauve/tree/GISTree.java                |  414 +++++++
 src/org/gel/mauve/tree/GapKey.java                 |   45 +
 src/org/gel/mauve/tree/IntervalSequenceTree.java   |  320 ++++++
 src/org/gel/mauve/tree/IstIterator.java            |   31 +
 src/org/gel/mauve/tree/IstNode.java                |   67 ++
 src/org/gel/mauve/tree/Key.java                    |   17 +
 src/org/gel/mauve/tree/TreeStore.java              |   98 ++
 test/org/gel/mauve/LcbViewerModelTest.java         |   59 +
 test/org/gel/mauve/ModelLoader.java                |   44 +
 test/org/gel/mauve/XMFAAlignmentTest.java          |   95 ++
 .../gel/mauve/format/DelegatingSequenceTest.java   |   89 ++
 test/org/gel/mauve/format/MockFormat.java          |  118 ++
 test/org/gel/mauve/tree/GistNode.java              |   58 +
 test/org/gel/mauve/tree/GistTest.java              |  194 ++++
 testdata/S_bayanus_small.fasta                     |   12 +
 testdata/S_bayanus_small.raw                       |    9 +
 testdata/S_cerevisiae_small.gbk                    |  207 ++++
 testdata/small.alignment                           |   58 +
 testdata/small.mauve                               |   28 +
 testdata/small.mums                                |  128 +++
 testdata/small_raw.alignment                       |   58 +
 288 files changed, 43136 insertions(+), 497 deletions(-)

diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..c5043f8
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="var" path="JUNIT_HOME/junit.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.jdt.source_3.0.0/src/org.junit_3.8.1/junitsrc.zip"/>
+	<classpathentry kind="lib" path="ext/commons-cli-1.2.jar"/>
+	<classpathentry kind="lib" path="ext/ant-1.7.1.jar"/>
+	<classpathentry kind="lib" path="ext/jebl-0.4.jar" sourcepath="/jebl/src"/>
+	<classpathentry kind="src" path="test"/>
+	<classpathentry kind="lib" path="ext/zeus-jscl.jar"/>
+	<classpathentry kind="lib" path="ext/dbus-2.6.jar"/>
+	<classpathentry kind="lib" path="ext/unix-0.5.jar"/>
+	<classpathentry kind="lib" path="ext/jsc.jar"/>
+	<classpathentry kind="lib" path="ext/goose.jar"/>
+	<classpathentry kind="lib" path="ext/jnlp.jar"/>
+	<classpathentry kind="lib" path="ext/bytecode-1.9.2-SNAPSHOT.jar"/>
+	<classpathentry kind="lib" path="ext/core-1.9.2-SNAPSHOT.jar"/>
+	<classpathentry kind="lib" path="ext/gui-1.9.2-SNAPSHOT.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644
index 0000000..21e4562
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>mauve_trunk.branch.dcj_fix</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7ad20ce
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Sat Mar 28 14:55:37 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/.umlproject b/.umlproject
new file mode 100644
index 0000000..04ad5c4
--- /dev/null
+++ b/.umlproject
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="ASCII"?>
+<properties:ProjectDescription xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:properties="properties.xmi" modelDir="model" libraryDir="libraries" profileDir="profiles" templateDir="templates">
+  <profiles id="org.eclipse.uml2.resources.BasicProfile" description="Basic Profile" uri="platform:/plugin/org.eclipse.uml2.resources/profiles/Basic.profile.uml2" exported="false" kind="system"/>
+  <profiles id="org.eclipse.uml2.resources.IntermediateProfile" description="Intermediate Profile" uri="platform:/plugin/org.eclipse.uml2.resources/profiles/Intermediate.profile.uml2" exported="false" kind="system"/>
+  <profiles id="org.eclipse.uml2.resources.CompleteProfile" description="Complete Profile" uri="platform:/plugin/org.eclipse.uml2.resources/profiles/Complete.profile.uml2" exported="false" kind="system"/>
+  <profiles id="com.omondo.uml.core.JavaProfile" description="Java Profile" uri="platform:/plugin/com.omondo.uml.core/profiles/Java.profile.uml2" exported="false" kind="system"/>
+  <profiles id="com.omondo.uml2.core.OmondoProfile" description="Omondo Profile" uri="platform:/plugin/com.omondo.uml.core/profiles/Omondo.profile.uml2" exported="false" kind="system"/>
+  <profiles id="com.omondo.uml2.core.ArchetypeProfile" description="Archetype Profile" uri="platform:/plugin/com.omondo.uml.core/profiles/Archetype.profile.uml2" exported="false" kind="system"/>
+</properties:ProjectDescription>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..fbdd65f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644
index 0000000..1aacc85
--- /dev/null
+++ b/README
@@ -0,0 +1,63 @@
+THIS FILE IS OUT OF DATE
+The current user guide resides online at http://gel.ahabs.wisc.edu/mauve/mauve-user-guide
+
+Mauve Overview
+==============
+Mauve is a program for performing comaparative analyses of genomes.  To generate a new genome alignment with Mauve, select 'Align' from the File menu.  To open an existing genome alignment, select 'Open' from the file menu.
+
+Aligment Overview
+=================
+Once 'Align' has been selected from the File menu, a new window pops up that allows alignment parameters to be specified.  The first thing to do is add sequences to align using the 'Add Sequence' button.  Sequence files can be in FastA (.fas), Genbank (.gbk), or DNAStar (.seq) formats and MUST have the corresponding filename extension.  (Otherwise mauveAligner will crash)
+
+The output file selection is optional.  If an output file is not specified, Mauve will save the alignment in your temp directory.
+
+The Minimum LCB weight parameter specifies the minimum number of matching base pairs Mauve should use to determine wether a rearranged segment of the chromosome is significant.  Rearrangements below the weight threshold are treated as random matches and are discarded.
+
+The Minimum Island size parameter specifies the number of consecutive gaps in the alignment between a pair of sequences that mauveAligner should consider to be an island in one of the sequences.  The location of islands is written to <output filename>.islands
+
+The Full Alignment option allows Mauve to perform a recursive alignment of gapped regions, allowing for complete coverage over the genomes.  In general you will want to use this option.
+
+If 'Minimum LCB weight', 'Minimum Island size', and 'Full Alignment' are not specified, Mauve defaults
+to simply locating matches between the sequences without trying to determine boundaries of rearrangement.  This can be useful because Mauve will not filter out subset matches in this execution mode, allowing a better perspective of subset homology (try using the 'Y' color scheme with this).
+
+If Mauve is performing an alignment, it outputs the phylogenetic guide tree to <output filename>.guide_tree and outputs a gapped alignment to <output filename>.alignment
+
+Basic keyboard interface commands
+=======================
+up arrow - Zoom in
+down arrow - Zoom out
+Ctrl+left arrow - Shift left
+Ctrl+right arrow - Shift right
+Ctrl+Shift+left arrow - Big shift left
+Ctrl+Shift+right arrow - Big shift right
+
+o - Generalized offset color scheme
+i - Normalized generalized offset color scheme
+u - Multiplicity type color scheme
+y - Normalized multiplicity type color scheme
+p - Multiplicity color scheme
+l - LCB color scheme
+
+Shift+L - Toggle LCB homology tracking lines
+
+Shift+S - Lock scales to the longest range of sequence currently in view.
+
+Ctrl+p - print
+Ctrl+p - print
+print - Ctrl+p
+'A problem of type 2094 has occured'. What the (heck) is that? What are the 2093 other problems I just missed to get to that one?
+
+Mouse interface
+================
+Click on a matching region to highlight the corresponding matches in other sequences
+Right-click on a matching region for a pop-up menu to center the display on that match
+
+Running Mauve in Linux
+======================
+A shell script called "Mauve" has been included that calls java with the appropriate parameters to execute Mauve.  From the mauve directory you should be able to run Mauve by entering the command:
+./Mauve
+You may need to adjust the java command line used by the script to execute Mauve.
+
+Bugs
+=====
+Yes there are bugs.  Send reports to darling(at)cs.wisc.edu
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..2b48b77
--- /dev/null
+++ b/TODO
@@ -0,0 +1,68 @@
+DON'T USE THIS!!!!!  I'm only keeping this for updating changes or whatever.
+
+CLOSED ISSUES
+(DEFECT) Can't scroll at max zoom
+	Resolution: added some code in SeqPanel.zoomAndMove for when move is less than 1 symbol.
+(DEFECT) Printout of "doubleclick.net" when a double-click is detected.
+	Resolution: Got rid of junk double-click handling code in RRSequencePanel	
+(DEFECT) Errors printed when click to left or right of genome
+	Resolution: Added exception-handling code to RearrangementPanel.alignView()
+(FEATURE) file checks (missing, unreadable, wrong format) for loading genbank files
+	Resolution: Added checks to FeaturePanel
+(DEFECT) FeaturePanel displays at wrong zoom initially, then resizes.
+	Resolution: Added hack to FeaturePanel.setBounds()	
+(FEATURE) Validate length of Genbank sequence for target.
+	Resolution: Added check at file-loading time.
+(DEFECT) Reasonably fast-moving mouse causes clicks to be lost.
+	Resolution: Greatly improved mouseover performance, by eliminating unnecessary repainting and using threading.
+(USABILITY) How about a scrollbar instead of left-right arrows?
+	Resolution: Not done, because it will take too many resources.	
+(USABILITY) Extremely slow and memory-hungry when showing features at low magnification
+	Resolution: Currently okay again, with changes to mouse-handling.
+(DESIGN) Some panels might be getting unnecessarily repainted.
+	Resolution: Reviewed calls to repaintSeqPanels(), a very expensive call, eliminated one.
+(FEATURE) Show feature details for CDS, rRNA, tRNA and misc_RNA in popup window.
+	Resolution: Added QualifierPanel
+(DEFECT) Off by one in alignment file writing
+	Resolution: fixed by Aaron.		
+(DEFECT) FeaturePanel currently assumes genbank file will be one larger than sequence (interim fix for alignment file off by one problem).
+	Resolution: fixed by Paul, after above fix.	
+(DEFECT) NullPointerExceptions when dealing with .mauve and .mums files.
+	Resolution: revised highlighting routine	
+(DEFECT) No product qualifier for a feature causes java.util.NoSuchElementException for mouseovers.
+	Resolution: Qualifier is checked for, and only location of feature shown if not present.
+(FEATURE) Show feature symbols on single line.
+	Resolution: Using biojava features to render on one line.	
+(FEATURE) If RRSequencePanel double-clicked, do something like move the annotation browser window onto this section of sequence.
+	Resolution: Apparently, this describes the feature popup.
+(DEFECT) Rearrangement of .mums sequences fails
+	Resolution: Pushed down fix for reordering / LCB problem.		
+(USABILITY) Maybe restore variable-sized label for sequence
+	Resolution: Eh, why bother?
+(FEATURE) Update licensing to GPL.
+	Replaced COPYING with the GPL text
+(DEFECT) Text information in bottom toolbar doesn't go away when file is closed
+	Added a clear() method to LCBStatusBar, call it when final RearrangementPanel is being closed
+(DEFECT) ArrayIndexOutOfBounds exceptions when clicking on .mauve sequences after adjusting LCB weight
+	References to LCBs weren't being properly updated in filterLCBs; now they are.
+(DEFECT) When too many sequences need to fit in window need to be displayed, display punks out.
+	Taken care of by adding scrolling.		
+(USABILITY) Allow use of scrollbar for set of sequence panels.
+	Added a scrollPane to mauveFrame.
+(DEFECT) Moving LCB slider after rearranging sequences makes everything go to hell.
+	Added call to updateLCBweight() after rearrangement, in LcbViewerModel.
+(DEFECT) Selecting "LCB" color option when viewing MUMS format breaks display.
+	Initialization of color_selector is now later, when model is known, and LCB is omitted.
+(DEFECT) Slider use also breaks color picker when viewing .mauve files.
+	Added reapplication of color scheme after updating LCB weight.
+(DEFECT) Ruler shows the same number twice for different ticks at high magnification.
+	Replaced home-grown ruler panel with Ruler from biojava.
+(DEFECT) Printing output is very small.
+	Replaced some printing code, added an x-scaling.
+(USABILITY) Multi-threading viewer panels for usability during alignment
+	Silly idea, ignored.	
+(USABILITY) Ruler units float around too much, even take fractional values at high magnification
+	Taken care of by replacement
+(DEFECT) Output file name text (in AlignFrame.java) doesn't scroll right with long file names
+	Changed TextArea (?) to TextField; this was probably just a typo.
+			
\ No newline at end of file
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..d4da9cb
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,516 @@
+<project name="Mauve" default="compile">
+	<description>
+	This buildfile tells the ant build tool how to build and package the Mauve Java 
+	visualization environment.
+	</description>
+	
+	<!-- set the release version, unless making a snapshot -->
+	<target name="release_properties" unless="env.MAUVE_SNAPSHOT">
+		<property name="release.version" value="2.4.0"/>
+	</target>
+
+	<!-- deployment locations for production ASAP integration -->
+	<property name="deploy.asapdir" value="/srv/asap/htdocs/mauve" />
+	<property name="asap.codebase" value="http://asap.ahabs.wisc.edu/asap/mauve/" />
+	<!-- deployment locations for devel ASAP integration -->
+	<!--
+	<property name="deploy.asapdir" value="/srv/asap-devel/htdocs/mauve" />
+	<property name="asap.codebase" value="http://asap.ahabs.wisc.edu/asap-devel/mauve/" />
+	-->
+	<!-- deployment locations for standalone releases -->
+	<property name="deploy.address" value="darlini8 at darlinglab.org" />
+	<property name="deploy.bareaddress" value="darlinglab.org" />
+	<property name="deploy.username" value="darlini8" />
+	<property name="deploy.win32.keyfile" value="C:\Documents and Settings\koadman\plonk.ppk" />
+	<property name="deploy.serverdir" value="/home3/darlini8/www/mauve/downloads" />
+	<property name="key.alias" value="mauve" />
+	<property name="key.keystore" value="C:\Documents and Settings\koadman\My Documents\mauve-keystore" />
+	
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="bin"/>
+	<property name="dist"  location="dist"/>
+	<property name="ext" location="ext"/>
+ 	<property name="support" location="build_support"/>
+
+	<!-- Make datestamp for update checks -->
+	<tstamp>
+		<format property="datestamp" pattern="yyyyMMdd" />
+	</tstamp>
+	
+	<!-- increment the build number-->
+	<buildnumber/>
+	
+	<!-- get any environment variables -->	
+	<property environment="env" />
+
+	<!-- osxant.jar creates Mac OS X disk images -->
+	<taskdef name="dmg" classname="com.amberarcher.ant.osx.DmgTask">
+		<classpath>
+			<pathelement path="${support}/osxant.jar"/>
+		</classpath>
+	</taskdef>
+	<!-- maverick-ant.jar supports secure FTP for deployment -->
+    <taskdef name="ssh" classname="com.sshtools.ant.Ssh" classpath="${support}/maverick-ant.jar"/>
+	<taskdef resource="net/sf/antcontrib/antcontrib.properties">
+		<classpath>
+			<pathelement path="${support}/ant-contrib-1.0b3.jar"/>
+		</classpath>
+	</taskdef>
+	
+	<target name="init">
+		<!-- Create the time stamp -->
+		<tstamp/>
+	</target>
+	
+	<!-- set the timestamp if building a snapshot -->
+	<target name="snapshot_properties" if="env.MAUVE_SNAPSHOT">
+		<property name="release.version" value="${datestamp}"/>
+	</target>
+
+	<!-- set the version number -->
+	<target name="create_properties" depends="snapshot_properties,release_properties">
+		<propertyfile 
+			file="${src}/version.properties"
+			comment="Stores version and build information">
+			<entry key="release.version" value="${release.version}"/>
+			<entry key="build.number" value="${build.number}"/>
+			<entry key="build.timestamp" value="${datestamp}"/>
+		</propertyfile>
+	</target>
+	  
+	<target name="compile" depends="init,create_properties" description="compile the java source">
+		<!-- Create the build directory structure used by compile -->
+		<mkdir dir="${build}" />
+				
+		<!-- Build all java source files -->
+		<javac
+			classpath="
+				${ext}/core-1.9.2-SNAPSHOT.jar;
+				${ext}/bytecode-1.9.2-SNAPSHOT.jar;
+				${ext}/gui-1.9.2-SNAPSHOT.jar;
+				${ext}/commons-cli-1.2.jar;
+				${support}/junit.jar;
+				${ext}/zeus-jscl.jar;
+				${ext}/goose.jar;
+				${ext}/ant-1.7.1.jar;
+				${ext}/dbus-2.6.jar;
+				${ext}/jebl-0.4.jar;
+				${ext}/unix-0.5.jar"
+			srcdir="${src}"
+			destdir="${build}"
+			target="1.6"
+			source="1.6"
+		>
+			<compilerarg value="-Xbootclasspath/p:${env.JAVA_6_RT_PATH}"/>
+		</javac>
+		<mkdir dir="${build}/images"/>
+		<copy todir="${build}/images">
+			<fileset dir="${src}/images"/>
+		</copy>
+		<copy file="${src}/version.properties" todir="${build}"/>
+	</target>
+
+	<target name="rmic" depends="compile">
+		<rmic base="${build}" classname="org.gel.mauve.remote.RemoteControlImpl" />
+	</target>
+	
+	<target name="jar" depends="rmic">
+		<!-- Make list of external dependencies -->
+		<fileset dir="${ext}" id="ext.jars">
+			<include name="**/*.jar"/>
+		</fileset>
+		<pathconvert pathsep=" " property="ext.path" refid="ext.jars">
+			<mapper>
+				<chainedmapper>
+					<mapper type="flatten" />
+					<mapper type="glob" from="*.jar" to="ext/*.jar"/>
+				</chainedmapper>
+			</mapper>
+		</pathconvert>
+		
+	    <!-- Put everything in ${build} into the Mauve.jar file -->
+		<delete file="Mauve.jar"/>
+	    <jar jarfile="Mauve.jar">
+			<fileset dir="${build}" includes="**/*" />
+
+	    	<!-- Create a manifest file to tell java the name of the main class -->
+			<manifest>
+				<attribute name="Main-Class" value="org.gel.mauve.gui.Mauve"/>
+				<attribute name="Built-By" value="${user.name}"/>
+				<attribute name="Class-Path" value="${ext.path}" />
+				<section name="common">
+					<attribute name="Specification-Title" value="Mauve"/>
+					<attribute name="Specification-Version" value="${version}"/>
+					<attribute name="Specification-Vendor" value="Aaron E. Darling"/>
+					<attribute name="Implementation-Title" value="Mauve"/>
+					<attribute name="Implementation-Version" value="${version} ${TODAY}"/> 
+					<attribute name="Implementation-Vendor" value="Aaron E. Darling"/>
+				</section>
+			</manifest>
+		 </jar>
+	</target>
+	
+	<target name="appletjar" depends="rmic">
+		<delete file="mauveApplet.jar" />
+		<jar jarfile="mauveApplet.jar">
+			<fileset dir="${build}">
+				<include name="org/gel/mauve/remote/*" />
+			</fileset>
+		</jar>
+	</target>
+
+	
+
+	
+
+	<target name="checkkeyprops" unless="key.password">
+		<fail message="key.password must be set on command line, using -Dkey.password=<password>" />
+	</target>
+	
+	<target name="signjars" depends="checkkeyprops,jar,appletjar">
+		<signjar jar="Mauve.jar" alias="${key.alias}" storepass="${key.password}" keystore="${key.keystore}" />
+		<signjar jar="mauveApplet.jar" alias="${key.alias}" storepass="${key.password}" keystore="${key.keystore}" />
+	</target>
+	
+	<target name="signextjars" depends="checkkeyprops">
+		<signjar alias="${key.alias}" storepass="${key.password}" keystore="${key.keystore}">
+			<fileset dir="./ext">
+				<include name="*.jar" />
+			</fileset>
+		</signjar>
+	</target>
+	
+	<target name="testdist" depends="signjars">
+		<copy todir="C:\htdocs" file="Mauve.jar" />
+		<copy todir="C:\htdocs" file="mauveApplet.jar" />
+		<copy todir="C:\htdocs" file="./testdata/smallAlignment.jar" />
+		<copy todir="C:\htdocs" file="./jws/index.html" />
+		<copy todir="C:\htdocs" file="./jws/mauve.jnlp" />
+	</target>
+		
+	<target name="dist" depends="jar,getChangeLog" description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}" />
+		
+		<!-- Set the tarball OS name based on the build host OS -->
+		<condition property="OSNAME" value="linux">
+			<os name="Linux"/>
+		</condition>
+		<condition property="OSNAME" value="OSX">
+			<os name="Mac OS X"/>
+		</condition>
+
+		<!-- Make the tarball -->
+		<tar destfile="${dist}/mauve_${OSNAME}_${release.version}.tar.gz" compression="gzip">
+			<!-- Copy various support files into the dist directory-->
+			<tarfileset dir="." prefix="/mauve_${release.version}">
+				<include name="Mauve.jar" />
+				<include name="COPYING" />
+				<include name="README" />
+				<include name="ChangeLog.html" />
+			</tarfileset>
+			<tarfileset dir="linux-x64" prefix="/mauve_${release.version}/" mode="755">
+				<include name="Mauve" />
+			</tarfileset>
+			<tarfileset dir="linux-x64" prefix="/mauve_${release.version}/linux-x64" mode="755">
+				<include name="mauveAligner" />
+				<include name="progressiveMauve" />
+			</tarfileset>
+			<tarfileset dir="${ext}" prefix="/mauve_${release.version}/ext" includes="*.jar" />
+		</tar>
+	</target>
+
+	<taskdef name="bundleapp"
+		classname="com.oracle.appbundler.AppBundlerTask"   
+		classpath="build_support/appbundler-1.0.jar" />
+
+<!-- Create a DMG - This only works on MacOSX (requires hdiutil) -->
+<target name="dmg" depends="macdist" description="Create a DMG package for MacOSX (only works on MacOSX)">
+    
+    <!-- Set this property value to your application name -->
+    <property name="app.name" value="Mauve-${release.version}"/>
+    
+    <!-- Set this property value to a directory where you can
+     mount temporarily your images, for example /tmp -->
+    <property name="mountdir"  value="/tmp"/>
+    
+    <!-- Delete previously created DMG -->
+    <delete file="${dist}/${app.name}.dmg" quiet="yes" failonerror="false"/>
+    
+    <!-- Create a temporary Disk Image -->
+    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
+        <arg value="create"/>
+        <arg value="-srcfolder"/>
+        <arg value="staging"/>
+        <arg value="-volname"/>
+        <arg value="${app.name}"/>
+        <arg value="-ov"/>
+        <arg value="${dist}/${app.name}-tmp.dmg"/>
+        <arg value="-format"/>
+        <arg value="UDRW"/>
+    </exec>
+    
+    <!-- Attach the temporary image -->
+    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
+        <arg value="attach"/>
+        <arg value="${dist}/${app.name}-tmp.dmg"/>
+        <arg value="-mountroot"/>
+        <arg value="${mountdir}/"/>
+    </exec>
+    
+    <!-- Copy the background, the volume icon and DS_Store files -->
+    <mkdir dir="${mountdir}/${app.name}/.background"/>
+    <copy file="osx/background.png" tofile="${mountdir}/${app.name}/.background/background.png" overwrite="true"/>
+    <copy file="osx/mauve.icns" tofile="${mountdir}/${app.name}/.VolumeIcon.icns" overwrite="true"/>
+    <copy file="osx/DS_Store" tofile="${mountdir}/${app.name}/.DS_Store" overwrite="true"/>
+
+
+    <!-- Indicate that we want a custom icon -->
+    <exec executable="SetFile" os="Mac OS X">
+        <arg value="-a"/>
+        <arg value="C"/>
+        <arg value="${mountdir}/${app.name}"/>
+    </exec>
+    
+    <!-- Add a symbolic link to the Applications directory -->
+    <symlink link="${mountdir}/${app.name}" resource="/Applications"/>
+
+
+
+    <!-- Detach the temporary image -->
+    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
+        <arg value="detach"/>
+        <arg value="${mountdir}/${app.name}"/>
+    </exec>
+    
+    <!-- Compress it to a new image -->
+    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
+        <arg value="convert"/>
+        <arg value="${dist}/${app.name}-tmp.dmg"/>
+        <arg value="-format"/>
+        <arg value="UDZO"/>
+        <arg value="-o"/>
+        <arg value="${dist}/${app.name}.dmg"/>
+    </exec>
+    
+    <!-- Delete the temporary image -->
+    <delete file="${dist}/${app.name}-tmp.dmg" quiet="yes" failonerror="false"/>
+</target>
+
+
+	<!-- Create an OS X application and disk image -->
+	<target name="macdist" depends="jar,getChangeLog" description="Generate a Mac OS X disk image" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}" />
+		<delete dir="${dist}/Mauve.app"/>
+
+        <!-- get the java home -->
+        <exec executable="/usr/libexec/java_home" os="Mac OS X" failonerror="true" outputproperty="osx.java.home"/>
+		<bundleapp outputdirectory="${dist}"
+			name="Mauve"
+			displayname="Mauve ${release.version}"
+			identifier="components.Mauve"
+			icon="osx/mauve.icns"
+			shortversion="${release.version}"
+			copyright="(c) 2003-2015  http://darlinglab.org/mauve"
+			mainclassname="org.gel.mauve.gui.Mauve">
+			<classpath file="Mauve.jar" />
+			<classpath file="ext/*.jar" />
+			<librarypath file="osx/progressiveMauve" />
+			<librarypath file="osx/mauveAligner" />
+            <runtime dir="${osx.java.home}" />
+            <option value="-Dapple.laf.useScreenMenuBar=false"/>
+            <option value="-Xmx500m"/>
+		</bundleapp>
+		
+		<!-- Create a staging directory for the Disk Image filesystem -->
+		<mkdir dir="staging"/>
+        <exec executable="ditto" failonerror="true">
+                <arg value="-v"/>
+                <arg value="${dist}/Mauve.app"/>
+                <arg value="staging/Mauve.app"/>
+        </exec>
+		<copy todir="staging">
+			<fileset dir="${basedir}">
+				<include name="ChangeLog.html"/>
+			</fileset>
+		</copy>
+		
+		<!-- copy text files to the staging directory, adding .txt
+		     to their filename -->
+		<copy todir="staging">
+			<fileset dir="${basedir}">
+				<include name="COPYING"/>
+			</fileset>
+			<mapper type="glob" from="*" to="*.txt"/>
+		</copy>
+		<chmod file="staging/Mauve.app/Contents/MacOS/JavaAppLauncher" perm="755"/>
+		<chmod file="staging/Mauve.app/Contents/MacOS/mauveAligner" perm="755"/>
+		<chmod file="staging/Mauve.app/Contents/MacOS/progressiveMauve" perm="755"/>
+
+<!-- sign the app -->
+<exec executable="/usr/bin/codesign" os="Mac OS X" failonerror="true">
+    <arg value="-s"/>
+    <arg value="Developer ID Application: Aaron Darling"/>
+    <arg value="--deep"/>
+    <arg value="staging/Mauve.app"/>
+</exec>
+<!---->
+
+<!--        <dmg destfile="${dist}/Mauve-${release.version}.dmg" name="Mauve ${release.version}" srcdir="staging" compressed="true"/> -->
+	</target>
+
+	<target name="srcdist" depends="init" description="package a source distribution">
+		<tar destfile="${dist}/mauve_source_${datestamp}.tar.gz" compression="gzip">
+			<!-- Copy support files into the dist directory-->
+			<tarfileset dir="." prefix="/mauve-src">
+				<include name="COPYING" />
+				<include name="Mauve" />
+				<include name="README" />
+				<include name="TODO" />
+				<include name="ChangeLog.html" />
+				<include name="build.xml" />
+				<include name="mauve.nsi" />
+				<include name="mauve.dox" />
+				<include name="mauve.ico" />
+				<include name=".project" />
+				<include name=".classpath" />
+				<include name="mauve.icns" />
+				<include name="Mauve Online Documentation.url" />
+				<include name="Mauve.lnk" />
+			</tarfileset>
+			<tarfileset dir="${src}" includes="**/*" prefix="/mauve-src/src" />
+		</tar>
+	</target>
+
+	<target name="getChangeLog" >
+		<get src="http://darlinglab.org/mauve/user-guide/versions.html" dest="ChangeLog.html" usetimestamp="true"/>
+		<copy file="ChangeLog.html" tofile="ChangeLog"/>
+	</target>
+
+	<target name="nsicompile" depends="jar,getChangeLog" description="make windows installer" >
+
+		<!-- create output directory -->
+		<mkdir dir="${dist}" />
+
+		<!-- Make timestamp and timestampdash for installer making -->
+		<property environment="env" />
+		<echo message="Program files: ${env.ProgramFiles}"/>
+		<exec executable="C:\Program Files (x86)\NSIS\makensis.exe" dir=".">
+			<arg line="/V2 /NOCD win32\mauve.nsi" />
+			<env key="release_version" value="${release.version}" />
+			<env key="datestamp" value="${datestamp}" />
+		</exec>
+	</target>
+		
+	
+	<target name="run" depends="jar" description="run Mauve">
+		<java jar="Mauve.jar" fork="true" />
+	</target>
+	
+	<target name="asapResources">
+		<pathconvert property="asap.resources" pathsep=" ">
+			<path>
+				<fileset dir="./ext">
+					<include name="*.jar" />
+				</fileset>
+			</path>
+			<mapper>			
+		        <chainedmapper>
+		    		<mapper type="flatten" />
+		    		<mapper type="glob" from="*" to="<jar href="*" />
"/>
+		        </chainedmapper>
+			</mapper>
+		</pathconvert>
+	</target>
+	
+	<target name="deployASAP" depends="signjars, asapResources">
+		
+		<echo message="Deploying mauve ASAP stuff to directory ${asap.dir}" />
+		<echo message="Using codebase of ${asap.codebase}" />
+		
+		<!-- update and copy mauve.jnlp to dist-->
+		<copy file="./jws/mauve.jnlp.template" tofile="${dist}/mauve.jnlp" overwrite="yes">
+			<filterset>
+				<filter token="CODEBASE" value="${asap.codebase}"/>
+				<filter token="RESOURCES" value="${asap.resources}"/>
+				<filter token="ARGUMENTS" value=""/>
+			</filterset>
+		</copy>
+
+		<!-- copy unmodified files to the server -->
+		<ssh host="${deploy.bareaddress}"
+			username="${deploy.username}"
+			version="2"
+			keyfile="${deploy.win32.keyfile}">
+			<sftp action="put" remotedir="${deploy.asapdir}" verbose="true" newer="true">
+				<fileset dir=".">
+					<include name="Mauve.jar" />
+					<include name="mauveApplet.jar" />
+				</fileset>
+				<fileset dir="./ext">
+					<include name="*.jar"/>
+				</fileset>
+				<fileset dir="./src/images">
+					<include name="mauve_icon.gif" />
+					<include name="mauve_logo.png" />
+				</fileset>
+				<fileset dir="${dist}">
+					<include name="mauve.jnlp"/>
+				</fileset>
+			</sftp>
+			<!-- set the permissions so others in the group can modify them -->
+			<exec cmd="chgrp annotation ${deploy.asapdir}/*"/>
+			<exec cmd="chmod 664 ${deploy.asapdir}/*"/>
+			<!-- jar files should have the execute bit set -->
+			<exec cmd="chmod 775 ${deploy.asapdir}/*.jar"/>
+		</ssh>
+	</target>
+	
+	<!-- assumes that an ssh key has been registered with the web server -->
+	<target name="deployWin32" depends="nsicompile">
+		<echo file="${dist}\latest" message="${datestamp}"/>
+		<echo file="${dist}\latest.windows" message="${datestamp}"/>
+		<ssh host="${deploy.bareaddress}"
+			username="${deploy.username}"
+			version="2"
+			keyfile="${deploy.win32.keyfile}">
+
+			<sftp action="put" remotedir="${deploy.serverdir}" verbose="true">
+				<fileset dir="${dist}">
+					<include name="mauve_installer_${release.version}.exe"/>
+					<include name="${dist}\latest"/>
+					<include name="${dist}\latest.windows"/>
+				</fileset>
+			</sftp>
+			<exec cmd="cp ${deploy.serverdir}/mauve_installer_${release.version}.exe ${deploy.serverdir}/mauve_installer_${datestamp}.exe"/>
+		</ssh>
+	</target>
+
+	<!-- assumes that passwordless ssh authentication has been configured to the web server -->
+	<target name="deployMacOSX" depends="dmg">
+		<exec executable="scp">
+			<arg value="${dist}/Mauve-${release.version}.dmg" />
+			<arg value="${deploy.address}:${deploy.serverdir}" />
+		</exec>
+		<echo file="${dist}/latest.mac" message="${datestamp}"/>
+		<exec executable="scp">
+			<arg value="${dist}/latest.mac" />
+			<arg value="${deploy.address}:${deploy.serverdir}" />
+		</exec>
+	</target>
+
+	<!-- assumes that passwordless ssh authentication has been configured to the web server -->
+	<target name="deployLinux" depends="dist">
+		<exec executable="scp">
+			<arg value="${dist}/mauve_linux_${release.version}.tar.gz" />
+			<arg value="${deploy.address}:${deploy.serverdir}" />
+		</exec>
+		<echo file="${dist}/latest.linux" message="${datestamp}"/>
+		<exec executable="scp">
+			<arg value="${dist}/latest.linux" />
+			<arg value="${deploy.address}:${deploy.serverdir}" />
+		</exec>
+	</target>
+</project>
diff --git a/build_support/appbundler-1.0.jar b/build_support/appbundler-1.0.jar
new file mode 100644
index 0000000..ef30f1c
Binary files /dev/null and b/build_support/appbundler-1.0.jar differ
diff --git a/debian/README.Debian b/debian/README.Debian
deleted file mode 100644
index 5cc7e5c..0000000
--- a/debian/README.Debian
+++ /dev/null
@@ -1,7 +0,0 @@
-libMems for Debian
-------------------
-
-This library is packaged as a precondition of the Mauve
-multiple genome alignment package.
-
- -- Andreas Tille <tille at debian.org>  Fri, 17 Apr 2015 15:38:22 +0200
diff --git a/debian/bin/mauve b/debian/bin/mauve
deleted file mode 100644
index 7ff0258..0000000
--- a/debian/bin/mauve
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-JAVA_CMD=java
-JAVA_ARGS="-Xms200M -Xmx500M"
-
-DEBJAR="/usr/share/java"
-
-$JAVA_CMD $JAVA_ARGS -DmauveDir=${DEBJAR}/ -classpath "${CLASSPATH}:${DEBJAR}/bytecode.jar:${DEBJAR}/commons-cli.jar:${DEBJAR}/jebl.jar" -jar ${DEBJAR}/Mauve.jar $@
-## :{DEBJAR}/postgresql-jdbc4.jar 
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index e1eea8e..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,5 +0,0 @@
-mauve (2.4.0+4734-1) UNRELEASED; urgency=medium
-
-  * Initial release (Closes: #??????)
-
- -- Andreas Tille <tille at debian.org>  Fri, 17 Apr 2015 15:38:22 +0200
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 5af45ef..0000000
--- a/debian/control
+++ /dev/null
@@ -1,46 +0,0 @@
-Source: mauve
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>
-Section: science
-Priority: optional
-Build-Depends: debhelper (>= 9),
-               dh-autoreconf,
-               javahelper,
-               default-jdk,
-               ant,
-               ant-contrib,
-               libbytecode-java,
-               libcommons-cli-java,
-               libdbus-java,
-               libjebl2-java,
-               libpostgresql-jdbc-java
-Standards-Version: 3.9.6
-Vcs-Browser: http://anonscm.debian.org/viewvc/debian-med/trunk/packages/mauve/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/mauve/trunk/
-Homepage: http://darlinglab.org/mauve/
-
-Package: mauve
-Architecture: any
-Depends: ${shlibs:Depends},
-         ${misc:Depends}
-Description: multiple genome alignment
- Mauve is a system for efficiently constructing multiple genome alignments
- in the presence of large-scale evolutionary events such as rearrangement
- and inversion. Multiple genome alignment provides a basis for research
- into comparative genomics and the study of evolutionary dynamics.  Aligning
- whole genomes is a fundamentally different problem than aligning short
- sequences.
- .
- Mauve has been developed with the idea that a multiple genome aligner
- should require only modest computational resources. It employs algorithmic
- techniques that scale well in the amount of sequence being aligned. For
- example, a pair of Y. pestis genomes can be aligned in under a minute,
- while a group of 9 divergent Enterobacterial genomes can be aligned in
- a few hours.
- .
- Mauve computes and interactively visualizes genome sequence comparisons.
- Using FastA or GenBank sequence data, Mauve constructs multiple genome
- alignments that identify large-scale rearrangement, gene gain, gene loss,
- indels, and nucleotide substutition.
- .
- Mauve is developed at the University of Wisconsin.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 516ac9b..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,21 +0,0 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: libMems
-Upstream-Contact: Aaron Darling <darling at cs.wisc.edu>
-Source: http://sourceforge.net/p/mauve/code/HEAD/tree/libMems/trunk/
-
-Files: *
-Copyright: 2003 - 2014 Aaron Darling -- darling at cs.wisc.edu
-License: GPL-2+
-
-Files: debian/*
-Copyright: 2015 Andreas Tille <tille at debian.org>
-License: GPL-2+
-
-License: GPL-2+
- This package is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- .
- On Debian systems, the complete text of the licenses can be found in:
- GPL-2 - `/usr/share/common-licenses/GPL-2'
diff --git a/debian/get-orig-source b/debian/get-orig-source
deleted file mode 100755
index d9e55f5..0000000
--- a/debian/get-orig-source
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/sh
-# get source for mauve from SVN because there is no source tarball distribution
-
-set -e
-NAME=`dpkg-parsechangelog | awk '/^Source/ { print $2 }'`
-
-if ! echo $@ | grep -q upstream-version ; then
-    VERSION=`dpkg-parsechangelog | awk '/^Version:/ { print $2 }' | sed 's/\([0-9\.]\+\)-[0-9]\+$/\1/'`
-else
-    VERSION=`echo $@ | sed "s?^.*--upstream-version \([0-9.]\+\) .*${NAME}.*?\1?"`
-    if echo "$VERSION" | grep -q "upstream-version" ; then
-        echo "Unable to parse version number"
-        exit
-    fi
-fi
-
-SVNURI="svn://svn.code.sf.net/p/mauve/code/mauve/trunk"
-revision=`LANG=C svn info ${SVNURI} | grep "^Last Changed Rev:" | sed 's/Last Changed Rev: *//'`
-VERSION=`echo ${VERSION}| sed "s/+[0-9]\+$//"`+${revision}
-
-TARDIR=${NAME}-${VERSION}
-
-mkdir -p ../tarballs
-cd ../tarballs
-# svn export conserves time stamps of the files, checkout does not
-LC_ALL=C svn export ${SVNURI} ${TARDIR} >/dev/null 2>/dev/null
-
-cd ${TARDIR}
-rm -rf osx win32 win64
-rm -rf linux-x64/mauveAligner linux-x86/mauveAligner linux-x64/progressiveMauve linux-x86/progressiveMauve
-for libjava in ant bytecode commons-cli dbus jebl postgres ; do
-   find . -name "*${libjava}*.jar" -delete
-done
-rm -rf build_support/retroweaver
-rm -rf build_support/[b-z]*
-cd ..
-
-tar --owner=root --group=root --mode=a+rX -caf "$NAME"_"$VERSION".orig.tar.xz "${TARDIR}"
-rm -rf "${NAME}"-"$VERSION"
diff --git a/debian/install b/debian/install
deleted file mode 100644
index 0e291bb..0000000
--- a/debian/install
+++ /dev/null
@@ -1,2 +0,0 @@
-debian/bin	usr	
-Mauve.jar	/usr/share/java
diff --git a/debian/patches/build_xml.patch b/debian/patches/build_xml.patch
deleted file mode 100644
index a7fbd35..0000000
--- a/debian/patches/build_xml.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-Author: Andreas Tille <tille at debian.org>
-Last-Updated: Fri, 17 Apr 2015 15:38:22 +0200
-Description: Drop OSX specific things from build.xml,
- Prevent build from calling home at build time
-
---- a/build.xml
-+++ b/build.xml
-@@ -19,7 +19,6 @@
- 	-->
- 	<!-- deployment locations for standalone releases -->
- 	<property name="deploy.address" value="darlini8 at darlinglab.org" />
--	<property name="deploy.bareaddress" value="darlinglab.org" />
- 	<property name="deploy.username" value="darlini8" />
- 	<property name="deploy.win32.keyfile" value="C:\Documents and Settings\koadman\plonk.ppk" />
- 	<property name="deploy.serverdir" value="/home3/darlini8/www/mauve/downloads" />
-@@ -44,14 +43,6 @@
- 	<!-- get any environment variables -->	
- 	<property environment="env" />
- 
--	<!-- osxant.jar creates Mac OS X disk images -->
--	<taskdef name="dmg" classname="com.amberarcher.ant.osx.DmgTask">
--		<classpath>
--			<pathelement path="${support}/osxant.jar"/>
--		</classpath>
--	</taskdef>
--	<!-- maverick-ant.jar supports secure FTP for deployment -->
--    <taskdef name="ssh" classname="com.sshtools.ant.Ssh" classpath="${support}/maverick-ant.jar"/>
- 	<taskdef resource="net/sf/antcontrib/antcontrib.properties">
- 		<classpath>
- 			<pathelement path="${support}/ant-contrib-1.0b3.jar"/>
-@@ -189,7 +180,7 @@
- 		<copy todir="C:\htdocs" file="./jws/mauve.jnlp" />
- 	</target>
- 		
--	<target name="dist" depends="jar,getChangeLog" description="generate the distribution" >
-+	<target name="dist" depends="jar" description="generate the distribution" >
- 		<!-- Create the distribution directory -->
- 		<mkdir dir="${dist}" />
- 		
-@@ -225,141 +216,6 @@
- 		classname="com.oracle.appbundler.AppBundlerTask"   
- 		classpath="build_support/appbundler-1.0.jar" />
- 
--<!-- Create a DMG - This only works on MacOSX (requires hdiutil) -->
--<target name="dmg" depends="macdist" description="Create a DMG package for MacOSX (only works on MacOSX)">
--    
--    <!-- Set this property value to your application name -->
--    <property name="app.name" value="Mauve-${release.version}"/>
--    
--    <!-- Set this property value to a directory where you can
--     mount temporarily your images, for example /tmp -->
--    <property name="mountdir"  value="/tmp"/>
--    
--    <!-- Delete previously created DMG -->
--    <delete file="${dist}/${app.name}.dmg" quiet="yes" failonerror="false"/>
--    
--    <!-- Create a temporary Disk Image -->
--    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
--        <arg value="create"/>
--        <arg value="-srcfolder"/>
--        <arg value="staging"/>
--        <arg value="-volname"/>
--        <arg value="${app.name}"/>
--        <arg value="-ov"/>
--        <arg value="${dist}/${app.name}-tmp.dmg"/>
--        <arg value="-format"/>
--        <arg value="UDRW"/>
--    </exec>
--    
--    <!-- Attach the temporary image -->
--    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
--        <arg value="attach"/>
--        <arg value="${dist}/${app.name}-tmp.dmg"/>
--        <arg value="-mountroot"/>
--        <arg value="${mountdir}/"/>
--    </exec>
--    
--    <!-- Copy the background, the volume icon and DS_Store files -->
--    <mkdir dir="${mountdir}/${app.name}/.background"/>
--    <copy file="osx/background.png" tofile="${mountdir}/${app.name}/.background/background.png" overwrite="true"/>
--    <copy file="osx/mauve.icns" tofile="${mountdir}/${app.name}/.VolumeIcon.icns" overwrite="true"/>
--    <copy file="osx/DS_Store" tofile="${mountdir}/${app.name}/.DS_Store" overwrite="true"/>
--
--
--    <!-- Indicate that we want a custom icon -->
--    <exec executable="SetFile" os="Mac OS X">
--        <arg value="-a"/>
--        <arg value="C"/>
--        <arg value="${mountdir}/${app.name}"/>
--    </exec>
--    
--    <!-- Add a symbolic link to the Applications directory -->
--    <symlink link="${mountdir}/${app.name}" resource="/Applications"/>
--
--
--
--    <!-- Detach the temporary image -->
--    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
--        <arg value="detach"/>
--        <arg value="${mountdir}/${app.name}"/>
--    </exec>
--    
--    <!-- Compress it to a new image -->
--    <exec executable="/usr/bin/hdiutil" os="Mac OS X" failonerror="true">
--        <arg value="convert"/>
--        <arg value="${dist}/${app.name}-tmp.dmg"/>
--        <arg value="-format"/>
--        <arg value="UDZO"/>
--        <arg value="-o"/>
--        <arg value="${dist}/${app.name}.dmg"/>
--    </exec>
--    
--    <!-- Delete the temporary image -->
--    <delete file="${dist}/${app.name}-tmp.dmg" quiet="yes" failonerror="false"/>
--</target>
--
--
--	<!-- Create an OS X application and disk image -->
--	<target name="macdist" depends="jar,getChangeLog" description="Generate a Mac OS X disk image" >
--		<!-- Create the distribution directory -->
--		<mkdir dir="${dist}" />
--		<delete dir="${dist}/Mauve.app"/>
--
--        <!-- get the java home -->
--        <exec executable="/usr/libexec/java_home" os="Mac OS X" failonerror="true" outputproperty="osx.java.home"/>
--		<bundleapp outputdirectory="${dist}"
--			name="Mauve"
--			displayname="Mauve ${release.version}"
--			identifier="components.Mauve"
--			icon="osx/mauve.icns"
--			shortversion="${release.version}"
--			copyright="(c) 2003-2015  http://darlinglab.org/mauve"
--			mainclassname="org.gel.mauve.gui.Mauve">
--			<classpath file="Mauve.jar" />
--			<classpath file="ext/*.jar" />
--			<librarypath file="osx/progressiveMauve" />
--			<librarypath file="osx/mauveAligner" />
--            <runtime dir="${osx.java.home}" />
--            <option value="-Dapple.laf.useScreenMenuBar=false"/>
--            <option value="-Xmx500m"/>
--		</bundleapp>
--		
--		<!-- Create a staging directory for the Disk Image filesystem -->
--		<mkdir dir="staging"/>
--        <exec executable="ditto" failonerror="true">
--                <arg value="-v"/>
--                <arg value="${dist}/Mauve.app"/>
--                <arg value="staging/Mauve.app"/>
--        </exec>
--		<copy todir="staging">
--			<fileset dir="${basedir}">
--				<include name="ChangeLog.html"/>
--			</fileset>
--		</copy>
--		
--		<!-- copy text files to the staging directory, adding .txt
--		     to their filename -->
--		<copy todir="staging">
--			<fileset dir="${basedir}">
--				<include name="COPYING"/>
--			</fileset>
--			<mapper type="glob" from="*" to="*.txt"/>
--		</copy>
--		<chmod file="staging/Mauve.app/Contents/MacOS/JavaAppLauncher" perm="755"/>
--		<chmod file="staging/Mauve.app/Contents/MacOS/mauveAligner" perm="755"/>
--		<chmod file="staging/Mauve.app/Contents/MacOS/progressiveMauve" perm="755"/>
--
--<!-- sign the app -->
--<exec executable="/usr/bin/codesign" os="Mac OS X" failonerror="true">
--    <arg value="-s"/>
--    <arg value="Developer ID Application: Aaron Darling"/>
--    <arg value="--deep"/>
--    <arg value="staging/Mauve.app"/>
--</exec>
--<!---->
--
--<!--        <dmg destfile="${dist}/Mauve-${release.version}.dmg" name="Mauve ${release.version}" srcdir="staging" compressed="true"/> -->
--	</target>
- 
- 	<target name="srcdist" depends="init" description="package a source distribution">
- 		<tar destfile="${dist}/mauve_source_${datestamp}.tar.gz" compression="gzip">
-@@ -384,27 +240,6 @@
- 		</tar>
- 	</target>
- 
--	<target name="getChangeLog" >
--		<get src="http://darlinglab.org/mauve/user-guide/versions.html" dest="ChangeLog.html" usetimestamp="true"/>
--		<copy file="ChangeLog.html" tofile="ChangeLog"/>
--	</target>
--
--	<target name="nsicompile" depends="jar,getChangeLog" description="make windows installer" >
--
--		<!-- create output directory -->
--		<mkdir dir="${dist}" />
--
--		<!-- Make timestamp and timestampdash for installer making -->
--		<property environment="env" />
--		<echo message="Program files: ${env.ProgramFiles}"/>
--		<exec executable="C:\Program Files (x86)\NSIS\makensis.exe" dir=".">
--			<arg line="/V2 /NOCD win32\mauve.nsi" />
--			<env key="release_version" value="${release.version}" />
--			<env key="datestamp" value="${datestamp}" />
--		</exec>
--	</target>
--		
--	
- 	<target name="run" depends="jar" description="run Mauve">
- 		<java jar="Mauve.jar" fork="true" />
- 	</target>
-@@ -439,68 +274,8 @@
- 			</filterset>
- 		</copy>
- 
--		<!-- copy unmodified files to the server -->
--		<ssh host="${deploy.bareaddress}"
--			username="${deploy.username}"
--			version="2"
--			keyfile="${deploy.win32.keyfile}">
--			<sftp action="put" remotedir="${deploy.asapdir}" verbose="true" newer="true">
--				<fileset dir=".">
--					<include name="Mauve.jar" />
--					<include name="mauveApplet.jar" />
--				</fileset>
--				<fileset dir="./ext">
--					<include name="*.jar"/>
--				</fileset>
--				<fileset dir="./src/images">
--					<include name="mauve_icon.gif" />
--					<include name="mauve_logo.png" />
--				</fileset>
--				<fileset dir="${dist}">
--					<include name="mauve.jnlp"/>
--				</fileset>
--			</sftp>
--			<!-- set the permissions so others in the group can modify them -->
--			<exec cmd="chgrp annotation ${deploy.asapdir}/*"/>
--			<exec cmd="chmod 664 ${deploy.asapdir}/*"/>
--			<!-- jar files should have the execute bit set -->
--			<exec cmd="chmod 775 ${deploy.asapdir}/*.jar"/>
--		</ssh>
--	</target>
--	
--	<!-- assumes that an ssh key has been registered with the web server -->
--	<target name="deployWin32" depends="nsicompile">
--		<echo file="${dist}\latest" message="${datestamp}"/>
--		<echo file="${dist}\latest.windows" message="${datestamp}"/>
--		<ssh host="${deploy.bareaddress}"
--			username="${deploy.username}"
--			version="2"
--			keyfile="${deploy.win32.keyfile}">
--
--			<sftp action="put" remotedir="${deploy.serverdir}" verbose="true">
--				<fileset dir="${dist}">
--					<include name="mauve_installer_${release.version}.exe"/>
--					<include name="${dist}\latest"/>
--					<include name="${dist}\latest.windows"/>
--				</fileset>
--			</sftp>
--			<exec cmd="cp ${deploy.serverdir}/mauve_installer_${release.version}.exe ${deploy.serverdir}/mauve_installer_${datestamp}.exe"/>
--		</ssh>
--	</target>
--
--	<!-- assumes that passwordless ssh authentication has been configured to the web server -->
--	<target name="deployMacOSX" depends="dmg">
--		<exec executable="scp">
--			<arg value="${dist}/Mauve-${release.version}.dmg" />
--			<arg value="${deploy.address}:${deploy.serverdir}" />
--		</exec>
--		<echo file="${dist}/latest.mac" message="${datestamp}"/>
--		<exec executable="scp">
--			<arg value="${dist}/latest.mac" />
--			<arg value="${deploy.address}:${deploy.serverdir}" />
--		</exec>
- 	</target>
--
-+	
- 	<!-- assumes that passwordless ssh authentication has been configured to the web server -->
- 	<target name="deployLinux" depends="dist">
- 		<exec executable="scp">
diff --git a/debian/patches/debian_classes.patch b/debian/patches/debian_classes.patch
deleted file mode 100644
index 2291120..0000000
--- a/debian/patches/debian_classes.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-Author: Andreas Tille <tille at debian.org>
-Last-Updated: Fri, 17 Apr 2015 15:38:22 +0200
-Description: Use Debian packaged classes
-
---- a/build.xml
-+++ b/build.xml
-@@ -30,6 +30,7 @@
- 	<property name="build" location="bin"/>
- 	<property name="dist"  location="dist"/>
- 	<property name="ext" location="ext"/>
-+        <property name="deblib" location="/usr/share/java" />
-  	<property name="support" location="build_support"/>
- 
- 	<!-- Make datestamp for update checks -->
-@@ -78,15 +79,15 @@
- 		<javac
- 			classpath="
- 				${ext}/core-1.9.2-SNAPSHOT.jar;
--				${ext}/bytecode-1.9.2-SNAPSHOT.jar;
-+				${deblib}/bytecode.jar;
- 				${ext}/gui-1.9.2-SNAPSHOT.jar;
--				${ext}/commons-cli-1.2.jar;
-+				${deblib}/commons-cli.jar;
- 				${support}/junit.jar;
- 				${ext}/zeus-jscl.jar;
- 				${ext}/goose.jar;
- 				${ext}/ant-1.7.1.jar;
--				${ext}/dbus-2.6.jar;
--				${ext}/jebl-0.4.jar;
-+				${deblib}/dbus.jar;
-+				${deblib}/jebl.jar;
- 				${ext}/unix-0.5.jar"
- 			srcdir="${src}"
- 			destdir="${build}"
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index a204ff7..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1,2 +0,0 @@
-build_xml.patch
-debian_classes.patch
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 89ef888..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/make -f
-# -*- makefile -*-
-# debian/rules file for mauve
-# Andreas Tille <tille at debian.org>
-# GPL
-
-#export DH_VERBOSE=1
-
-PACKAGE := $(shell dpkg-parsechangelog | sed -n 's/^Source: //p')
-
-JAVA_HOME  := /usr/lib/jvm/default-java
-
-DEBJAR    := /usr/share/java
-CLASS_PATH := $(DEBJAR)/bytecode.jar:$(DEBJAR)/commons-cli.jar:$(DEBJAR)/dbus.jar:$(DEBJAR)/jebl.jar
-## :$(DEBJAR)/postgresql-jdbc4.jar 
-
-# to run the test suite
-JAVA       := $(JAVA_HOME)/bin/java
-ANT_HOME   := /usr/share/ant
-ANT_BIN    := $(ANT_HOME)/bin/ant
-ANT_ARGS   := -Dcompile.debug=true -Dcompile.optimize=true
-
-%:
-	dh $@ --with javahelper
-
-override_dh_auto_clean:
-	echo "Manual cleaning needed since build.xml has no clean target"
-	find . -name "*.class" -delete
-	rm -rf bin dist
-	rm -rf build.number src/version.properties
-	rm -rf Mauve.jar ChangeLog*
-
-override_dh_auto_build:
-	# CLASSPATH=$(CLASS_PATH)
-	ant dist
-
-get-orig-source:
-	. debian/get-orig-source
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/upstream/metadata b/debian/upstream/metadata
deleted file mode 100644
index b38c69e..0000000
--- a/debian/upstream/metadata
+++ /dev/null
@@ -1,12 +0,0 @@
-Reference:
-  Author: "Mauve: multiple alignment of conserved genomic sequence with rearrangements"
-  Title: Aaron C. E. Darling and Bob Mau and Frederick R. Blattner and Nicole T. Perna
-  Journal: Genome research
-  Year: 2004
-  Volume: 14
-  Number: 7
-  Pages: 1394-1403
-  DOI: 10.1101/gr.2289704 
-  PMID: 15231754
-  URL: http://genome.cshlp.org/content/14/7/1394.short
-  eprint: http://genome.cshlp.org/content/14/7/1394.full.pdf+html
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index cf22602..0000000
--- a/debian/watch
+++ /dev/null
@@ -1 +0,0 @@
-# There is no tarball download location, libMuscle is only available in SVN
diff --git a/doc/overview.txt b/doc/overview.txt
new file mode 100644
index 0000000..b787a3c
--- /dev/null
+++ b/doc/overview.txt
@@ -0,0 +1,64 @@
+When approaching the set of classes that comprise Mauve, my first advice is: Don't Panic.  Whilst there are a large number of classes and collaborations, each one can be understood,  especially when the "big picture" is kept in mind.  The purpose of this document is to explain the big picture.
+
+I.  Models.
+
+The viewer displays models, which are loaded from alignment files.  There is a hierarchy of models, corresponding to the layering on of features in progressively later versions of Mauve.  These are BaseViewerModel, LcbViewerModel and XmfaViewerModel.  
+
+BaseViewerModel contains, mostly, a lot of stuff that is common to all three models.  
+
+
+
+-A.  Some concepts
+	View ordering vs. Source ordering.
+	Reordering madness
+		When sequences are reordered, what happens, and when.
+		
+		BaseViewerModel.reorderSequences(int[]) calls, in turn, startReorder(int[]), finishReorder() and fireReorderGenomeEvent().  If the concrete class is BaseViewerModel, all of the action occurs in startReorder(int[]), as finishReorder() is just a hook for subclasses.  BaseViewerModel.startReorder(int[]) permutes the array of genomes, thereby making sure that the viewing order and storage order are the same.  The viewingIndex of each genome is also updated in this process.  Note that this  [...]
+		
+	
+		LcbViewerModel overrides startReorder(int[]).  First, the BaseViewerModel.startReorder(int[]) is called to permute the array of genomes.  Next, reorderLCBs(int[]) is called, which invokes lcb.reorder(int[]) on each LCB in the model.  The callback LcbViewModel.finishReorder() is then invoked, which causes the reapplication of the minimum weight through an invocation of updateLCBweight(), which smells like a very iffy bug patch.  Note that this causes the firing of a weightChange event.  [...]
+		
+		XmfaViewerModel.startReorder first invokes LcbViewerModel.startReorder(int[]).  This causes.  First, the BaseViewerModel.startReorder(int[]) is called to permute the array of genomes.  Next, reorderLCBs(int[]) is called, which invokes lcb.reorder(int[]) on each LCB in the model.  Then XMFAAlignment.reorder(int[]) is called, which causes the reordering of the seq_length list, each LCB, and the GISTree array.  The callback LcbViewModel.finishReorder() is then invoked, which causes the re [...]
+		
+A.  Source-related stuff
+	BaseViewerModel(File)
+	getSrc()
+	setSourceURL(URL)
+	getSourceURL()
+	
+B.  Genome containment
+	setSequenceCount(int sequenceCount)
+	getSequenceCount()
+	getViewingOrder()
+	getGenome(int)
+	setGenome(int, Genome)
+
+C.  Match containment
+	getMatchCount()
+	getMatch(int)
+	sortedMatches(Comparator)
+	addMatch(Match)
+
+D.  Color scheme
+	setColorScheme(ColorScheme)
+	getColorScheme()
+	
+E.  Event firing
+	addModelListener(ModelListener)
+	removeModelListener(ModelListener)
+	addHighlightListener(HighlightListener)
+	removeHighlightListener(HighlightListener)
+	fireXXXevent
+
+F.  Modality
+	setMode(ViewerMode)
+	getMode()
+
+G.  Genome reordering
+	reorderSequences(int[])
+	startReorder(int[])
+	finishReorder()
+	correctReversals()
+	
+
+	
\ No newline at end of file
diff --git a/ext/core-1.9.2-SNAPSHOT.jar b/ext/core-1.9.2-SNAPSHOT.jar
new file mode 100644
index 0000000..5315acc
Binary files /dev/null and b/ext/core-1.9.2-SNAPSHOT.jar differ
diff --git a/ext/goose.jar b/ext/goose.jar
new file mode 100644
index 0000000..a10b37c
Binary files /dev/null and b/ext/goose.jar differ
diff --git a/ext/gui-1.9.2-SNAPSHOT.jar b/ext/gui-1.9.2-SNAPSHOT.jar
new file mode 100644
index 0000000..383a7a1
Binary files /dev/null and b/ext/gui-1.9.2-SNAPSHOT.jar differ
diff --git a/ext/jnlp.jar b/ext/jnlp.jar
new file mode 100644
index 0000000..8fd98f2
Binary files /dev/null and b/ext/jnlp.jar differ
diff --git a/ext/jsc.jar b/ext/jsc.jar
new file mode 100644
index 0000000..88d2af0
Binary files /dev/null and b/ext/jsc.jar differ
diff --git a/ext/unix-0.5.jar b/ext/unix-0.5.jar
new file mode 100644
index 0000000..729868b
Binary files /dev/null and b/ext/unix-0.5.jar differ
diff --git a/ext/zeus-jscl.jar b/ext/zeus-jscl.jar
new file mode 100644
index 0000000..d4be36c
Binary files /dev/null and b/ext/zeus-jscl.jar differ
diff --git a/jarAlignments.xml b/jarAlignments.xml
new file mode 100644
index 0000000..dee5b8f
--- /dev/null
+++ b/jarAlignments.xml
@@ -0,0 +1,161 @@
+<project name="Mauve" default="makeMyJar">
+	<description>
+	Use this ant build script to create alignment JAR files that
+	can be loaded with the viewer
+	</description>
+	
+
+	<!-- deployment locations for production ASAP integration -->
+<!--
+	<property name="asap.dir" value="\\192.168.26.120\darling\productionasap\htdocs\mauve" />
+	<property name="asap.codebase" value="http://asap.ahabs.wisc.edu/asap/mauve/" />
+-->
+	<!-- deployment locations for devel ASAP integration -->
+	<property name="asap.dir" value="\\192.168.26.120\darling\develasap\htdocs\mauve" />
+	<property name="asap.codebase" value="http://asap.ahabs.wisc.edu/asap-devel/mauve/" />
+	      
+	<target name="makeTestJar">
+	    <jar jarfile="smallAlignment.jar">
+			<fileset dir="testdata">
+				<include name="small.alignment" />
+				<include name="S_bayanus_small.fasta" />
+				<include name="S_cerevisiae_small.gbk" />
+			</fileset>
+	        <manifest>
+				<attribute name="Mauve-Alignment" value="small.alignment" />
+				<attribute name="Sequence-1-ID" value="12_34" />
+				<attribute name="Sequence-2-ID" value="56_78" />
+	        </manifest>
+		</jar>
+	</target>
+	
+	<target name="makeMyJar" depends="asapResources">
+		<!--
+		<jar jarfile="coliAlignment.jar">
+			<fileset dir="C:\Development\mauveAligner\bin\asap\coli">
+				<include name="coli.alignment" />
+				<include name="WIS_MG1655_m56.gbk" />
+				<include name="WIS_EDL933_vers1.gbk" />
+				<include name="WIS_EcoRIMD_v1.gbk" />
+				<include name="WIS_CFT073_v1.gbk" />
+			</fileset>
+			<manifest>
+				<attribute name="Mauve-Alignment" value="coli.alignment" />
+				<attribute name="Sequence-1-ID" value="MG1655_WIS" />
+				<attribute name="Sequence-2-ID" value="EDL933_WIS" />
+				<attribute name="Sequence-3-ID" value="EcoRIMD_WIS" />
+				<attribute name="Sequence-4-ID" value="CFT073_WIS" />
+			</manifest>
+		</jar>
+		-->
+<!--
+		<jar jarfile="sam5Alignment.jar">
+			<fileset dir="C:\Development\mauveAligner\bin\asap\salmonella">
+				<include name="sam5.alignment" />
+				<include name="WIS_SenLT2_v1.gbk" />
+				<include name="WIS_SenChB67_1.gbk" />
+				<include name="WIS_SenPA9150_1.gbk" />
+				<include name="WIS_SenCT18_v1.gbk" />
+				<include name="WIS_SenTy2_v1.gbk" />
+			</fileset>
+			<manifest>
+				<attribute name="Mauve-Alignment" value="sam5.alignment" />
+				<attribute name="Sequence-1-ID" value="SenLT2_WIS" />
+				<attribute name="Sequence-2-ID" value="SenChB67_WIS" />
+				<attribute name="Sequence-3-ID" value="SenPA9150_WIS" />
+				<attribute name="Sequence-4-ID" value="SenCT18_WIS" />
+				<attribute name="Sequence-5-ID" value="SenTy2_WIS" />
+			</manifest>
+		</jar>
+-->
+		<!--
+		<jar jarfile="k12echAlignment.jar">
+			<fileset dir="C:\Development\mauveAligner\bin\asap\ech_k12">
+				<include name="ech_k12.alignment" />
+				<include name="WIS_MG1655_m56.gbk" />
+				<include name="Erwinia_chysanthemi_3937_v6b.gbk" />
+			</fileset>
+			<manifest>
+				<attribute name="Mauve-Alignment" value="ech_k12.alignment" />
+				<attribute name="Sequence-1-ID" value="MG1655_WIS" />
+				<attribute name="Sequence-2-ID" value="ECH3937_WIS" />
+			</manifest>
+		</jar>
+		-->
+<!--
+		<jar jarfile="yeast4_v1.jar">
+			<fileset dir="C:\Development\mauveAligner\bin\yeast">
+				<include name="yeast4.alignment" />
+				<include name="S_cerevisiae.gbk" />
+				<include name="S_bayanus.fasta" />
+				<include name="S_miktae.fasta" />
+				<include name="S_paradoxus.fasta" />
+			</fileset>
+			<manifest>
+				<attribute name="Mauve-Alignment" value="yeast4.alignment" />
+				<attribute name="Sequence-1-ID" value="S_cerevisiae" />
+				<attribute name="Sequence-2-ID" value="S_bayanus" />
+				<attribute name="Sequence-3-ID" value="S_miktae" />
+				<attribute name="Sequence-4-ID" value="S_paradoxus" />
+			</manifest>
+		</jar>
+-->
+<!--
+		<property name="alignment.name" value="yersinia" />
+		<jar jarfile="${alignment.name}.jar">
+			<fileset dir="C:\Development\mauveAligner\bin\asap\yersinia">
+				<include name="yersinia.alignment" />
+				<include name="WIS_YPKIM_vers1.gbk" />
+				<include name="WIS_YP91001_vers1.gbk" />
+				<include name="WIS_YPCO92_v1.gbk" />
+				<include name="WIS_Ypt32953_1.gbk" />
+			</fileset>
+			<manifest>
+				<attribute name="Mauve-Alignment" value="yersinia.alignment" />
+				<attribute name="Sequence-1-ID" value="YPKIM_WIS" />
+				<attribute name="Sequence-2-ID" value="YP91001_WIS" />
+				<attribute name="Sequence-3-ID" value="YPCO92_WIS" />
+				<attribute name="Sequence-4-ID" value="Ypt32953_WIS" />
+			</manifest>
+		</jar>
+-->
+		<property name="alignment.name" value="shigella" />
+		<jar jarfile="${alignment.name}.jar">
+			<fileset dir="C:\Development\mauveAligner\bin\asap\shigella">
+				<include name="shigella.alignment" />
+				<include name="WIS_Sfl2457T_v1.gbk" />
+				<include name="WIS_Sfl301_v1.gbk" />
+			</fileset>
+			<manifest>
+				<attribute name="Mauve-Alignment" value="shigella.alignment" />
+				<attribute name="Sequence-1-ID" value="Sfl2457T_WIS" />
+				<attribute name="Sequence-2-ID" value="Sfl301_WIS" />
+			</manifest>
+		</jar>
+		<!-- create a mauve.jnlp for this alignment -->
+		<copy file="./jws/mauve.jnlp.template" tofile="${alignment.name}.jnlp" overwrite="yes">
+			<filterset>
+				<filter token="CODEBASE" value="${asap.codebase}"/>
+				<filter token="RESOURCES" value="${asap.resources}"/>
+				<filter token="ARGUMENTS" value="<argument>http://gel.ahabs.wisc.edu/mauve/alignments/${alignment.name}.jar</argument>"/>
+			</filterset>
+		</copy>
+	</target>
+	
+	<target name="asapResources">
+		<pathconvert property="asap.resources" pathsep=" ">
+			<path>
+				<fileset dir="./ext">
+					<include name="*.jar" />
+				</fileset>
+			</path>
+			<mapper>			
+		        <chainedmapper>
+		    		<mapper type="flatten" />
+		    		<mapper type="glob" from="*" to="<jar href="*" />
"/>
+		        </chainedmapper>
+			</mapper>
+		</pathconvert>
+	</target>
+	
+</project>
diff --git a/jws/deployment.txt b/jws/deployment.txt
new file mode 100644
index 0000000..72fab29
--- /dev/null
+++ b/jws/deployment.txt
@@ -0,0 +1,48 @@
+Pick some directory MAUVEDIR in which to put application jars and such.  This 
+should be accessible via the path MAUVEPATH.
+
+I.  Set up files. (Do this for every deployment).
+
+	1.  Set the properties "asap.dir" to MAUVEDIR and "asap.codebase" to MAUVEPATH
+		in build.xml.  Then run the target "deployASAP".  This will copy over the old
+		version, if present.  *If additional external dependencies are added to the
+		project, then use the "signextjars" target to add sign them.
+		
+		You will need to set the "key.password" property on the command line, using
+		the option "-Dkey.password=<password>"
+		
+		This task updates a templatized JNLP file, so don't edit the JNLP file on the
+		server, unless you want those changes to be lost in the future, which I doubt
+		you would want.  It also copies required files, signs jars, all that nifty stuff.
+
+II.  Update PHP code. (Only required on first setup)
+
+	1.  Include the mauve-launching applet in basic_feature_info.php page:
+	
+		See lines 279-283 of ~pinfield/public_html/asap/basic_feature_info.  
+		This should be copied entirely, changing the CODEBASE attribute to reflect
+		the MAUVEPATH chosen.
+
+	2.  Include link to open alignment at a particular location:
+
+		See lines 160-163 of ~pinfield/public_html/asap/basic_feature_info.php for an
+		example.  *The line beginning "$alignmentJarURL..." will need to be 
+		adjusted to point to a meaningful jar file.*  Note the trailing "!/" in 
+		the URL; this is very much required.
+
+III.  Package alignments
+
+	1.  Package alignment.
+	
+		See "makeMixedJar" target in build.xml for an example.  The file contents 
+		are the alignment file and source files.  In the manifest, the following
+		attributes are required:
+
+		Mauve-Alignment: the name of the alignment file.
+		Sequence-N-ID: the combination of ASAP genome id and location ID for the 
+		Nth sequence, separated by an underscore.  
+
+	2.  Put alignment where it belongs.
+	
+		Copy the resulting alignment file to wherever it needs to live to be
+		accessible at URL generated for basic_feature_info link.
diff --git a/jws/index.html b/jws/index.html
new file mode 100644
index 0000000..5aebaee
--- /dev/null
+++ b/jws/index.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Test JWS page</title>
+<script language="javascript">
+	function doit()
+	{
+		document.app.goTo("jar:http://snefru.gel.local/smallAlignment.jar!/", "12_34", 100, 300);
+	}
+</script>
+</head>
+<body>
+<applet name="app"
+	code="org.gel.mauve.remote.RemoteApplet"
+    archive="mauveApplet.jar"
+    width="500" height="120"></applet>
+<a href="javascript:doit()">Click me</a>
+</body>
+</html>
diff --git a/jws/mauve.jnlp.template b/jws/mauve.jnlp.template
new file mode 100644
index 0000000..c4e9046
--- /dev/null
+++ b/jws/mauve.jnlp.template
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This file is AUTO-GENERATED.  Edit mauve.jnlp.template to make permanent changes! -->
+<jnlp spec="1.0+" codebase="@CODEBASE@" href="mauve.jnlp">
+  <information>
+    <title>Mauve</title>
+    <vendor>University of Wisconsin - Madison</vendor>
+    <homepage href="docs/help.html"/>
+    <description>Mauve Application</description>
+    <icon href="mauve_icon.gif"/>
+    <icon kind="splash" href="mauve_logo.png"/>
+    <offline-allowed/>
+  </information>
+  <security>
+      <all-permissions/>
+  </security>
+  <resources>
+    <j2se version="1.4+" java-vm-args="-Xmx500m"/>
+    <jar href="Mauve.jar"/>
+    <!-- begin auto-generated section -->
+    @RESOURCES@
+    <!-- end auto-generated section -->
+	<property name="mauve.enable.remote" value="any_value_means_yes" />
+  </resources>
+  <application-desc main-class="org.gel.mauve.gui.Mauve">
+    <!-- begin auto-generated section -->
+  	@ARGUMENTS@
+    <!-- end auto-generated section -->
+  </application-desc>
+</jnlp> 
\ No newline at end of file
diff --git a/linux-x64/Mauve b/linux-x64/Mauve
new file mode 100755
index 0000000..57b5f6e
--- /dev/null
+++ b/linux-x64/Mauve
@@ -0,0 +1,34 @@
+#!/bin/sh
+# simple script to execute java with the appropriate options to run Mauve
+# Change JAVA_CMD to the location of your java binary if necessary
+# Change the values of the -Xms and -Xmx parameters if you are getting
+# out of memory errors
+#
+# (c) 2003 Aaron Darling
+
+JAVA_CMD=java
+JAVA_ARGS="-Xms200M -Xmx500M"
+
+# this section taken from GPL BEAST code
+## resolve links - $0 may be a link to application
+PRG="$0"
+
+# need this for relative symlinks
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+PRG="$link"
+    else
+PRG="`dirname "$PRG"`/$link"
+    fi
+done
+
+# make it fully qualified
+saveddir=`pwd`
+MAUVE0=`dirname "$PRG"`
+MAUVE=`cd "$MAUVE0" && pwd`
+cd "$saveddir"
+#end BEAST section
+
+$JAVA_CMD $JAVA_ARGS -DmauveDir=$MAUVE/ -jar $MAUVE/Mauve.jar $1 $2 $3 $4 $5
diff --git a/mauve.dox b/mauve.dox
new file mode 100644
index 0000000..6f7ac79
--- /dev/null
+++ b/mauve.dox
@@ -0,0 +1,1153 @@
+# Doxyfile 1.3.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for the mpiBLAST project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = Mauve
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 1.0.0
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of source 
+# files, where putting all generated files in the same directory would otherwise 
+# cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is used 
+# as the annotated text. Otherwise, the brief description is used as-is. If left 
+# blank, the following values are used ("$name" is automatically replaced with the 
+# name of the entity): "The $name class" "The $name widget" "The $name file" 
+# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS          = *.java
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = *getopt*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superseded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = YES
diff --git a/notes/Biojava Renderer Notes.txt b/notes/Biojava Renderer Notes.txt
new file mode 100644
index 0000000..3fa1d4c
--- /dev/null
+++ b/notes/Biojava Renderer Notes.txt	
@@ -0,0 +1,14 @@
+TranslatedSequencePanel is the top-level element that needs to be added
+to the mauve GUI.  The sequence panel has, most importantly, a sequence
+to be rendered, and a renderer to renderer it.  It also has some doodads
+to control the part of the sequence being rendered.
+
+Using MultiLineRenderer as the rendered for a TranslatedSequencePanel
+allows for the rendering of multiple views of the same sequence.
+
+The blocks for the features are created using the FeatureBlockSequenceRenderer.
+The FeatureBlockSequenceRenderer delegated the rendering of features to FeatureRenderers,
+which actually go about doing the drawing (using a Graphics2D object, the feature, etc).
+These FeatureBlockSequenceRenderers are then wrapped up in a FeatureFilter, that only causes
+select features to be rendered by each particular FeatureRenderer.
+
diff --git a/notes/Mauve Notes.txt b/notes/Mauve Notes.txt
new file mode 100644
index 0000000..a09bb7f
--- /dev/null
+++ b/notes/Mauve Notes.txt	
@@ -0,0 +1,95 @@
+MauveFrame has the menu items, an AlignFrame and A DnDRearrangementPanel.
+
+AlignFrame provides a GUI interface to the alignment tool, and so is of
+no consequence at the moment.  
+
+DnDRearrangementPanel extends RearrangmentPanel by allowing drag-and-drop
+opening of alignment files.  This is especially useful for setting up the
+viewing window with multiple alignments.  
+
+RearrangementPanel is the mother of all GUI elements.
+
+============
+
+Initial plan of attack:
+
+RearrangementPanel currently maintains two parallel Vectors of RulePanels and 
+RRSequencePanels, as well as a bunch of arrays and some fields that constitute 
+the data model for the application.  We need to add to this some number of
+panels containing feature data a la FastBeadDemo.
+
+Rather than further complicate an already overly large class, I will convert the
+RearrangementPanel into (basically) an association of a collection of child panels,
+each of which will contain a ruler, sequence and optional feature display.  The 
+RearrangementPanel will also maintain a reference to a separate data model.  This
+data model will contain the arrays etc that the RearrangementPanel currently holds, 
+plus whatever additional data is necessary for the feature display.
+
+============
+
+General problems
+- Data model and GUI code are too intertwined
+- Lots of commented-out code
+- No tests, manual or otherwise
+- Spotty comments
+
+============
+
+
+How sequencePanel vector is used:
+
+DnDRearrangementPanel:
+	drag
+		- To determine the target for dragging
+	* drop
+		- To determine the sequencePanel target for dropping
+		- To determine the rulerPanel target for dropping
+
+LcbLinePanel
+	- To obtain LCBs that should be connected
+	
+RearrangementPanel
+	actionPerformed
+		- to add or remove mouse listeners when the hand button is activated
+	*X alignView
+		- to find the viewable range(*) given a clicked-on sequence 
+		- to adjust the viewable range for all the other sequences
+		- to adjust the viewable range for all the other rulers
+	highlightRegions
+		- to iterate through sequences, clearing highlighting and then redrawing.  In some indirect way, this causes the highlight lines to move around under the mouse.
+	*X initMatchDisplay
+		- Add a ruler for each sequence in the model
+		- Add a sequencePanel for each sequence in the model.
+	keyPressed
+		- to iterate through sequences, toggling different pieces of the display of each sequence (boxes, lines, etc)
+	* layoutDisplay
+		- to iterate through sequences, forcing their being relaid-out
+		- includes relaying-out ruler
+	print
+		- to iterate through sequences, doing some stuff for printing (seems to construct a different layout for printing, I think).
+	* reorderSequences
+		- to put sequencePanels in a different order, based on an array of integers
+		- to put rulerPanels in a different order, based on an array of integers
+		- sequencePanels vector is replaced entirely in this method
+		- rulerPanels vector is replaced entirely in this method
+	*X repaintSequencePanels
+		- to iterate through and repaint every sequence and ruler panel
+	stateChanged
+		- to iterate through sequences and either show blocky or non-block stuff
+	* zoomAndMove
+		- to iterate through sequences and change their viewable ranges, redraw.  
+		- to iterate through rulers and change their viewable ranges, redraw.
+
+==============================
+
+
+          Matches      XMFA      LCBs       Sim
+
+MUMS         X
+
+MAUVE        X                     X
+
+XMFA         *          X          X         X
+		
+		* XMFA matches come from the XMFA file, used differently.
+
diff --git a/notes/icon_with_rollover_template.psd b/notes/icon_with_rollover_template.psd
new file mode 100644
index 0000000..6dc2da1
Binary files /dev/null and b/notes/icon_with_rollover_template.psd differ
diff --git a/publish.xml b/publish.xml
new file mode 100644
index 0000000..7e23b04
--- /dev/null
+++ b/publish.xml
@@ -0,0 +1,77 @@
+<project name="PublishMauve" default="publish">
+	<description>
+		This ant script publishes a Mauve release to the freshmeat web site.
+    </description>
+	
+	<!-- set the release version -->
+	<property name="release.version" value="2.1.1"/>
+
+	<!-- set global properties for this build -->
+	<property name="support" location="build_support"/>
+
+
+	<taskdef name="freshmeat"
+		classname="de.frewert.ant.freshmeat.Announcement">
+	    <classpath>
+			<pathelement path="${support}/xmlrpc-1.2-b1.jar"/>
+			<pathelement path="${support}/antmeat.jar"/>
+	    </classpath>
+	</taskdef>
+
+	<target name="publish" description="Publish a release to freshmeat">
+
+		<freshmeat xmlrpcserver="http://freshmeat.net/xmlrpc/"
+		      username="darling"
+		      password="darling">
+	
+			<publish projectName="Mauve"
+				branchName="Default"
+				version="${release.version}"
+				focus="majorBugfixes"
+				hidden="false">
+	
+			<!-- focus can be one of 	
+			1	initialAnnouncement	
+			2	documentation	
+			3	cleanup	
+			4	minorEnhancements	
+			5	majorEnhancements	
+			6	minorBugfixes	
+			7	majorBugfixes	
+			8	minorSecurityFixes	
+			9	majorSecurityFixes
+			 -->
+	
+				<!-- Changes are an english prose summary of changes, 
+				     limited to 300 characters -->
+				<changes>
+					The new release fixes a missing library problem on Mac OS X.  Other fixes include reduced memory usage in some cases, fix for the failure of the sequence navigator to highlight annotation search results, and a fixed divide-by-zero that rarely arose in progressiveMauve.
+				</changes>
+		 
+				<urlBlock
+				    homepage="http://genome-alignment.org/mauve/"
+				    tgz="http://gel.ahabs.wisc.edu/mauve/downloads/mauve_linux_${release.version}.tar.gz"
+				    osx="http://gel.ahabs.wisc.edu/mauve/downloads/Mauve-${release.version}.dmg"
+				    changelog="http://asap.ahabs.wisc.edu/mauve-aligner/mauve-user-guide/mauve-version-history.html"
+				    mailinglist="http://sourceforge.net/mail/?group_id=181544"
+				/>
+			</publish>
+		</freshmeat>
+	</target>
+
+
+	<target name="withdraw" description="Withdraw a release from freshmeat">
+		<freshmeat xmlrpcserver="http://freshmeat.net/xmlrpc/"
+		      username=""
+		      password="">
+		      
+			<withdraw
+				projectName="Mauve"
+				branchName="Default"
+				version="${release.version}"
+			/>
+	
+		</freshmeat>
+	</target>
+
+</project>
diff --git a/src/images/Back16.gif b/src/images/Back16.gif
new file mode 100644
index 0000000..f48362d
Binary files /dev/null and b/src/images/Back16.gif differ
diff --git a/src/images/DarkHand16.gif b/src/images/DarkHand16.gif
new file mode 100644
index 0000000..f32f5b7
Binary files /dev/null and b/src/images/DarkHand16.gif differ
diff --git a/src/images/Dcj16.gif b/src/images/Dcj16.gif
new file mode 100644
index 0000000..ec45616
Binary files /dev/null and b/src/images/Dcj16.gif differ
diff --git a/src/images/Down16.gif b/src/images/Down16.gif
new file mode 100644
index 0000000..3984918
Binary files /dev/null and b/src/images/Down16.gif differ
diff --git a/src/images/DownRollover16.png b/src/images/DownRollover16.png
new file mode 100644
index 0000000..0fa4226
Binary files /dev/null and b/src/images/DownRollover16.png differ
diff --git a/src/images/Forward16.gif b/src/images/Forward16.gif
new file mode 100644
index 0000000..d25a3f9
Binary files /dev/null and b/src/images/Forward16.gif differ
diff --git a/src/images/Grimm16.gif b/src/images/Grimm16.gif
new file mode 100644
index 0000000..f7e9a51
Binary files /dev/null and b/src/images/Grimm16.gif differ
diff --git a/src/images/Hand16.gif b/src/images/Hand16.gif
new file mode 100644
index 0000000..a8d26fb
Binary files /dev/null and b/src/images/Hand16.gif differ
diff --git a/src/images/Home16.gif b/src/images/Home16.gif
new file mode 100644
index 0000000..3a78ec3
Binary files /dev/null and b/src/images/Home16.gif differ
diff --git a/src/images/ScoreAssembly.png b/src/images/ScoreAssembly.png
new file mode 100644
index 0000000..00bc4e2
Binary files /dev/null and b/src/images/ScoreAssembly.png differ
diff --git a/src/images/Up16.gif b/src/images/Up16.gif
new file mode 100644
index 0000000..cebe60d
Binary files /dev/null and b/src/images/Up16.gif differ
diff --git a/src/images/UpRollover16.png b/src/images/UpRollover16.png
new file mode 100644
index 0000000..9cbf548
Binary files /dev/null and b/src/images/UpRollover16.png differ
diff --git a/src/images/Zoom16.gif b/src/images/Zoom16.gif
new file mode 100644
index 0000000..9e48896
Binary files /dev/null and b/src/images/Zoom16.gif differ
diff --git a/src/images/ZoomIn16.gif b/src/images/ZoomIn16.gif
new file mode 100644
index 0000000..2329426
Binary files /dev/null and b/src/images/ZoomIn16.gif differ
diff --git a/src/images/ZoomIn24.gif b/src/images/ZoomIn24.gif
new file mode 100644
index 0000000..dbd4477
Binary files /dev/null and b/src/images/ZoomIn24.gif differ
diff --git a/src/images/ZoomOut16.gif b/src/images/ZoomOut16.gif
new file mode 100644
index 0000000..f9f7565
Binary files /dev/null and b/src/images/ZoomOut16.gif differ
diff --git a/src/images/ZoomOut24.gif b/src/images/ZoomOut24.gif
new file mode 100644
index 0000000..259bf9c
Binary files /dev/null and b/src/images/ZoomOut24.gif differ
diff --git a/src/images/aPlus16.png b/src/images/aPlus16.png
new file mode 100644
index 0000000..510bd53
Binary files /dev/null and b/src/images/aPlus16.png differ
diff --git a/src/images/fist_icon.gif b/src/images/fist_icon.gif
new file mode 100644
index 0000000..836e98b
Binary files /dev/null and b/src/images/fist_icon.gif differ
diff --git a/src/images/hand_icon.gif b/src/images/hand_icon.gif
new file mode 100644
index 0000000..655a124
Binary files /dev/null and b/src/images/hand_icon.gif differ
diff --git a/src/images/mauve_icon.gif b/src/images/mauve_icon.gif
new file mode 100644
index 0000000..1f37d77
Binary files /dev/null and b/src/images/mauve_icon.gif differ
diff --git a/src/images/mauve_logo.png b/src/images/mauve_logo.png
new file mode 100644
index 0000000..e2505aa
Binary files /dev/null and b/src/images/mauve_logo.png differ
diff --git a/src/images/minus16.png b/src/images/minus16.png
new file mode 100644
index 0000000..2b6fc0a
Binary files /dev/null and b/src/images/minus16.png differ
diff --git a/src/images/minusRollover16.png b/src/images/minusRollover16.png
new file mode 100644
index 0000000..9a85d54
Binary files /dev/null and b/src/images/minusRollover16.png differ
diff --git a/src/images/plus16.png b/src/images/plus16.png
new file mode 100644
index 0000000..b7a4a1c
Binary files /dev/null and b/src/images/plus16.png differ
diff --git a/src/images/plusRollover16.png b/src/images/plusRollover16.png
new file mode 100644
index 0000000..d9ce105
Binary files /dev/null and b/src/images/plusRollover16.png differ
diff --git a/src/images/r16.png b/src/images/r16.png
new file mode 100644
index 0000000..ca384a8
Binary files /dev/null and b/src/images/r16.png differ
diff --git a/src/images/searchBinoculars16.png b/src/images/searchBinoculars16.png
new file mode 100644
index 0000000..6bd9865
Binary files /dev/null and b/src/images/searchBinoculars16.png differ
diff --git a/src/images/searchBinoculars24.png b/src/images/searchBinoculars24.png
new file mode 100644
index 0000000..e710e19
Binary files /dev/null and b/src/images/searchBinoculars24.png differ
diff --git a/src/images/searchBinoculars32.png b/src/images/searchBinoculars32.png
new file mode 100644
index 0000000..5266f13
Binary files /dev/null and b/src/images/searchBinoculars32.png differ
diff --git a/src/org/gel/air/util/GroupHelpers.java b/src/org/gel/air/util/GroupHelpers.java
new file mode 100644
index 0000000..f064fa8
--- /dev/null
+++ b/src/org/gel/air/util/GroupHelpers.java
@@ -0,0 +1,29 @@
+package org.gel.air.util;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+
+public class GroupHelpers {
+
+	public static void arrayToCollection (Collection group, Object [] data) {
+		for (int i = 0; i < data.length; i++)
+			group.add (data[i]);
+	}
+	
+	public static boolean collectionsEqual (Collection a, Collection b, 
+			Comparator comp) {
+		if (a.size() == b.size()) {
+			Iterator one = a.iterator();
+			Iterator two = b.iterator();
+			while (one.hasNext()) {
+				if (comp.compare(one.next(), two.next ()) != 0)
+					return false;
+			}
+			return true;
+		}
+		else
+			return false;
+	}
+
+}
diff --git a/src/org/gel/air/util/IOUtils.java b/src/org/gel/air/util/IOUtils.java
new file mode 100644
index 0000000..51d63d7
--- /dev/null
+++ b/src/org/gel/air/util/IOUtils.java
@@ -0,0 +1,129 @@
+package org.gel.air.util;
+
+import java.io.BufferedInputStream;
+
+
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.Hashtable;
+
+public class IOUtils {
+	/**
+	 * Copies <code>source</code> to <code>dest</code>.
+	 * Overwrites or creates <code>dest</code> if necessary
+	 * @param source source file
+	 * @param dest destination file
+	 * @throws IOException 
+	 */
+	public static void copyFile (File sourceFile, File destFile) throws IOException {
+		System.out.println("Copying...\n\t" + sourceFile.getAbsolutePath() +"\nto\n\t" + destFile.getAbsolutePath());
+		if(!destFile.exists()) {
+			 destFile.createNewFile();
+		}
+			 
+		FileChannel source = null;
+		FileChannel destination = null;
+		try {
+			source = new FileInputStream(sourceFile).getChannel();
+			destination = new FileOutputStream(destFile).getChannel();
+			destination.transferFrom(source, 0, source.size());
+		} finally {
+			if(source != null) {
+				source.close();
+			}
+			if(destination != null) {
+				destination.close();
+			}
+		}
+	}
+	
+	/**
+	 * for each arg that starts with a '-', puts it in as key with next arg as value
+	 * @param args
+	 * @return
+	 */
+	public static Hashtable <String, String> parseDashPairedArgs (String [] args) {
+		int i = 0;
+		Hashtable <String, String> pairs = new Hashtable <String, String> ();
+		while (i < args.length) {
+			if (args [i].charAt (0) == '-') {
+				if (args [i].charAt(1) == '-') {
+				int equals = args [i].indexOf('=');
+				if (equals == -1)
+					equals = args [i].length();
+				pairs.put(args [i].substring(0, equals), 
+						equals == args [i].length() ? "" :
+						args [i].substring(equals + 1));
+				i++;
+				}
+				else if (i + 1 < args.length)
+					pairs.put(args [i++], args [i++]);
+			}
+			else
+				i++;
+		}
+		return pairs;
+	}
+	
+	public static void deleteDir (File dir) {
+		try {
+			File files [] = dir.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				if (files [i].isDirectory())
+					deleteDir (files [i]);
+				else
+					files [i].delete();
+			}
+			dir.delete();
+		} catch (Exception e) {
+		}
+	}
+
+	public static void writeLongArray (DataOutputStream out, long [] data) {
+		try {
+			for (int i = 0; i < data.length; i++)
+				out.writeLong(data [i]);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public static long [] readLongArray (DataInputStream in, int length) {
+		try {
+			long [] data = new long [length];
+			for (int i = 0; i < data.length; i++)
+				data [i] = in.readLong ();
+			return data;
+		} catch (IOException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+	
+	public static DataInputStream getDataInputStream (String file) {
+		try {
+			return new DataInputStream (new BufferedInputStream (new FileInputStream (file)));
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+	
+	public static DataOutputStream getDataOutputStream (String file) {
+		try {
+			return new DataOutputStream (new BufferedOutputStream (
+					new FileOutputStream (file)));
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+}
diff --git a/src/org/gel/air/util/MathUtils.java b/src/org/gel/air/util/MathUtils.java
new file mode 100644
index 0000000..b44fd1c
--- /dev/null
+++ b/src/org/gel/air/util/MathUtils.java
@@ -0,0 +1,34 @@
+package org.gel.air.util;
+
+public class MathUtils {
+	
+	public static double percentContained (long in_left, long in_right, 
+			long out_left, long out_right) {
+		long length = in_right - in_left;
+		long hang = 0;
+		if (in_left < out_left)
+			hang = out_left - in_left;
+		if (in_right > out_right)
+			hang += in_right - out_right;
+		return ((double) length - hang) / length * 100;
+	}
+	
+	public static int compareByStartThenLength (int start1, int end1, int start2,
+			int end2) {
+		/*rval = a.starts[i] - b.starts[i];
+		if (rval < 0)
+			ret = -1;
+		else if (rval > 0)
+			ret = 1;
+		else if (i > BY_MULTIPLICITY){
+			rval = a.lengths [i] - b.lengths [i];
+			ret = rval > 0 ? 1 : (rval == 0) ? 0 : -1;
+		}*/
+		int ret = start1 - start2;
+		if (ret == 0)
+			ret = end1 - end2;
+		return ret;
+	}
+	
+
+}
diff --git a/src/org/gel/air/util/StatusBox.java b/src/org/gel/air/util/StatusBox.java
new file mode 100644
index 0000000..bf505f6
--- /dev/null
+++ b/src/org/gel/air/util/StatusBox.java
@@ -0,0 +1,40 @@
+package org.gel.air.util;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.*;
+
+public class StatusBox extends JWindow {
+
+	private final static int BORDER_SIZE = 23;
+
+	private JLabel label;
+
+	public StatusBox (String msg, Icon icon) {
+		label = new JLabel (msg, icon, SwingConstants.CENTER);
+		label.setIconTextGap (BORDER_SIZE);
+		JComponent blah = (JComponent) getContentPane ();
+		blah.setLayout (new BorderLayout (23, 23));
+		blah.add (label);
+		blah.setBorder (new CompoundBorder (
+				new BevelBorder (BevelBorder.RAISED), new EmptyBorder (
+						BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)));
+		pack ();
+		Dimension scr = Toolkit.getDefaultToolkit ().getScreenSize ();
+		Dimension s = getSize ();
+		setLocation ((scr.width - s.width) >> 1, (scr.height - s.height) >> 1);
+	}
+
+	public void setVisible (boolean visible) {
+		super.setVisible (visible);
+		if (visible) {
+			toFront ();
+			paintAll (getGraphics ());
+		}
+	}
+
+	public void setText (String msg) {
+		label.setText (msg);
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/BaseViewerModel.java b/src/org/gel/mauve/BaseViewerModel.java
new file mode 100644
index 0000000..23afa31
--- /dev/null
+++ b/src/org/gel/mauve/BaseViewerModel.java
@@ -0,0 +1,971 @@
+package org.gel.mauve;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Vector;
+
+import javax.swing.event.EventListenerList;
+
+import org.gel.mauve.contigs.DefaultContigHandler;
+
+/**
+ * @author pinfield
+ * 
+ * A combination of alignment data, in the list of genomes, and viewing data, in
+ * the form of everything else. This doesn't include LCBs or anything
+ * XMFA-specific.
+ * 
+ * This is a test.
+ * 
+ */
+public class BaseViewerModel {
+	// Genomes, stored in the order in which they are being viewed.
+	protected Genome [] genomes;
+
+	// Genomes, stored in the order in which they appear in the source file.
+	private Genome [] sourceGenomes;
+
+	// Current viewer mode.
+	private ViewerMode mode = ViewerMode.NORMAL;
+
+	// Listeners for changes to model, and event to use.
+	protected EventListenerList listenerList = new EventListenerList ();
+
+	protected ModelEvent modelEvent = new ModelEvent (this);
+
+	// Length of longest genome.
+	private long longestRange = -1;
+
+	// Color scheme to be used.
+	private ColorScheme colorScheme;
+
+	// Set of matches.
+	private Vector matchVector = new Vector ();
+
+	// The list of boxes to highlight
+	private LinkedList highBoxes = new LinkedList ();
+
+	// The reference genome, for displaying reversals.
+	private Genome referenceGenome;
+
+	// The highlighted genome.
+	private Genome highlightGenome;
+
+	// The sequence coordinate for the current highlight.
+	private long highlightCoordinate;
+
+	// Whether to draw matches.
+	private boolean drawMatches = true;
+
+	// Whether to draw contig/chromosome boundary lines.
+	private boolean drawChromosomeBoundaries = true;
+
+	// Whether or not to draw the mouse cursor
+	private boolean drawMouseHighlighting = true;
+	
+	// Original source file for this model.
+	private File src;
+
+	// Original source url for this model, if applicable.
+	private URL sourceURL = null;
+
+	// The genome in which a range of sequence has been highlighted
+	private Genome rangeHighlightGenome = null;
+
+	// the left end of the highlight range
+	private long rangeHighlightLeft = -1;
+
+	// the right end of the highlight range
+	private long rangeHighlightRight = -1;
+	
+	//changes coordinates to and from contig to pseudogene system
+	protected DefaultContigHandler contig_handler;
+
+	/**
+	 * Create a model with the specified source. Note that this does not load
+	 * the model, use
+	 * {@link org.gel.mauve.ModelBuilder#buildModel(File,ModelProgressListener)}
+	 * for reading files.
+	 * 
+	 * @param src
+	 *            file from which data came
+	 */
+	public BaseViewerModel (File src) {
+		this.src = src;
+		contig_handler = new DefaultContigHandler (this);
+	}
+
+	/**
+	 * Returns the file from which this model was derived.
+	 * 
+	 * @return source file
+	 */
+	public File getSrc () {
+		return src;
+	}
+
+	/**
+	 * Sets the URL from which the model was originally derived.
+	 * 
+	 * @param url
+	 */
+	public void setSourceURL (URL url) {
+		this.sourceURL = url;
+	}
+
+	/**
+	 * Returns the URL from which the model was originally derived.
+	 * 
+	 * @return source URL
+	 */
+	public URL getSourceURL () {
+		return sourceURL;
+	}
+
+	/**
+	 * Set the number of genomes to be added to this model. Only used at
+	 * construction-time, since this will clear out preexisting genome data.
+	 * 
+	 * @param sequenceCount
+	 *            the number of sequences to be displayed.
+	 */
+	public void setSequenceCount (int sequenceCount) {
+		// NEWTODO Make the list of genomes simply a list
+		genomes = new Genome [sequenceCount];
+		sourceGenomes = new Genome [sequenceCount];
+	}
+
+	/**
+	 * @return Returns the number of sequences being displayed.
+	 */
+	public int getSequenceCount () {
+		return genomes.length;
+	}
+
+	/**
+	 * Returns the Nth genome in the model, as indexed according to the viewing
+	 * order. That is, suppose that the file contains the order A B C D, and the
+	 * viewer shows A B D C. Then, getGenome(3) will return genome C.
+	 * 
+	 * @param viewIndex
+	 * @return genome at viewIndex.
+	 */
+	public Genome getGenomeByViewingIndex (int viewIndex) {
+		return genomes[viewIndex];
+	}
+
+	public Genome getGenomeBySourceIndex (int sourceIndex) {
+		return sourceGenomes[sourceIndex];
+	}
+
+	/**
+	 * Returns an Vector containing all the Genomes in this BaseViewerModel
+	 * 
+	 * @return Vector of genomes
+	 */
+	public Vector<Genome> getGenomes () {
+		Vector ret = new Vector ();
+		for (int i = 0; i < genomes.length; i++)
+			ret.add (genomes[i]);
+		return ret;
+	}
+	
+	public int numGenomes(){
+		return genomes.length;
+	}
+
+	/**
+	 * Set the genome at viewing position viewIndex to g. This will update the
+	 * viewing index stored in the genome, as well. This also has the
+	 * side-effect of resetting the view length for all genomes.
+	 * 
+	 * @param viewIndex
+	 * @param g
+	 */
+	public void setGenome (int viewIndex, Genome g) {
+		genomes[viewIndex] = g;
+		g.setViewIndex (viewIndex);
+
+		sourceGenomes[g.getSourceIndex ()] = g;
+
+		// Update longest range.
+		if (longestRange < g.getLength ()) {
+			longestRange = g.getLength ();
+		}
+
+		// Set lengths.
+		for (int i = 0; i < genomes.length; i++) {
+			Genome genome = genomes[i];
+			if (genome != null) {
+				genome.setViewLength (longestRange);
+			}
+		}
+	}
+
+	/**
+	 * Returns the number of Matches held by this model.
+	 * 
+	 * @return number of matches
+	 */
+	public int getMatchCount () {
+		return matchVector.size ();
+	}
+
+	/**
+	 * Return the specified Match.
+	 * 
+	 * @param index
+	 * @return match
+	 */
+	public Match getMatch (int index) {
+		return (Match) matchVector.elementAt (index);
+	}
+
+	/**
+	 * 
+	 * Get a copy of the list of matches, sorted according to the given
+	 * comparator.
+	 * 
+	 * @param c
+	 *            a Match comparator
+	 * @return sorted list of matches
+	 */
+	public Vector sortedMatches (Comparator c) {
+		Vector sorted = (Vector) matchVector.clone ();
+		if (c != null)
+			Collections.sort (sorted, c);
+		return sorted;
+	}
+
+	/**
+	 * Add a match to the list of matches.
+	 * 
+	 * @param m
+	 *            match to add
+	 */
+	public void addMatch (Match m) {
+		matchVector.add (m);
+	}
+
+	/**
+	 * Set the color scheme to be applied in the viewer, and apply it. This will
+	 * cause the firing of {@link ModelListener at colorChanged(ModelEvent)} for
+	 * any registered listeners, if the scheme is different than the current
+	 * one.
+	 * 
+	 * @param colorScheme
+	 *            new color scheme
+	 */
+	public void setColorScheme (ColorScheme colorScheme) {
+		if (!colorScheme.equals (this.colorScheme)) {
+			this.colorScheme = colorScheme;
+			colorScheme.apply (this);
+
+			fireColorEvent ();
+		}
+	}
+
+	/**
+	 * Returns the currently-applied color scheme.
+	 * 
+	 * @return color scheme
+	 */
+	public ColorScheme getColorScheme () {
+		return colorScheme;
+	}
+
+	/**
+	 * Add a ModelListener to the list of listeners for the model.
+	 * 
+	 * @param l
+	 *            listener to add
+	 */
+	public void addModelListener (ModelListener l) {
+		listenerList.add (ModelListener.class, l);
+	}
+
+	/**
+	 * Remove a ModelListener from the list of listeners for this model.
+	 * 
+	 * @param l
+	 *            listener to remove
+	 */
+	public void removeModelListener (ModelListener l) {
+		listenerList.remove (ModelListener.class, l);
+	}
+
+	/**
+	 * Add a HighlightListener to the list of listeners for the model.
+	 * 
+	 * @param l
+	 *            listener to add
+	 */
+	public void addHighlightListener (HighlightListener l) {
+		listenerList.add (HighlightListener.class, l);
+	}
+
+	/**
+	 * Remove a HighlightListener from the list of listeners for this model.
+	 * 
+	 * @param l
+	 *            listener to remove
+	 */
+	public void removeHighlightListener (HighlightListener l) {
+		listenerList.remove (HighlightListener.class, l);
+	}
+
+	/**
+	 * Invoke {@link HighlightListener.highlightChanged(ModelEvent)} on this
+	 * model's collection of HighlightListeners.
+	 */
+	protected void fireHighlightEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == HighlightListener.class) {
+				((HighlightListener) listeners[i + 1])
+						.highlightChanged (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.colorChanged(ModelEvent)} on this model's
+	 * collection of ModelListeners.
+	 */
+	protected void fireColorEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1]).colorChanged (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.weightChanged(ModelEvent)} on this model's
+	 * collection of ModelListeners.
+	 */
+	protected void fireWeightEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1]).weightChanged (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.drawingSettingsChanged(ModelEvent)} on this
+	 * model's collection of ModelListeners.
+	 */
+	protected void fireDrawingSettingsEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.drawingSettingsChanged (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.modeChanged(ModelEvent)} on this model's
+	 * collection of ModelListeners.
+	 */
+	protected void fireModeEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1]).modeChanged (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke{@link ModelListener.printingStart(ModelEvent)} on this
+	 * model's collection of ModelListeners.
+	 */
+	public void firePrintingStartEvent() {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.printingStart (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke{@link ModelListener.printingEnd(ModelEvent)} on this
+	 * model's collection of ModelListeners.
+	 */
+	public void firePrintingEndEvent() {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.printingEnd (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.viewableRangeChanged(ModelEvent)} on this
+	 * model's collection of ModelListeners.
+	 */
+	public void fireViewableRangeEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.viewableRangeChanged (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.viewableRangeChangeStart(ModelEvent)} on this
+	 * model's collection of ModelListeners.
+	 */
+	protected void fireViewableRangeStartEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.viewableRangeChangeStart (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.viewableRangeChangeEnd(ModelEvent)} on this
+	 * model's collection of ModelListeners.
+	 */
+	protected void fireViewableRangeEndEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.viewableRangeChangeEnd (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.genomesReordered(ModelEvent)} on this model's
+	 * collection of ModelListeners.
+	 */
+	protected void fireReorderGenomeEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.genomesReordered (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Invoke {@link ModelListener.referenceChanged(ModelEvent)} on this model's
+	 * collection of ModelListeners.
+	 */
+	protected void fireReferenceChangedEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.referenceChanged (modelEvent);
+			}
+		}
+	}
+
+	/**
+	 * Returns the current drawing mode for the application.
+	 * 
+	 * @return
+	 */
+	public ViewerMode getMode () {
+		return mode;
+	}
+
+	/**
+	 * Set the current drawing mode for the application. If this results in a
+	 * change of mode, then {@link ModelListener#modeChanged(ModelEvent)} will
+	 * be fired for all of the model's ModelListeners.
+	 * 
+	 * @param mode
+	 */
+	public void setMode (ViewerMode mode) {
+		if (this.mode != mode) {
+			this.mode = mode;
+			fireModeEvent ();
+		}
+	}
+
+	/**
+	 * Reorder genomes according to given permutation array. For example, if the
+	 * genomes are being viewed in the order A B C D, and the array is 1 0 3 2,
+	 * then the new viewing order will be B A D C. The permutation array is
+	 * applied to the genomes in viewing order, not source order.
+	 * 
+	 * This method will cause the firing of
+	 * {@link ModelListener#genomesReordered(ModelEvent)} for all registered
+	 * ModelListeners.
+	 * 
+	 * @param new_order
+	 *            permutation array
+	 */
+	public void reorderSequences (int [] new_order) {
+		// Reorder genomes
+		Genome [] newGenomes = new Genome [genomes.length];
+		for (int seqI = 0; seqI < genomes.length; seqI++) {
+			newGenomes[seqI] = genomes[new_order[seqI]];
+			newGenomes[seqI].setViewIndex (seqI);
+		}
+		genomes = newGenomes;
+
+		fireReorderGenomeEvent ();
+	}
+
+	public void setReference (Genome g) {
+		referenceGenome = g;
+		referenceUpdated ();
+		fireReferenceChangedEvent ();
+	}
+
+	public Genome getReference () {
+		return referenceGenome;
+	}
+
+	protected void referenceUpdated () {
+		// Hook for subclasses.
+	}
+
+    public void setVisible(Genome g, boolean visible)
+    {
+    	if(g.getVisible() != visible )
+    	{
+    		g.setVisible(visible);
+    		fireGenomeVisibilityChangedEvent();
+    	}
+    }
+    
+    public void fireGenomeVisibilityChangedEvent()
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==ModelListener.class) {
+                ((ModelListener)listeners[i+1]).genomeVisibilityChanged(modelEvent);
+            }
+        }
+    }
+
+	private void correctMatchReversals () {
+		// Correct the orientation due to reversals.
+		for (int matchI = 0; matchI < getMatchCount (); matchI++) {
+			Match m = getMatch (matchI);
+
+			// Find the first genome with something to show for each match
+			// and make that the reference orientation.
+			for (int seqI = 0; seqI < genomes.length; seqI++) {
+				Genome g = getGenomeByViewingIndex (seqI);
+
+				if (m.getStart (g) != 0) {
+					if (m.getReverse (g)) {
+						for (int seqJ = seqI; seqJ < genomes.length; seqJ++) {
+							Genome g2 = getGenomeByViewingIndex (seqJ);
+							m.setReverse (g2, !m.getReverse (g2));
+						}
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Provides a range of indexes into the m_matches array which contain
+	 * matches intersecting with the specified range of coordinates
+	 * 
+	 * @param start_coord
+	 *            The first coordinate of the intersection range
+	 * @param end_coord
+	 *            The last coordinate of the intersection range
+	 * @param match_range
+	 *            An int array with 2 elements. The resulting range of
+	 *            intersecting match indices will be returned as the first and
+	 *            second elements in the array.
+	 */
+	public void getMatchRange (Genome g, long start_coord, long end_coord,
+			int [] match_range) {
+		Vector sortedMatches = g.getSortedMatches ();
+
+		if (sortedMatches.size () == 0) {
+			match_range[0] = -1;
+			match_range[1] = -1;
+			return;
+		}
+
+		MatchStartComparator matchComparator = new MatchStartComparator (g);
+		Match f_match = new Match ((Match) sortedMatches.get (0));
+
+		f_match.setStart (g, start_coord);
+		if (f_match.getStart (g) < 1)
+			f_match.setStart (g, 1);
+
+		int match_startI = Collections.binarySearch (sortedMatches, f_match,
+				matchComparator);
+		match_startI = match_startI >= 0 ? match_startI : -match_startI - 1;
+
+		f_match.setStart (g, end_coord + 1);
+		int match_endI = Collections.binarySearch (sortedMatches, f_match,
+				matchComparator);
+		match_endI = match_endI >= 0 ? match_endI : -match_endI - 1;
+
+		// now look backwards 1 match for an intersection
+		if (match_startI > 0) {
+			Match prev_match = (Match) sortedMatches.get (match_startI - 1);
+
+			if (prev_match.getStart (g) + prev_match.getLength (g) > start_coord
+					&& prev_match.getStart (g) != Match.NO_MATCH)
+				match_startI--;
+		}
+		match_range[0] = match_startI;
+		match_range[1] = match_endI;
+	}
+
+	/**
+	 * Adds a highlighted match to the list of currently highlighted matches.
+	 * This method does not highlight the match, it ensures that when another
+	 * group of matches are highlighted, this match can be found on the list and
+	 * cleared of highlighting.
+	 */
+	public void addMatchHighlight (Match highlighted_box) {
+		ListIterator high_iter = highBoxes.listIterator ();
+		while (high_iter.hasNext ()) {
+			if (high_iter.next () == highlighted_box) {
+				return;
+			}
+		}
+		highlighted_box.highlighted = true;
+		highBoxes.addLast (highlighted_box);
+
+		fireHighlightEvent ();
+	}
+
+	public Match lastMatchHighlight () {
+		if (highBoxes.isEmpty ())
+			return null;
+
+		return (Match) highBoxes.getLast ();
+
+	}
+
+	/**
+	 * Unhighlights any boxes which are currently highlighted.
+	 */
+	public void clearMatchHighlights () {
+		ListIterator high_iter = highBoxes.listIterator ();
+		while (high_iter.hasNext ()) {
+			Match next_box = (Match) high_iter.next ();
+			next_box.highlighted = false;
+		}
+		highBoxes.clear ();
+
+		fireHighlightEvent ();
+	}
+
+	public long getHighlightCoordinate () {
+		return highlightCoordinate;
+	}
+
+	public Genome getHighlightGenome () {
+		return highlightGenome;
+	}
+
+	public long getRangeHighlightLeft () {
+		return rangeHighlightLeft;
+	}
+
+	public long getRangeHighlightRight () {
+		return rangeHighlightRight;
+	}
+
+	public Genome getRangeHighlightGenome () {
+		return rangeHighlightGenome;
+	}
+
+	public void updateHighlight (Genome g, long coordinate) {
+		highlightGenome = g;
+		highlightCoordinate = coordinate;
+		fireHighlightEvent ();
+	}
+
+	public void setDrawMatches (boolean value) {
+		if (value != drawMatches) {
+			this.drawMatches = value;
+			this.fireDrawingSettingsEvent ();
+		}
+	}
+
+	public boolean getDrawMatches () {
+		return drawMatches;
+	}
+
+	public void setDrawChromosomeBoundaries (boolean value) {
+		if (value != drawChromosomeBoundaries) {
+			this.drawChromosomeBoundaries = value;
+			this.fireDrawingSettingsEvent ();
+		}
+	}
+
+	public boolean getDrawChromosomeBoundaries () {
+		return drawChromosomeBoundaries;
+	}
+
+	public void setDrawMouseCursor (boolean value) {
+		if (value != drawMouseHighlighting) {
+			this.drawMouseHighlighting = value;
+			this.fireDrawingSettingsEvent ();
+		}
+	}
+
+	public boolean getDrawMouseHighlighting () {
+		return drawMouseHighlighting;
+	}
+
+	public void setFocus (String sequenceID, long start, long end, String contig) {
+		Genome g = null;
+		for (int i = 0; i < genomes.length; i++) {
+			if (sequenceID.equals (genomes[i].getID ())) {
+				g = genomes[i];
+				break;
+			}
+		}
+		if (g == null) {
+			System.err
+					.println ("Received focus request for nonexistent sequence id "
+							+ sequenceID);
+			return;
+		}
+		if (contig != null) {
+			start = contig_handler.getPseudoCoord(g.getSourceIndex(), start, contig);
+			end = contig_handler.getPseudoCoord(g.getSourceIndex(), end, contig);
+		}
+		long len = (end - start) * 5;
+		int zoom = (int) (100 * g.getViewLength () / (double) len);
+		long center = start + ((end - start) / 2);
+		zoomAndCenter (g, zoom, center);
+		// highlight this part of the display
+		highlightRange (g, start, end);
+	}
+
+	/** ************ Sequence alignment ***************** */
+	/**
+	 * Align the display to a particular set of coordinates in each sequence,
+	 * using one sequence to set the viewable width
+	 */
+	public void alignView (long [] align_coords, Genome selected) {
+		fireViewableRangeStartEvent ();
+
+		// adjust everything to the same zoom level as the selected sequence
+
+		long start_offset = align_coords[selected.getSourceIndex ()]
+				- selected.getViewStart ();
+		for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+			if (seqI != selected.getSourceIndex ()
+					&& align_coords[seqI] != Match.NO_MATCH) {
+				Genome g = getGenomeBySourceIndex (seqI);
+				g.setViewStart (align_coords[seqI] - start_offset);
+				g.setViewLength (selected.getViewLength ());
+			}
+		}
+		fireViewableRangeEvent ();
+
+		fireViewableRangeEndEvent ();
+	}
+
+	public void alignView (Match align_match, Genome g) {
+		long [] align_coords = new long [getSequenceCount ()];
+
+		for (int i = 0; i < getSequenceCount (); i++) {
+			align_coords[i] = align_match.getStart (getGenomeBySourceIndex (i));
+		}
+
+		alignView (align_coords, g);
+	}
+
+	/**
+	 * Shift and zoom the displayed sequence regions relative to their currently
+	 * viewable coordinates.
+	 * 
+	 * @param zoom_percent
+	 *            0 will zoom all the way out. 100 does nothing. values between
+	 *            1 and 99 zoom out, values greater than 100 zoom in
+	 * @param move_percent
+	 *            negative values shift to the left, positive values shift to
+	 *            the right. zero does nothing. A value of 100 would shift the
+	 *            display one full viewing range to the right, such that the
+	 *            rightmost coordinate previously viewable would now be at the
+	 *            left edge of the display.
+	 */
+	public void zoomAndMove (int zoom_percent, int move_percent) {
+		for (int i = 0; i < genomes.length; i++) {
+			zoomAndMove (genomes[i], zoom_percent, move_percent);
+		}
+	}
+
+	public void zoomAndMove (int zoom_percent, long offset) {
+		for (int i = 0; i < genomes.length; i++) {
+			zoomAndMove (genomes[i], zoom_percent, offset);
+		}
+	}
+
+	/**
+	 * Shift and zoom the displayed sequence regions relative to their currently
+	 * viewable coordinates
+	 * 
+	 * @param zoom_percent
+	 *            0 will zoom all the way out. 100 does nothing. values between
+	 *            1 and 99 zoom out, values greater than 100 zoom in
+	 * @param move_percent
+	 *            negative values shift to the left, positive values shift to
+	 *            the right. zero does nothing. A value of 100 would shift the
+	 *            display one full viewing range to the right, such that the
+	 *            rightmost coordinate previously viewable would now be at the
+	 *            left edge of the display.
+	 */
+	public void zoomAndMove (Genome g, int zoom_percent, int move_percent) {
+
+		if (move_percent != 0) {
+			double move = ((double) g.getViewLength ()) * (move_percent / 100d);
+			if (Math.abs (move) < 1) {
+				move = (move < 0) ? -1 : 1;
+			}
+			zoomAndMove (g, zoom_percent, (long) move);
+		} else {
+			zoomAndMove (g, zoom_percent, 0L);
+		}
+	}
+
+	private void zoomAndMove (Genome g, int zoom_percent, long offset) {
+		fireViewableRangeStartEvent ();
+
+		// move before zooming so that we end up on the right coordinates!
+		if (offset != 0) {
+			// calculate the new viewable coordinates.
+			long new_view_start = g.getViewStart () + offset;
+
+			// don't let the sequence get completely out of range
+			if (new_view_start + g.getViewLength () < 1) {
+				new_view_start = 1 - g.getViewLength ();
+			} else if (new_view_start > g.getLength ()) {
+				new_view_start = g.getLength ();
+			}
+			g.setViewStart (new_view_start);
+		}
+
+		if (zoom_percent == 0) {
+			// zoom all the way out.
+			g.setViewStart (1);
+			g.setViewLength (longestRange);
+		} else if (zoom_percent != 100) {
+			// calculate the new viewable coordinates.
+			long new_view_length = (long) ((100d * g.getViewLength ()) / zoom_percent);
+			// don't let the view length get too big or small
+			if (new_view_length < 5)
+				new_view_length = 5;
+			if (new_view_length > longestRange)
+				new_view_length = longestRange;
+
+			long new_view_start = g.getViewStart ()
+					+ ((g.getViewLength () - new_view_length) / 2);
+
+			// don't let the sequence get completely out of range
+			if (new_view_start + new_view_length < 1)
+				new_view_start = 1 - g.getViewLength ();
+			else if (new_view_start > g.getLength ())
+				new_view_start = g.getLength ();
+
+			g.setViewStart (new_view_start);
+			g.setViewLength (new_view_length);
+		}
+
+		fireViewableRangeEvent ();
+
+		fireViewableRangeEndEvent ();
+	}
+
+	public void zoomAndCenter (Genome g, int zoom_percent, long coord) {
+		long offset = coord - (g.getViewStart () + (g.getViewLength () / 2));
+		zoomAndMove (zoom_percent, offset);
+	}
+
+	/** Apply translucent highlighting to a range of sequence */
+	public void highlightRange (Genome g, long left_end, long right_end) {
+		rangeHighlightGenome = g;
+		rangeHighlightLeft = left_end;
+		rangeHighlightRight = right_end;
+		fireHighlightEvent ();
+	}
+
+	private HashMap<Genome,List> attributes = new HashMap<Genome,List>();
+	public void addGenomeAttribute(Genome g, Object o){
+		List l = attributes.get(g);
+		if(l==null){
+			l = new ArrayList();
+			attributes.put(g,l);
+		}
+		l.add(o);
+		fireAttributesEvent();
+	}
+	public List getGenomeAttributes(Genome g){
+		return attributes.get(g);
+	}
+	boolean drawAttributes = false;
+	public boolean getDrawAttributes() {
+		return drawAttributes;
+	}
+	public void setDrawAttributes(boolean drawAttributes) {
+		this.drawAttributes = drawAttributes;
+		fireAttributesEvent();
+	}
+
+	/**
+	 * Invoke {@link ModelListener.attributesChanged(ModelEvent)} on this model's
+	 * collection of ModelListeners.
+	 */
+	protected void fireAttributesEvent () {
+		Object [] listeners = listenerList.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1]).attributesChanged (modelEvent);
+			}
+		}
+	}
+
+	protected int drawAnnotationThreshold = 1000000;
+	/**
+	 * Width of the view in bp below which any annotation features should be drawn
+	 * @param threshold
+	 */
+	public void setDrawAnnotationThreshold(int threshold) {
+		if(threshold!=drawAnnotationThreshold)
+		{
+			drawAnnotationThreshold = threshold;
+			fireDrawingSettingsEvent();
+		}
+		drawAnnotationThreshold = threshold;
+	}
+	public int getDrawAnnotationThreshold() {
+		return drawAnnotationThreshold;
+	}
+
+}
diff --git a/src/org/gel/mauve/BrowserLauncher.java b/src/org/gel/mauve/BrowserLauncher.java
new file mode 100644
index 0000000..0a471d8
--- /dev/null
+++ b/src/org/gel/mauve/BrowserLauncher.java
@@ -0,0 +1,824 @@
+package org.gel.mauve;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * BrowserLauncher is a class that provides one static method, openURL, which
+ * opens the default web browser for the current user of the system to the given
+ * URL. It may support other protocols depending on the system -- mailto, ftp,
+ * etc. -- but that has not been rigorously tested and is not guaranteed to
+ * work.
+ * <p>
+ * Yes, this is platform-specific code, and yes, it may rely on classes on
+ * certain platforms that are not part of the standard JDK. What we're trying to
+ * do, though, is to take something that's frequently desirable but inherently
+ * platform-specific -- opening a default browser -- and allow programmers (you,
+ * for example) to do so without worrying about dropping into native code or
+ * doing anything else similarly evil.
+ * <p>
+ * Anyway, this code is completely in Java and will run on all JDK 1.1-compliant
+ * systems without modification or a need for additional libraries. All classes
+ * that are required on certain platforms to allow this to run are dynamically
+ * loaded at runtime via reflection and, if not found, will not cause this to do
+ * anything other than returning an error when opening the browser.
+ * <p>
+ * There are certain system requirements for this class, as it's running through
+ * Runtime.exec(), which is Java's way of making a native system call.
+ * Currently, this requires that a Macintosh have a Finder which supports the
+ * GURL event, which is true for Mac OS 8.0 and 8.1 systems that have the
+ * Internet Scripting AppleScript dictionary installed in the Scripting
+ * Additions folder in the Extensions folder (which is installed by default as
+ * far as I know under Mac OS 8.0 and 8.1), and for all Mac OS 8.5 and later
+ * systems. On Windows, it only runs under Win32 systems (Windows 95, 98, and NT
+ * 4.0, as well as later versions of all). On other systems, this drops back
+ * from the inherently platform-sensitive concept of a default browser and
+ * simply attempts to launch Netscape via a shell command.
+ * <p>
+ * This code is Copyright 1999-2002 by Eric Albert (ejalbert at cs.stanford.edu)
+ * and may be redistributed or modified in any form without restrictions as long
+ * as the portion of this comment from this paragraph through the end of the
+ * comment is not removed. The author requests that he be notified of any
+ * application, applet, or other binary that makes use of this code, but that's
+ * more out of curiosity than anything and is not required. This software
+ * includes no warranty. The author is not repsonsible for any loss of data or
+ * functionality or any adverse or unexpected effects of using this software.
+ * <p>
+ * Credits: <br>
+ * Steven Spencer, JavaWorld magazine (<a
+ * href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java Tip
+ * 66</a>) <br>
+ * Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea
+ * Cantatore, Larry Barowski, Trevor Bedzek, Frank Miedrich, Ron Rabakukk, and
+ * Glenn Vanderburg
+ * 
+ * @author Eric Albert (<a
+ *         href="mailto:ejalbert at cs.stanford.edu">ejalbert at cs.stanford.edu</a>)
+ * @version 1.4b2
+ */
+public class BrowserLauncher {
+
+	/**
+	 * The Java virtual machine that we are running on. Actually, in most cases
+	 * we only care about the operating system, but some operating systems
+	 * require us to switch on the VM.
+	 */
+	private static int jvm;
+
+	/** The browser for the system */
+	private static Object browser;
+
+	/**
+	 * Caches whether any classes, methods, and fields that are not part of the
+	 * JDK and need to be dynamically loaded at runtime loaded successfully.
+	 * <p>
+	 * Note that if this is <code>false</code>, <code>openURL()</code> will
+	 * always return an IOException.
+	 */
+	private static boolean loadedWithoutErrors;
+
+	/** The com.apple.mrj.MRJFileUtils class */
+	private static Class mrjFileUtilsClass;
+
+	/** The com.apple.mrj.MRJOSType class */
+	private static Class mrjOSTypeClass;
+
+	/** The com.apple.MacOS.AEDesc class */
+	private static Class aeDescClass;
+
+	/** The <init>(int) method of com.apple.MacOS.AETarget */
+	private static Constructor aeTargetConstructor;
+
+	/** The <init>(int, int, int) method of com.apple.MacOS.AppleEvent */
+	private static Constructor appleEventConstructor;
+
+	/** The <init>(String) method of com.apple.MacOS.AEDesc */
+	private static Constructor aeDescConstructor;
+
+	/** The findFolder method of com.apple.mrj.MRJFileUtils */
+	private static Method findFolder;
+
+	/** The getFileCreator method of com.apple.mrj.MRJFileUtils */
+	private static Method getFileCreator;
+
+	/** The getFileType method of com.apple.mrj.MRJFileUtils */
+	private static Method getFileType;
+
+	/** The openURL method of com.apple.mrj.MRJFileUtils */
+	private static Method openURL;
+
+	/** The makeOSType method of com.apple.MacOS.OSUtils */
+	private static Method makeOSType;
+
+	/** The putParameter method of com.apple.MacOS.AppleEvent */
+	private static Method putParameter;
+
+	/** The sendNoReply method of com.apple.MacOS.AppleEvent */
+	private static Method sendNoReply;
+
+	/** Actually an MRJOSType pointing to the System Folder on a Macintosh */
+	private static Object kSystemFolderType;
+
+	/** The keyDirectObject AppleEvent parameter type */
+	private static Integer keyDirectObject;
+
+	/** The kAutoGenerateReturnID AppleEvent code */
+	private static Integer kAutoGenerateReturnID;
+
+	/** The kAnyTransactionID AppleEvent code */
+	private static Integer kAnyTransactionID;
+
+	/** The linkage object required for JDirect 3 on Mac OS X */
+	private static Object linkage;
+
+	/** The framework to reference on Mac OS X 10.0.x */
+	private static final String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
+
+	/** JVM constant for MRJ 2.0 */
+	private static final int MRJ_2_0 = 0;
+
+	/** JVM constant for MRJ 2.1.x and 2.2.x */
+	private static final int MRJ_2_1 = 1;
+
+	/** JVM constant for Java on Mac OS X 10.0 (MRJ 3.0) */
+	private static final int MRJ_3_0 = 3;
+
+	/** JVM constant for Java 1.3.x on Mac OS X 10.1 and later (MRJ 3.1 and 3.2) */
+	private static final int MRJ_3_1 = 4;
+
+	/** JVM constant for Java 1.4.x and later on Mac OS X */
+	private static final int MRJ_COCOA = 5;
+
+	/** JVM constant for any Windows NT JVM */
+	private static final int WINDOWS_NT = 6;
+
+	/** JVM constant for any Windows 9x JVM */
+	private static final int WINDOWS_9x = 7;
+
+	/** JVM constant for any other platform */
+	private static final int OTHER = -1;
+
+	/**
+	 * The file type of the Finder on a Macintosh. Hardcoding "Finder" would
+	 * keep non-U.S. English systems from working properly.
+	 */
+	private static final String FINDER_TYPE = "FNDR";
+
+	/**
+	 * The creator code of the Finder on a Macintosh, which is needed to send
+	 * AppleEvents to the application.
+	 */
+	private static final String FINDER_CREATOR = "MACS";
+
+	/** The name for the AppleEvent type corresponding to a GetURL event. */
+	private static final String GURL_EVENT = "GURL";
+
+	/**
+	 * The first parameter that needs to be passed into Runtime.exec() to open
+	 * the default web browser on Windows.
+	 */
+	private static final String FIRST_WINDOWS_PARAMETER = "/c";
+
+	/** The second parameter for Runtime.exec() on Windows. */
+	private static final String SECOND_WINDOWS_PARAMETER = "start";
+
+	/**
+	 * The third parameter for Runtime.exec() on Windows. This is a "title"
+	 * parameter that the command line expects. Setting this parameter allows
+	 * URLs containing spaces to work.
+	 */
+	private static final String THIRD_WINDOWS_PARAMETER = "\"\"";
+
+	/**
+	 * The shell parameters for Netscape that opens a given URL in an
+	 * already-open copy of Netscape on many command-line systems.
+	 */
+	private static final String NETSCAPE_REMOTE_PARAMETER = "-remote";
+
+	private static final String NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
+
+	private static final String NETSCAPE_OPEN_PARAMETER_END = ")'";
+
+	/**
+	 * The message from any exception thrown throughout the initialization
+	 * process.
+	 */
+	private static String errorMessage;
+
+	/**
+	 * An initialization block that determines the operating system and loads
+	 * the necessary runtime data.
+	 */
+	static {
+		loadedWithoutErrors = true;
+		String osName = System.getProperty ("os.name");
+		if (osName.startsWith ("Mac OS")) {
+			String javaVersion = System.getProperty ("java.version");
+			String majorJavaVersion = javaVersion.substring (0, 3);
+			try {
+				double version = Double.valueOf (majorJavaVersion)
+						.doubleValue ();
+				if (version >= 1.4) {
+					jvm = MRJ_COCOA;
+				}
+			} catch (NumberFormatException nfe) {
+				// Fall through to earlier versions of Java on the Mac.
+			}
+			if (jvm != MRJ_COCOA) {
+				String mrjVersion = System.getProperty ("mrj.version");
+				String majorMRJVersion = mrjVersion.substring (0, 3);
+				try {
+					double version = Double.valueOf (majorMRJVersion)
+							.doubleValue ();
+					if (version == 2) {
+						jvm = MRJ_2_0;
+					} else if (version >= 2.1 && version < 3) {
+						// Assume that all post-2.1 2.x versions of MRJ work the
+						// same. MRJ 2.1 actually
+						// works via Runtime.exec() and 2.2 supports that but
+						// has an openURL() method
+						// as well that we don't use because the Runtime.exec()
+						// method works fine.
+						jvm = MRJ_2_1;
+					} else if (version == 3.0) {
+						jvm = MRJ_3_0;
+					} else if (version >= 3.1) {
+						// Assume that all 3.1 and later versions of MRJ work
+						// the same.
+						jvm = MRJ_3_1;
+					} else {
+						loadedWithoutErrors = false;
+						errorMessage = "Unsupported MRJ version: " + version;
+					}
+				} catch (NumberFormatException nfe) {
+					loadedWithoutErrors = false;
+					errorMessage = "Invalid MRJ version: " + mrjVersion;
+				}
+			}
+		} else if (osName.startsWith ("Windows")) {
+			if (osName.indexOf ("9") != -1 || osName.indexOf ("Me") != -1) {
+				jvm = WINDOWS_9x;
+			} else {
+				jvm = WINDOWS_NT;
+			}
+		} else {
+			jvm = OTHER;
+		}
+
+		if (loadedWithoutErrors) { // if we haven't hit any errors yet
+			loadedWithoutErrors = loadClasses ();
+		}
+	}
+
+	/**
+	 * This class should be never be instantiated; this just ensures so.
+	 */
+	private BrowserLauncher () {
+	}
+
+	/**
+	 * Called by a static initializer to load any classes, fields, and methods
+	 * required at runtime to locate the user's web browser.
+	 * 
+	 * @return <code>true</code> if all intialization succeeded
+	 *         <code>false</code> if any portion of the initialization failed
+	 */
+	private static boolean loadClasses () {
+		switch (jvm) {
+			case MRJ_2_0:
+				return loadMRJ20Classes ();
+			case MRJ_2_1:
+				try {
+					mrjFileUtilsClass = Class
+							.forName ("com.apple.mrj.MRJFileUtils");
+					mrjOSTypeClass = Class.forName ("com.apple.mrj.MRJOSType");
+					Field systemFolderField = mrjFileUtilsClass
+							.getDeclaredField ("kSystemFolderType");
+					kSystemFolderType = systemFolderField.get (null);
+					findFolder = mrjFileUtilsClass.getDeclaredMethod (
+							"findFolder", new Class [] {mrjOSTypeClass});
+					getFileCreator = mrjFileUtilsClass.getDeclaredMethod (
+							"getFileCreator", new Class [] {File.class});
+					getFileType = mrjFileUtilsClass.getDeclaredMethod (
+							"getFileType", new Class [] {File.class});
+				} catch (ClassNotFoundException cnfe) {
+					errorMessage = cnfe.getMessage ();
+					return false;
+				} catch (NoSuchFieldException nsfe) {
+					errorMessage = nsfe.getMessage ();
+					return false;
+				} catch (NoSuchMethodException nsme) {
+					errorMessage = nsme.getMessage ();
+					return false;
+				} catch (SecurityException se) {
+					errorMessage = se.getMessage ();
+					return false;
+				} catch (IllegalAccessException iae) {
+					errorMessage = iae.getMessage ();
+					return false;
+				}
+				break;
+			case MRJ_3_0:
+				try {
+					Class linker = Class
+							.forName ("com.apple.mrj.jdirect.Linker");
+					Constructor constructor = linker
+							.getConstructor (new Class [] {Class.class});
+					linkage = constructor
+							.newInstance (new Object [] {BrowserLauncher.class});
+				} catch (ClassNotFoundException cnfe) {
+					errorMessage = cnfe.getMessage ();
+					return false;
+				} catch (NoSuchMethodException nsme) {
+					errorMessage = nsme.getMessage ();
+					return false;
+				} catch (InvocationTargetException ite) {
+					errorMessage = ite.getMessage ();
+					return false;
+				} catch (InstantiationException ie) {
+					errorMessage = ie.getMessage ();
+					return false;
+				} catch (IllegalAccessException iae) {
+					errorMessage = iae.getMessage ();
+					return false;
+				}
+				break;
+			case MRJ_3_1:
+			case MRJ_COCOA:
+				try {
+					mrjFileUtilsClass = Class
+							.forName ("com.apple.mrj.MRJFileUtils");
+					openURL = mrjFileUtilsClass.getDeclaredMethod ("openURL",
+							new Class [] {String.class});
+				} catch (ClassNotFoundException cnfe) {
+					errorMessage = cnfe.getMessage ();
+					return false;
+				} catch (NoSuchMethodException nsme) {
+					errorMessage = nsme.getMessage ();
+					return false;
+				}
+				break;
+			default:
+				break;
+		}
+		return true;
+	}
+
+	/**
+	 * Loads the classes, fields, and methods needed when running under MRJ 2.0.
+	 * Sets <code>errorMessage</code> if it fails.
+	 * 
+	 * @return <code>true</code> if all operations succeeded;
+	 *         <code>false</code> otherwise
+	 */
+	private static boolean loadMRJ20Classes () {
+		try {
+			Class aeTargetClass = Class.forName ("com.apple.MacOS.AETarget");
+			Class osUtilsClass = Class.forName ("com.apple.MacOS.OSUtils");
+			Class appleEventClass = Class
+					.forName ("com.apple.MacOS.AppleEvent");
+			Class aeClass = Class.forName ("com.apple.MacOS.ae");
+			aeDescClass = Class.forName ("com.apple.MacOS.AEDesc");
+
+			aeTargetConstructor = aeTargetClass
+					.getDeclaredConstructor (new Class [] {int.class});
+			appleEventConstructor = appleEventClass
+					.getDeclaredConstructor (new Class [] {int.class,
+							int.class, aeTargetClass, int.class, int.class});
+			aeDescConstructor = aeDescClass
+					.getDeclaredConstructor (new Class [] {String.class});
+
+			makeOSType = osUtilsClass.getDeclaredMethod ("makeOSType",
+					new Class [] {String.class});
+			putParameter = appleEventClass.getDeclaredMethod ("putParameter",
+					new Class [] {int.class, aeDescClass});
+			sendNoReply = appleEventClass.getDeclaredMethod ("sendNoReply",
+					new Class [] {});
+
+			Field keyDirectObjectField = aeClass
+					.getDeclaredField ("keyDirectObject");
+			keyDirectObject = (Integer) keyDirectObjectField.get (null);
+			Field autoGenerateReturnIDField = appleEventClass
+					.getDeclaredField ("kAutoGenerateReturnID");
+			kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField
+					.get (null);
+			Field anyTransactionIDField = appleEventClass
+					.getDeclaredField ("kAnyTransactionID");
+			kAnyTransactionID = (Integer) anyTransactionIDField.get (null);
+		} catch (ClassNotFoundException cnfe) {
+			errorMessage = cnfe.getMessage ();
+			return false;
+		} catch (NoSuchMethodException nsme) {
+			errorMessage = nsme.getMessage ();
+			return false;
+		} catch (NoSuchFieldException nsfe) {
+			errorMessage = nsfe.getMessage ();
+			return false;
+		} catch (IllegalAccessException iae) {
+			errorMessage = iae.getMessage ();
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Attempts to locate the default web browser on the local system. Caches
+	 * the result so it only locates the browser once for each use of this class
+	 * per JVM instance.
+	 * 
+	 * @return The browser for the system. Note that this may not be what you
+	 *         would consider to be a standard web browser; instead, it's the
+	 *         application that gets called to open the default web browser. In
+	 *         some cases this will be a non-String object that provides the
+	 *         means of calling the default browser.
+	 */
+	private static Object locateBrowser () {
+		if (browser != null) {
+			return browser;
+		}
+		switch (jvm) {
+			case MRJ_2_0:
+				try {
+					Integer finderCreatorCode = (Integer) makeOSType.invoke (
+							null, new Object [] {FINDER_CREATOR});
+					Object aeTarget = aeTargetConstructor
+							.newInstance (new Object [] {finderCreatorCode});
+					Integer gurlType = (Integer) makeOSType.invoke (null,
+							new Object [] {GURL_EVENT});
+					Object appleEvent = appleEventConstructor
+							.newInstance (new Object [] {gurlType, gurlType,
+									aeTarget, kAutoGenerateReturnID,
+									kAnyTransactionID});
+					// Don't set browser = appleEvent because then the next time
+					// we call
+					// locateBrowser(), we'll get the same AppleEvent, to which
+					// we'll already have
+					// added the relevant parameter. Instead, regenerate the
+					// AppleEvent every time.
+					// There's probably a way to do this better; if any has any
+					// ideas, please let
+					// me know.
+					return appleEvent;
+				} catch (IllegalAccessException iae) {
+					browser = null;
+					errorMessage = iae.getMessage ();
+					return browser;
+				} catch (InstantiationException ie) {
+					browser = null;
+					errorMessage = ie.getMessage ();
+					return browser;
+				} catch (InvocationTargetException ite) {
+					browser = null;
+					errorMessage = ite.getMessage ();
+					return browser;
+				}
+			case MRJ_2_1:
+				File systemFolder;
+				try {
+					systemFolder = (File) findFolder.invoke (null,
+							new Object [] {kSystemFolderType});
+				} catch (IllegalArgumentException iare) {
+					browser = null;
+					errorMessage = iare.getMessage ();
+					return browser;
+				} catch (IllegalAccessException iae) {
+					browser = null;
+					errorMessage = iae.getMessage ();
+					return browser;
+				} catch (InvocationTargetException ite) {
+					browser = null;
+					errorMessage = ite.getTargetException ().getClass () + ": "
+							+ ite.getTargetException ().getMessage ();
+					return browser;
+				}
+				String [] systemFolderFiles = systemFolder.list ();
+				// Avoid a FilenameFilter because that can't be stopped mid-list
+				for (int i = 0; i < systemFolderFiles.length; i++) {
+					try {
+						File file = new File (systemFolder,
+								systemFolderFiles[i]);
+						if (!file.isFile ()) {
+							continue;
+						}
+						// We're looking for a file with a creator code of
+						// 'MACS' and
+						// a type of 'FNDR'. Only requiring the type results in
+						// non-Finder
+						// applications being picked up on certain Mac OS 9
+						// systems,
+						// especially German ones, and sending a GURL event to
+						// those
+						// applications results in a logout under Multiple
+						// Users.
+						Object fileType = getFileType.invoke (null,
+								new Object [] {file});
+						if (FINDER_TYPE.equals (fileType.toString ())) {
+							Object fileCreator = getFileCreator.invoke (null,
+									new Object [] {file});
+							if (FINDER_CREATOR.equals (fileCreator.toString ())) {
+								browser = file.toString (); // Actually the
+								// Finder, but
+								// that's OK
+								return browser;
+							}
+						}
+					} catch (IllegalArgumentException iare) {
+						browser = null;
+						errorMessage = iare.getMessage ();
+						return null;
+					} catch (IllegalAccessException iae) {
+						browser = null;
+						errorMessage = iae.getMessage ();
+						return browser;
+					} catch (InvocationTargetException ite) {
+						browser = null;
+						errorMessage = ite.getTargetException ().getClass ()
+								+ ": "
+								+ ite.getTargetException ().getMessage ();
+						return browser;
+					}
+				}
+				browser = null;
+				break;
+			case MRJ_3_0:
+			case MRJ_3_1:
+				browser = ""; // Return something non-null
+				break;
+			case WINDOWS_NT:
+				browser = "cmd.exe";
+				break;
+			case WINDOWS_9x:
+				browser = "command.com";
+				break;
+			case OTHER:
+			default:
+				// On systems other than Windows and the Mac, we try via a
+				// rather Unix-
+				// specific hack to read the BROWSER environment variable
+				// <http://tuxedo.org/~esr/BROWSER/>. If we can't read that
+				// variable or
+				// it isn't set, we use Netscape.
+				// Note: This is commented out for now. It'll work soon.
+				// browser = getEnvironmentBrowser();
+				// if (browser == null) {
+				browser = "firefox";
+				// }
+				break;
+		}
+		return browser;
+	}
+
+	/**
+	 * Attempts to open the default web browser to the given URL.
+	 * 
+	 * @param url
+	 *            The URL to open
+	 * @throws IOException
+	 *             If the web browser could not be located or does not run
+	 */
+	public static void openURL (String url) throws IOException {
+		if (!loadedWithoutErrors) {
+			throw new IOException ("Exception in finding browser: "
+					+ errorMessage);
+		}
+		Object browser = locateBrowser ();
+		if (browser == null) {
+			throw new IOException ("Unable to locate browser: " + errorMessage);
+		}
+
+		switch (jvm) {
+			case MRJ_2_0:
+				Object aeDesc = null;
+				try {
+					aeDesc = aeDescConstructor
+							.newInstance (new Object [] {url});
+					putParameter.invoke (browser, new Object [] {
+							keyDirectObject, aeDesc});
+					sendNoReply.invoke (browser, new Object [] {});
+				} catch (InvocationTargetException ite) {
+					throw new IOException (
+							"InvocationTargetException while creating AEDesc: "
+									+ ite.getMessage ());
+				} catch (IllegalAccessException iae) {
+					throw new IOException (
+							"IllegalAccessException while building AppleEvent: "
+									+ iae.getMessage ());
+				} catch (InstantiationException ie) {
+					throw new IOException (
+							"InstantiationException while creating AEDesc: "
+									+ ie.getMessage ());
+				} finally {
+					aeDesc = null; // Encourage it to get disposed if it was
+					// created
+					browser = null; // Ditto
+				}
+				break;
+			case MRJ_2_1:
+				Runtime.getRuntime ().exec (
+						new String [] {(String) browser, url});
+				break;
+			case MRJ_3_0:
+				int [] instance = new int [1];
+				int result = ICStart (instance, 0);
+				if (result == 0) {
+					int [] selectionStart = new int [] {0};
+					byte [] urlBytes = url.getBytes ();
+					int [] selectionEnd = new int [] {urlBytes.length};
+					result = ICLaunchURL (instance[0], new byte [] {0},
+							urlBytes, urlBytes.length, selectionStart,
+							selectionEnd);
+					if (result == 0) {
+						// Ignore the return value; the URL was launched
+						// successfully
+						// regardless of what happens here.
+						ICStop (instance);
+					} else {
+						throw new IOException ("Unable to launch URL: "
+								+ result);
+					}
+				} else {
+					throw new IOException (
+							"Unable to create an Internet Config instance: "
+									+ result);
+				}
+				break;
+			case MRJ_3_1:
+			case MRJ_COCOA:
+				try {
+					openURL.invoke (null, new Object [] {url});
+				} catch (InvocationTargetException ite) {
+					throw new IOException (
+							"InvocationTargetException while calling openURL: "
+									+ ite.getMessage ());
+				} catch (IllegalAccessException iae) {
+					throw new IOException (
+							"IllegalAccessException while calling openURL: "
+									+ iae.getMessage ());
+				}
+				break;
+			case WINDOWS_NT:
+			case WINDOWS_9x:
+				// Add quotes around the URL to allow ampersands and other
+				// special
+				// characters to work.
+				String [] arguments;
+				if (jvm == WINDOWS_9x) {
+					arguments = new String [] {(String) browser,
+							FIRST_WINDOWS_PARAMETER, SECOND_WINDOWS_PARAMETER,
+							null};
+				} else {
+					arguments = new String [] {(String) browser,
+							FIRST_WINDOWS_PARAMETER, SECOND_WINDOWS_PARAMETER,
+							THIRD_WINDOWS_PARAMETER, null};
+				}
+				arguments[arguments.length - 1] = '"' + url + '"';
+				Process process = Runtime.getRuntime ().exec (arguments);
+				// This avoids a memory leak on some versions of Java on
+				// Windows.
+				// That's hinted at in
+				// <http://developer.java.sun.com/developer/qow/archive/68/>.
+				try {
+					process.waitFor ();
+					process.exitValue ();
+				} catch (InterruptedException ie) {
+					throw new IOException (
+							"InterruptedException while launching browser: "
+									+ ie.getMessage ());
+				}
+				break;
+			case OTHER:
+
+				// Attempt to open the URL in one of the many unix browsers
+				boolean launch_success = false;
+				// try firefox first
+				if (!launch_success) {
+					try {
+						process = Runtime.getRuntime ().exec (
+								new String [] {"firefox", url});
+						process.waitFor ();
+						launch_success = true;
+					} catch (InterruptedException ie) {
+						throw new IOException (
+								"InterruptedException while launching browser: "
+										+ ie.getMessage ());
+					} catch (IOException ioe) {
+						launch_success = false;
+					}
+
+				}
+				// try opera next
+				if (!launch_success) {
+					try {
+						process = Runtime.getRuntime ().exec (
+								new String [] {"opera", url});
+						process.waitFor ();
+						launch_success = true;
+					} catch (InterruptedException ie) {
+						throw new IOException (
+								"InterruptedException while launching browser: "
+										+ ie.getMessage ());
+					} catch (IOException ioe) {
+						launch_success = false;
+					}
+
+				}
+				// try konqueror
+				if (!launch_success) {
+					try {
+						process = Runtime.getRuntime ().exec (
+								new String [] {"konqueror", url});
+						process.waitFor ();
+						launch_success = true;
+					} catch (InterruptedException ie) {
+						throw new IOException (
+								"InterruptedException while launching browser: "
+										+ ie.getMessage ());
+					} catch (IOException ioe) {
+						launch_success = false;
+					}
+
+				}
+				// try mozilla
+				if (!launch_success) {
+					try {
+						process = Runtime.getRuntime ().exec (
+								new String [] {"mozilla", url});
+						process.waitFor ();
+						launch_success = true;
+					} catch (InterruptedException ie) {
+						throw new IOException (
+								"InterruptedException while launching browser: "
+										+ ie.getMessage ());
+					} catch (IOException ioe) {
+						launch_success = false;
+					}
+
+				}
+				// try netscape
+				if (!launch_success) {
+					try {
+						process = Runtime.getRuntime ().exec (
+								new String [] {"netscape", url});
+						process.waitFor ();
+						launch_success = true;
+					} catch (InterruptedException ie) {
+						throw new IOException (
+								"InterruptedException while launching browser: "
+										+ ie.getMessage ());
+					} catch (IOException ioe) {
+						launch_success = false;
+					}
+
+				}
+				break;
+			default:
+				// This should never occur, but if it does, we'll try the
+				// simplest thing possible
+				Runtime.getRuntime ().exec (
+						new String [] {(String) browser, url});
+				break;
+		}
+	}
+
+	/**
+	 * Tries to read the BROWSER environment variable, which should be set to
+	 * the absolute path of the user's preferred web browser as proposed at
+	 * <http://tuxedo.org/~esr/BROWSER/>.
+	 * 
+	 * @return The value of the BROWSER environment variable, or null if the
+	 *         variable does not exist or can't be read.
+	 */
+	private static String getEnvironmentBrowser () {
+		String browser = null;
+		try {
+			String [] echoParams = {"/bin/sh", "-c", "echo $BROWSER"};
+			Process echoProcess = Runtime.getRuntime ().exec (echoParams);
+			InputStream echoStream = echoProcess.getInputStream ();
+			BufferedReader echoReader = new BufferedReader (
+					new InputStreamReader (echoStream));
+			browser = echoReader.readLine ();
+			echoReader.close ();
+		} catch (Throwable t) {
+			// If anything goes wrong, we'll return null.
+		}
+		return browser;
+	}
+
+	/**
+	 * Methods required for Mac OS X 10.0.x. The presence of native methods does
+	 * not cause any problems on other platforms.
+	 */
+	private native static int ICStart (int [] instance, int signature);
+
+	private native static int ICStop (int [] instance);
+
+	private native static int ICLaunchURL (int instance, byte [] hint,
+			byte [] data, int len, int [] selectionStart, int [] selectionEnd);
+}
diff --git a/src/org/gel/mauve/Chromosome.java b/src/org/gel/mauve/Chromosome.java
new file mode 100644
index 0000000..acc98aa
--- /dev/null
+++ b/src/org/gel/mauve/Chromosome.java
@@ -0,0 +1,56 @@
+package org.gel.mauve;
+
+public class Chromosome {
+	private long start;
+
+	private long end;
+
+	private String name;
+
+	boolean circular;
+
+	public Chromosome (long start, long end, String name, boolean circular) {
+		this.start = start;
+		this.end = end;
+		this.name = name.trim ();
+		this.circular = circular;
+	}
+
+	public long getStart () {
+		return start;
+	}
+
+	public long getEnd () {
+		return end;
+	}
+	
+	public long getLength () {
+		return end - start + 1;
+	}
+
+	public String getName () {
+		return name;
+	}
+	
+	public void setName (String n) {
+		name = n;
+	}
+
+	public long relativeLocation (long location) {
+		return location - start + 1;
+	}
+
+	public boolean getCircular () {
+		return circular;
+	}
+	
+	public String toString () {
+		return getName () + " (" + getStart () + "," + getEnd () + ")";
+	}
+	
+	public boolean equals (Object comp) {
+		Chromosome chrom = (Chromosome) comp;
+		boolean ret = getName ().equals(chrom.getName ());
+		return ret;
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/ColorScheme.java b/src/org/gel/mauve/ColorScheme.java
new file mode 100644
index 0000000..bd237b7
--- /dev/null
+++ b/src/org/gel/mauve/ColorScheme.java
@@ -0,0 +1,25 @@
+package org.gel.mauve;
+
+/**
+ * Interface for objects that can apply a color scheme to the elements of a
+ * model.
+ */
+public interface ColorScheme {
+	static final float MATCH_SAT = .8f;
+
+	static final float MATCH_BRIGHT = .65f;
+
+	// Put a gap in the spectrum around violet to make offsets on each end
+	// distinguishable
+	static final float SPECTRUM_GAP = .2f;
+
+	static final float LCB_BRIGHT = .55f;
+
+	/**
+	 * @param model
+	 * 
+	 * Apply the color scheme to the elements of the model. At a minimum, the
+	 * matches of the model must be assigned colors.
+	 */
+	void apply (BaseViewerModel model);
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/DbXrefFactory.java b/src/org/gel/mauve/DbXrefFactory.java
new file mode 100644
index 0000000..42df6c4
--- /dev/null
+++ b/src/org/gel/mauve/DbXrefFactory.java
@@ -0,0 +1,155 @@
+/*
+ * Created on May 1, 2005
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.gel.mauve;
+
+import java.util.HashMap;
+
+/**
+ * A factory class that tracks databases known to have a GenBank xref Use this
+ * class to map a GenBank db_xref to a URL
+ * 
+ * @author koadman
+ */
+public final class DbXrefFactory {
+	private static DbXrefFactory instance = new DbXrefFactory ();
+
+	private HashMap handlers = new HashMap ();
+
+	public static DbXrefFactory getInstance () {
+		return instance;
+	}
+
+	public void addHandler (DbXrefHandler dxh) {
+		if (!handlers.containsKey (dxh.getDbId ()))
+			handlers.put (dxh.getDbId (), dxh);
+	}
+
+	public void removeHandler (String db_id) {
+		handlers.remove (db_id);
+	}
+
+	public String getDbURL (String db_xref) throws UnknownDatabaseException {
+		DbXrefHandler dxh = getHandler (db_xref);
+
+		// the record id is to the right of the colon
+		int loc = db_xref.indexOf (":");
+		String feature_id = db_xref.substring (loc + 1, db_xref.length ());
+		return dxh.getURL (feature_id);
+	}
+
+	public String getDbName (String db_xref) throws UnknownDatabaseException {
+		DbXrefHandler dxh = getHandler (db_xref);
+		return dxh.getName ();
+	}
+
+	public DbXrefHandler getHandler (String db_xref)
+			throws UnknownDatabaseException {
+		// the db name is stored to the left of the colon
+		// get that and match it against registered DB handlers
+		int loc = db_xref.indexOf (":");
+		String db_id = db_xref.substring (0, loc);
+		Object o = handlers.get (db_id);
+		if (o == null) {
+			throw new UnknownDatabaseException (db_id);
+		}
+		DbXrefHandler handler = (DbXrefHandler) o;
+		return handler;
+	}
+
+	private DbXrefFactory () {
+		DbXrefHandler dxh;
+		dxh = AsapXrefHandler.getInstance ();
+		handlers.put (dxh.getDbId (), dxh);
+
+		dxh = EricXrefHandler.getInstance ();
+		handlers.put (dxh.getDbId (), dxh);
+
+		dxh = GiXrefHandler.getInstance ();
+		handlers.put (dxh.getDbId (), dxh);
+	}
+
+	public class UnknownDatabaseException extends Exception {
+		UnknownDatabaseException (String message) {
+			super ("Database not registered: " + message);
+		}
+	}
+}
+
+final class AsapXrefHandler implements DbXrefHandler {
+	private static AsapXrefHandler instance = new AsapXrefHandler ();
+
+	private AsapXrefHandler () {
+	}
+
+	public static DbXrefHandler getInstance () {
+		return instance;
+	}
+
+	public String getDbId () {
+		return "ASAP";
+	}
+
+	public String getName () {
+		return "ASAPdb";
+	}
+
+	public String getURL (String feature_id) {
+		String my_url = "https://asap.ahabs.wisc.edu/asap/feature_info.php?FeatureID="
+				+ feature_id;
+		return my_url;
+	}
+}
+
+final class EricXrefHandler implements DbXrefHandler {
+	private static EricXrefHandler instance = new EricXrefHandler ();
+
+	private EricXrefHandler () {
+	}
+
+	public static DbXrefHandler getInstance () {
+		return instance;
+	}
+
+	public String getDbId () {
+		return "ERIC";
+	}
+
+	public String getName () {
+		return "ERICdb";
+	}
+
+	public String getURL (String feature_id) {
+		String my_url = "https://www.ericbrc.org/asap/feature_info.php?FeatureID="
+				+ feature_id;
+		return my_url;
+	}
+}
+
+final class GiXrefHandler implements DbXrefHandler {
+	private static GiXrefHandler instance = new GiXrefHandler ();
+
+	private GiXrefHandler () {
+	}
+
+	public static DbXrefHandler getInstance () {
+		return instance;
+	}
+
+	public String getDbId () {
+		return "GI";
+	}
+
+	public String getName () {
+		return "Entrez Protein";
+	}
+
+	public String getURL (String feature_id) {
+		String my_url = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Link&db=protein&dbFrom=protein&from_uid="
+				+ feature_id;
+		return my_url;
+	}
+}
diff --git a/src/org/gel/mauve/DbXrefHandler.java b/src/org/gel/mauve/DbXrefHandler.java
new file mode 100644
index 0000000..df1d380
--- /dev/null
+++ b/src/org/gel/mauve/DbXrefHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Created on May 1, 2005
+ *
+ */
+package org.gel.mauve;
+
+/**
+ * An interface classes that map a GenBank db_xref to a web database URL
+ * 
+ * @author koadman
+ */
+public interface DbXrefHandler {
+	/** returns the static internal instance of this handler */
+	// public static DbXrefHandler getInstance();
+	/** Returns the name of the database as it appears in GenBank db_xref entries */
+	public String getDbId ();
+
+	/** Returns the english name of the database */
+	public String getName ();
+
+	/** Constructs a URL for the given feature ID */
+	public String getURL (String feature_id);
+}
diff --git a/src/org/gel/mauve/FilterCacheSpec.java b/src/org/gel/mauve/FilterCacheSpec.java
new file mode 100644
index 0000000..8af538f
--- /dev/null
+++ b/src/org/gel/mauve/FilterCacheSpec.java
@@ -0,0 +1,48 @@
+package org.gel.mauve;
+
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.gui.sequence.FeatureRenderer;
+
+public class FilterCacheSpec {
+	public final static String ALL_ANNOTATIONS = "ALL_ANNOTATIONS_99";
+
+	public FeatureFilter filter;
+
+	public String [] annotations;
+
+	private FeatureRenderer renderer;
+
+	public FilterCacheSpec (FeatureFilter filter) {
+		this.filter = filter;
+	}
+
+	public FilterCacheSpec (FeatureFilter filter, String [] annotations) {
+		this.filter = filter;
+		this.annotations = annotations;
+	}
+
+	public FilterCacheSpec (FeatureFilter filter, FeatureRenderer renderer) {
+		this.filter = filter;
+		this.renderer = renderer;
+	}
+
+	public FilterCacheSpec (FeatureFilter filter, String [] annotations,
+			FeatureRenderer renderer) {
+		this.filter = filter;
+		this.annotations = annotations;
+		this.renderer = renderer;
+	}
+
+	public FeatureFilter getFilter () {
+		return filter;
+	}
+
+	public String [] getAnnotations () {
+		return annotations;
+	}
+
+	public FeatureRenderer getFeatureRenderer () {
+		return renderer;
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/Genome.java b/src/org/gel/mauve/Genome.java
new file mode 100644
index 0000000..8073b93
--- /dev/null
+++ b/src/org/gel/mauve/Genome.java
@@ -0,0 +1,204 @@
+package org.gel.mauve;
+
+import java.util.ArrayList;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.symbol.LocationTools;
+import org.biojavax.bio.seq.Position;
+import org.biojavax.bio.seq.RichLocation;
+import org.biojavax.bio.seq.SimplePosition;
+import org.biojavax.bio.seq.SimpleRichLocation;
+
+public class Genome {
+	private long length;
+
+	private BaseViewerModel model;
+
+	private Sequence annotationSequence;
+
+	private List<Chromosome> chromosomes = new ArrayList<Chromosome> ();
+
+	private String displayName;
+
+	private SupportedFormat format;
+
+	private String id;
+
+	private long viewStart;
+
+	private long viewLength;
+
+	private int viewIndex;
+
+	private Vector sortedMatches;
+
+	private int sourceIndex;
+	private boolean visible = true;
+	public Genome (long length, BaseViewerModel model, int sourceIndex) {
+		this.length = length;
+		this.model = model;
+		this.sourceIndex = sourceIndex;
+	}
+
+	public int getSourceIndex () {
+		return sourceIndex;
+	}
+
+	public long getLength () {
+		return length;
+	}
+
+	public void setAnnotationSequence (Sequence annotationSequence,
+			SupportedFormat format) {
+		this.format = format;
+		this.annotationSequence = annotationSequence;
+	}
+
+	public BaseViewerModel getModel () {
+		return model;
+	}
+
+	// list must be in order by position in sequence!
+	public void setChromosomes (List list) {
+		this.chromosomes = list;
+	}
+
+	public Sequence getAnnotationSequence () {
+		return annotationSequence;
+	}
+
+	public SupportedFormat getAnnotationFormat () {
+		return format;
+	}
+	
+	/**
+	 * Returns annotations overlapping the given position of this 
+	 * genome
+	 * 
+	 * @param left left position 
+	 * @param right right position 
+	 * @param rev true of the query position lies on complementary strand, false otherwise
+	 * 
+	 * @return a FeatureHolder containing the features in this genome that overlap the given position.
+	 */
+	public FeatureHolder getAnnotationsAt(long left, long right, boolean rev){
+		Position min = new SimplePosition((int) left);
+		Position max = new SimplePosition((int) right);
+		RichLocation loc = new SimpleRichLocation (min,max,0,rev?
+								RichLocation.Strand.NEGATIVE_STRAND : 
+								RichLocation.Strand.POSITIVE_STRAND);
+		return annotationSequence.filter(new FeatureFilter.OverlapsLocation(loc));
+	}
+
+	public String getDisplayName () {
+		return displayName;
+	}
+
+	public void setDisplayName (String displayName) {
+		this.displayName = displayName;
+	}
+
+	/**
+	 * Returns an unmodifiable sorted list of the chromosomes comprising this genome.
+	 * 
+	 * @return an unmodifiable sorted list of chromosomes.
+	 */
+	public List<Chromosome> getChromosomes () {
+		return Collections.unmodifiableList (chromosomes);
+	}
+
+	public Chromosome getChromosomeAt (long loc) {
+
+		if (loc < 1 || loc > length)
+			return null;
+
+		// This could be done smarter, if there are many chromosomes, since the
+		// list is sorted.
+		Iterator i = chromosomes.iterator ();
+		while (i.hasNext ()) {
+			Chromosome c = (Chromosome) i.next ();
+			if (c.getStart () <= loc && loc <= c.getEnd ()) {
+				return c;
+			}
+		}
+		return null;
+	}
+
+	public void setID (String id) {
+		this.id = id;
+	}
+
+	public String getID () {
+		return id;
+	}
+
+	public long getViewLength () {
+		return viewLength;
+	}
+
+	public void setViewLength (long viewLength) {
+		this.viewLength = viewLength;
+	}
+
+	public long getViewStart () {
+		return viewStart;
+	}
+
+	public void setViewStart (long viewStart) {
+		this.viewStart = viewStart;
+	}
+
+	public int getViewIndex () {
+		return viewIndex;
+	}
+
+	// TODO: Fire an event?
+	public void setViewIndex (int viewIndex) {
+		this.viewIndex = viewIndex;
+
+		// Clear cache of matches.
+		sortedMatches = null;
+	}
+
+	public Vector getSortedMatches () {
+		try {
+			if (sortedMatches == null) {
+				sortedMatches = model
+						.sortedMatches (new MatchStartComparator (this));
+			}
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return sortedMatches;
+	}
+	
+	public boolean getVisible(){
+    	return visible;
+    }
+    public void setVisible(boolean v){
+    	visible = v;
+	}
+
+	public String toString () {
+		return getDisplayName ();
+	}
+	/**
+	 * Returns true if the specified chromosome is circular.
+	 * 
+	 * @param chrI the chromosome of interest
+	 * @return true if chromosome <code>chrI</code> is circular, false otherwise.
+	 */
+	public boolean isCircular(int chrI){
+		return ((Chromosome) chromosomes.get(chrI)).circular;
+	}
+	
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/GenomeBuilder.java b/src/org/gel/mauve/GenomeBuilder.java
new file mode 100644
index 0000000..a531b94
--- /dev/null
+++ b/src/org/gel/mauve/GenomeBuilder.java
@@ -0,0 +1,213 @@
+package org.gel.mauve;
+
+import java.io.File;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+import org.biojava.bio.BioException;
+import org.biojava.bio.seq.ComponentFeature;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.seq.io.SimpleAssemblyBuilder;
+import org.biojava.bio.symbol.RangeLocation;
+import org.biojava.utils.ChangeVetoException;
+import org.biojavax.bio.seq.RichSequence;
+import org.biojavax.bio.seq.RichSequenceIterator;
+import org.gel.mauve.format.FastaFormat;
+import org.gel.mauve.format.FileFinder;
+import org.gel.mauve.format.SupportedFormatFactory;
+
+public class GenomeBuilder
+{
+    public static final String MAUVE_AGGREGATE = "MauveAggregation";
+
+    private GenomeBuilder()
+    {
+        // Don't allow an object to be created.
+    }
+
+    public static Genome buildGenome(int sequenceIndex, XmfaViewerModel model)
+    {
+        int adjustedIndex = sequenceIndex + 1;
+
+        long length = model.getXmfa().seq_length[sequenceIndex];
+        String annotationFilename = model.getXmfa().getName(sequenceIndex);
+        SupportedFormat annotationFormat = SupportedFormatFactory.guessFormatFromFilename(annotationFilename);
+        int restrictedIndex = -1;
+
+        Properties meta = model.getXmfa().metadata;
+        // See if we are in a file with multisequences here
+        if (meta.containsKey("Sequence" + adjustedIndex + "Entry"))
+        {
+            annotationFilename = meta.getProperty("Sequence" + adjustedIndex + "File");
+            annotationFormat = SupportedFormatFactory.formatNameToFormat(meta.getProperty("Sequence" + adjustedIndex + "Format"));
+            restrictedIndex = Integer.parseInt(meta.getProperty("Sequence" + adjustedIndex + "Entry"));
+        }
+
+        // See if there is a better file to use anyway,
+        if (meta.containsKey("Annotation" + adjustedIndex + "File"))
+        {
+            annotationFilename = meta.getProperty("Annotation" + adjustedIndex + "File");
+            annotationFormat = SupportedFormatFactory.formatNameToFormat(meta.getProperty("Annotation" + adjustedIndex + "Format"));
+        }
+        else if (meta.containsKey("Sequence" + adjustedIndex + "File"))
+        {
+            annotationFilename = meta.getProperty("Sequence" + adjustedIndex + "File");
+            annotationFormat = SupportedFormatFactory.formatNameToFormat(meta.getProperty("Sequence" + adjustedIndex + "Format"));
+        }
+
+        return buildGenome(length, annotationFilename, annotationFormat, model, restrictedIndex, sequenceIndex);
+    }
+
+    public static Genome buildGenome(long length, String annotationFilename, BaseViewerModel model, int sequenceIndex)
+    {
+        return buildGenome(length, annotationFilename, SupportedFormatFactory.guessFormatFromFilename(annotationFilename), model, -1, sequenceIndex);
+    }
+
+
+    private static Genome buildGenome(long length, String annotationFilename, SupportedFormat annotationFormat, BaseViewerModel model, int restrictedIndex, int sequenceIndex)
+    {    	
+    	String fname = FileFinder.findFile(model, annotationFilename);
+        File f = new File(fname);
+        return buildGenome(length, f, annotationFormat, model, restrictedIndex, sequenceIndex);
+    }
+
+    // Left package-visible for testing.
+    static Genome buildGenome(long length, 
+    		File annotationFile, SupportedFormat annotationFormat, BaseViewerModel model, int restrictedIndex, int sequenceIndex)
+    {
+        Genome g = new Genome(length, model, sequenceIndex);
+
+        // By default, the displayname is the annotation file name
+        // and there is a single non-circular chromosome that is the length of the file.
+        g.setDisplayName(annotationFile.getName());
+        ArrayList chromo = new ArrayList();
+        chromo.add(new Chromosome(1, length, "[1," + length + "]", false));
+        g.setChromosomes(chromo);
+
+        // Construct the sequence Assembly.
+        SimpleAssemblyBuilder b = new SimpleAssemblyBuilder();
+        ComponentFeature.Template cft = new ComponentFeature.Template();
+        cft.type = MAUVE_AGGREGATE;
+        cft.strand = StrandedFeature.POSITIVE;
+        int start = 1;
+
+        // We will also build chromosome list simultaneously.
+        chromo = new ArrayList<Chromosome>();
+
+        if (!annotationFile.exists())
+        {
+            return g;
+        }
+
+        // Use the format to read the file; this most likely will create
+        // an iterator of delegating sequences.
+        SequenceIterator seqi = annotationFormat.makeIterator(annotationFile);
+
+        if (!seqi.hasNext())
+        {
+            // No sequences to read, so again a crippled genome.
+            return g;
+        }
+
+        int counter = 1;
+        while (seqi.hasNext())
+        {
+            Sequence s;
+            try
+            {
+            	if(seqi instanceof RichSequenceIterator)
+                    s = ((RichSequenceIterator)seqi).nextRichSequence();
+            	else
+            		s = seqi.nextSequence();
+            }
+            catch (NoSuchElementException e)
+            {
+                // This should never happen, so make it loud.
+                throw new RuntimeException(e);
+            }
+            catch (BioException e)
+            {
+                MyConsole.err().println("Error reading file.");
+                e.printStackTrace(MyConsole.err());
+                return g;
+            }
+
+            if (restrictedIndex == -1 || counter == restrictedIndex)
+            {
+                // capture the genome name from the first sequence, if it
+                // exists.
+                if (start == 1 && !(annotationFormat instanceof FastaFormat && restrictedIndex == -1 ) )
+                {
+                    String tmpName = annotationFormat.getSequenceName(s);
+                    if (tmpName != null)
+                    {
+                        g.setDisplayName(tmpName);
+                    }
+                }
+
+                cft.componentSequence = s;
+                cft.location = new RangeLocation(start, start + s.length() - 1);
+                cft.componentLocation = new RangeLocation(1, s.length());
+                try
+                {
+                    b.addComponentSequence(cft);
+                }
+                catch (ChangeVetoException e)
+                {
+                    // An unexpected exception, since we are building this.
+                    throw new RuntimeException(e);
+                }
+                catch (BioException e)
+                {
+                    // An unexpected exception, since we are building this.
+                    throw new RuntimeException(e);
+                }
+
+                // Create chromosomes.
+                // Default chromosome name to location of segment.
+                String chromoName = annotationFormat.getChromosomeName(s);
+                if (chromoName == null)
+                {
+                    chromoName = cft.location.toString();
+                }
+                boolean circular = s.getAnnotation().containsProperty("CIRCULAR");                
+                if(s instanceof RichSequence){
+                	circular = ((RichSequence)s).getCircular();
+                }
+               
+                chromo.add(new Chromosome(cft.location.getMin(), cft.location.getMax(), chromoName, circular));
+                start += s.length();                
+            }
+            counter++;
+        }
+
+        Sequence annotationSequence;
+        try
+        {
+            annotationSequence = b.makeSequence();
+        }
+        catch (BioException e)
+        {
+            // An unexpected exception, since we are building this.
+            throw new RuntimeException(e);
+        }
+
+        // A simple validation; if lengths are not the same, the GUI will
+        // break.
+        if (annotationSequence.length() != length)
+        {
+            g.setDisplayName(annotationFile.getName());
+        }
+        else
+        {
+            g.setAnnotationSequence(annotationSequence, annotationFormat);
+            g.setChromosomes(chromo);
+        }
+        return g;
+    }
+
+}
diff --git a/src/org/gel/mauve/HighlightListener.java b/src/org/gel/mauve/HighlightListener.java
new file mode 100644
index 0000000..9496e04
--- /dev/null
+++ b/src/org/gel/mauve/HighlightListener.java
@@ -0,0 +1,7 @@
+package org.gel.mauve;
+
+import java.util.EventListener;
+
+public interface HighlightListener extends EventListener {
+	public void highlightChanged (ModelEvent evt);
+}
diff --git a/src/org/gel/mauve/LCB.java b/src/org/gel/mauve/LCB.java
new file mode 100644
index 0000000..67e9d63
--- /dev/null
+++ b/src/org/gel/mauve/LCB.java
@@ -0,0 +1,172 @@
+package org.gel.mauve;
+
+import java.awt.Color;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.gel.mauve.analysis.Segment;
+
+/**
+ * The LCB class tracks locally collinear blocks: regions of homologous sequence
+ * that do not contain rearrangements.
+ */
+public class LCB extends Segment {
+
+	/**
+	 *  'Pointers' (actually IDs) to the LCBs on the left in each sequence
+	 */
+	private int [] left_adjacency;
+
+	/**
+	 *  'Pointers' (actually IDs) to the LCBs on the right in each sequence
+	 */
+	private int [] right_adjacency;
+
+	/**
+	 *  A numerical ID that can be assigned to this LCB
+	 */
+	public int id;
+
+	/**
+	 *  The weight (or coverage) of this LCB
+	 */
+	public long weight;
+
+	/**
+	 *  The color of the LCB frame
+	 */
+	public Color color;
+
+	/**
+	 *  The color of matches within the LCB
+	 */
+	public Color match_color;
+
+	/**
+	 *  set this to true to keep this LCB even when it's weight is too low
+	 */
+	boolean keep;
+
+	public LCB (int seq_count) {
+		left = new long [seq_count];
+		right = new long [seq_count];
+		reverse = new boolean [seq_count];
+		left_adjacency = new int [seq_count];
+		right_adjacency = new int [seq_count];
+	}
+
+	public LCB (Match m, int id, int seq_count) {
+		left = new long [seq_count];
+		right = new long [seq_count];
+		reverse = new boolean [seq_count];
+		left_adjacency = new int [seq_count];
+		right_adjacency = new int [seq_count];
+		this.id = id;
+
+		m.copyArrays (this, left, right, reverse, seq_count);
+
+		// set weight to average lcb length for now...
+		long len_sum = 0;
+		for (int seqI = 0; seqI < seq_count; seqI++) {
+			len_sum += right[seqI] - left[seqI];
+		}
+		weight = len_sum / seq_count;
+		keep = false;
+	}
+
+	public LCB (LCB l) {
+		int seq_count = l.left.length;
+		left = new long [seq_count];
+		right = new long [seq_count];
+		left_adjacency = new int [seq_count];
+		right_adjacency = new int [seq_count];
+		reverse = new boolean [seq_count];
+		id = l.id;
+		weight = l.weight;
+		color = l.color;
+		match_color = l.match_color;
+		keep = l.keep;
+
+		System.arraycopy (l.left, 0, left, 0, seq_count);
+		System.arraycopy (l.right, 0, right, 0, seq_count);
+		System.arraycopy (l.left_adjacency, 0, left_adjacency, 0, seq_count);
+		System.arraycopy (l.right_adjacency, 0, right_adjacency, 0, seq_count);
+		System.arraycopy (l.reverse, 0, reverse, 0, seq_count);
+	}
+
+	public long midpoint (Genome g) {
+		return (right[g.getSourceIndex ()] + left[g.getSourceIndex ()]) / 2;
+	}
+
+	public void setReference (Genome g) {
+		if (getReverse (g)) {
+			for (int seqI = 0; seqI < reverse.length; seqI++) {
+				Genome g2 = g.getModel ().getGenomeBySourceIndex (seqI);
+				setReverse (g2, !getReverse (g2));
+			}
+		}
+	}
+
+	public long getLength (Genome g) {
+		return right[g.getSourceIndex ()] - left[g.getSourceIndex ()];
+	}
+
+	public long getLeftEnd (Genome g) {
+		return left[g.getSourceIndex ()];
+	}
+
+	public void setLeftEnd (Genome g, long leftEnd) {
+		left[g.getSourceIndex ()] = leftEnd;
+	}
+
+	public long getRightEnd (Genome g) {
+		return right[g.getSourceIndex ()];
+	}
+
+	public void setRightEnd (Genome g, long rightEnd) {
+		right[g.getSourceIndex ()] = rightEnd;
+	}
+
+	public boolean getReverse (Genome g) {
+		return reverse[g.getSourceIndex ()];
+	}
+
+	public void setReverse (Genome g, boolean r) {
+		reverse[g.getSourceIndex ()] = r;
+	}
+
+	public int getLeftAdjacency (Genome g) {
+		return left_adjacency[g.getSourceIndex ()];
+	}
+
+	public void setLeftAdjacency (Genome g, int lcbID) {
+		left_adjacency[g.getSourceIndex ()] = lcbID;
+	}
+
+	public int getRightAdjacency (Genome g) {
+		return right_adjacency[g.getSourceIndex ()];
+	}
+
+	public void setRightAdjacency (Genome g, int lcbID) {
+		right_adjacency[g.getSourceIndex ()] = lcbID;
+	}
+
+	public void resetAdjacencies (int genomeCount) {
+		Arrays.fill (left_adjacency, 0);
+		Arrays.fill (right_adjacency, 0);
+	}
+
+	public int multiplicity () {
+		int mult = 0;
+		for (int i = 0; i < left.length; ++i) {
+			if (left[i] != 0)
+				mult++;
+		}
+		return mult;
+	}
+	
+
+	
+}
diff --git a/src/org/gel/mauve/LCBLeftComparator.java b/src/org/gel/mauve/LCBLeftComparator.java
new file mode 100644
index 0000000..14973b9
--- /dev/null
+++ b/src/org/gel/mauve/LCBLeftComparator.java
@@ -0,0 +1,32 @@
+package org.gel.mauve;
+
+import java.util.Comparator;
+
+/**
+ * Compares left end of LCBs.
+ */
+public class LCBLeftComparator implements Comparator<LCB> {
+	private Genome g;
+
+	public LCBLeftComparator (Genome g) {
+		this.g = g;
+	}
+
+	public int compare (LCB o_a, LCB o_b) {
+
+		LCB a = (LCB) o_a;
+		LCB b = (LCB) o_b;
+
+		long a_start = a.getLeftEnd (g);
+		long b_start = b.getLeftEnd (g);
+		if (a_start == 0 || b_start == 0) {
+			if (b_start != 0)
+				return 1;
+			return -1;
+		}
+
+		long diff = a_start - b_start;
+		return (int) diff;
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/LCBlist.java b/src/org/gel/mauve/LCBlist.java
new file mode 100644
index 0000000..61f7e23
--- /dev/null
+++ b/src/org/gel/mauve/LCBlist.java
@@ -0,0 +1,409 @@
+package org.gel.mauve;
+
+import java.util.Arrays;
+import java.util.Stack;
+import java.util.Vector;
+
+/**
+ * Class implementing manipulation and analysis functions for LCBs
+ */
+public class LCBlist {
+
+	public static final int ENDPOINT = -1;
+	public static final int REMOVED = -2;
+
+	public static boolean isNwayLcbList (LCB [] lcb_list, BaseViewerModel model) {
+		int lcbI = 0;
+		for (; lcbI < lcb_list.length; ++lcbI) {
+			int gI = 0;
+			for (; gI < model.getSequenceCount (); gI++) {
+				Genome g = model.getGenomeBySourceIndex (gI);
+				if (lcb_list[lcbI].getLeftEnd (g) == 0)
+					break;
+			}
+			if (gI < model.getSequenceCount ())
+				break;
+		}
+		return lcbI == lcb_list.length;
+	}
+
+	/**
+	 * Links together adjacent LCBs in an array of LCBs. Links are stored in the
+	 * left_adjacency[] and right_adjacency[] arrays as the index of the
+	 * adjacent LCB in lcb_list when lcb_list is sorted on sequence 0. Redesign
+	 * to be more intuitive. left_adjacency is always left, regardless of LCB
+	 * orientation
+	 */
+	public static void computeLCBAdjacencies (LCB [] lcb_list,
+			BaseViewerModel model) {
+		if (lcb_list.length == 0)
+			return; // there aren't any LCBs so there aren't any adjacencies!
+
+		int lcbI;
+		int seqI;
+		for (lcbI = 0; lcbI < lcb_list.length; lcbI++) {
+			lcb_list[lcbI].resetAdjacencies (model.getSequenceCount ());
+			lcb_list[lcbI].id = lcbI;
+		}
+
+        for (seqI = 0; seqI < model.getSequenceCount(); seqI++)
+        {
+            Genome g = model.getGenomeBySourceIndex(seqI);
+            
+            LCBLeftComparator llc = new LCBLeftComparator(g);
+            Arrays.sort(lcb_list, llc);
+            int first = 0;
+            for( ; first < lcb_list.length; first++ )
+            	if(lcb_list[first].getLeftEnd(g) != 0)
+            		break;
+            int last = lcb_list.length;
+            for( ; last > 0; last-- )
+            	if(lcb_list[last-1].getLeftEnd(g) != 0)
+            		break;
+
+            for (lcbI = first + 1; lcbI + 1 < last; lcbI++)
+            {
+                lcb_list[lcbI].setLeftAdjacency(g, lcb_list[lcbI - 1].id);
+                lcb_list[lcbI].setRightAdjacency(g, lcb_list[lcbI + 1].id);
+            }
+            if (lcbI == lcb_list.length)
+                lcbI--; // need to decrement when there is only a single LCB
+
+            // set first and last lcb adjacencies to ENDPOINT
+            if(first < lcb_list.length)
+            	lcb_list[first].setLeftAdjacency(g, ENDPOINT);
+            if(last > 0)
+            	lcb_list[last-1].setRightAdjacency(g, ENDPOINT);
+            if (first < last-1)
+            {
+                lcb_list[first].setRightAdjacency(g, lcb_list[first+1].id);
+                lcb_list[last-1].setLeftAdjacency(g, lcb_list[last-2].id);
+            }
+        }
+
+		// sort back by ID
+		LcbIdComparator lic = new LcbIdComparator ();
+		Arrays.sort (lcb_list, lic);
+	}
+
+	/**
+	 * @param targetMinWeight
+	 * @param fullLCBList
+	 * @param change_points -
+	 *            minimum weights that, if applied, cause a change in the list
+	 *            of LCBs.
+	 * 
+	 * Repeatedly removes the lowest weight LCB from the LCB list until all
+	 * remaining LCBs meet the minimum weight criteria. Call filterLCBs() after
+	 * calling this function to arrive at a final set of LCBs that meet the
+	 * minimum weight.
+	 */
+	public static void greedyBreakpointElimination (long targetMinWeight,
+			LCB [] fullLCBList, Vector change_points, LcbViewerModel model) {
+		if (fullLCBList.length == 0)
+			return;
+
+		// repeatedly remove the low weight LCBs until the minimum weight
+		// criteria is satisfied
+		int lcbI = 0;
+		long currentMinWeight = 0;
+		long previousMinWeight = 0;
+		int min_lcb = 0;
+		int lcb_count = fullLCBList.length;
+
+		while (currentMinWeight < targetMinWeight) {
+			// Loop through all LCBs, looking for lowest-weight LCB that has not
+			// either been eliminated, or that
+			currentMinWeight = 0;
+			for (lcbI = 0; lcbI < fullLCBList.length; lcbI++) {
+				LCB lcb = fullLCBList[lcbI];
+				if (lcb.id == lcbI && lcb.keep == false) {
+					if (lcb.weight < currentMinWeight || currentMinWeight == 0) {
+						currentMinWeight = lcb.weight;
+						min_lcb = lcbI;
+					}
+				}
+			}
+			lcbI = min_lcb;
+
+			// If we are saving change points, save it.
+			if (change_points != null && previousMinWeight != currentMinWeight) {
+				change_points.addElement (new Integer ((int) currentMinWeight));
+			}
+
+			// if only a single LCB remains, don't remove it
+			// and if we've exceeded the target, don't remove it.
+			if (lcb_count == 1 || currentMinWeight >= targetMinWeight) {
+				break;
+			}
+
+			previousMinWeight = currentMinWeight;
+
+			// remove this LCB
+			fullLCBList[lcbI].id = REMOVED;
+
+			// update adjacencies
+			for (int seqI = 0; seqI < model.getSequenceCount (); seqI++) {
+				Genome g = model.getGenomeBySourceIndex (seqI);
+
+				int left_adj = fullLCBList[lcbI].getLeftAdjacency (g);
+				int right_adj = fullLCBList[lcbI].getRightAdjacency (g);
+
+				// Do a bunch of sanity-checking.
+				if (left_adj == REMOVED || right_adj == REMOVED) {
+					throw new Error ("Improper linking in LCB list.");
+				}
+				// Check that the left-adjacent LCB for the right-adjacent
+				// neighbor
+				// of the current LCB is the current LCB, and vice-versa.
+				if (left_adj != ENDPOINT
+						&& fullLCBList[left_adj].getRightAdjacency (g) != lcbI) {
+					throw new Error ("Inconsistency in LCB list.");
+				}
+				if (right_adj != ENDPOINT
+						&& fullLCBList[right_adj].getLeftAdjacency (g) != lcbI) {
+					throw new Error ("Inconsistency in LCB list.");
+				}
+				if (right_adj >= fullLCBList.length) {
+					throw new Error ("Right adjacency id outside valid range.");
+				}
+
+				// Update the doubly-linked list in both directions.
+				if (left_adj != ENDPOINT) {
+					fullLCBList[left_adj].setRightAdjacency (g, right_adj);
+				}
+				if (right_adj != ENDPOINT) {
+					fullLCBList[right_adj].setLeftAdjacency (g, left_adj);
+				}
+
+			}
+			// just deleted an lcb, drop the lcb count
+			lcb_count--;
+
+			// check for collapse
+			for (int seqI = 0; seqI < model.getSequenceCount (); seqI++) {
+				Genome g = model.getGenomeBySourceIndex (seqI);
+
+				int left_adj = fullLCBList[lcbI].getLeftAdjacency (g);
+				int right_adj = fullLCBList[lcbI].getRightAdjacency (g);
+				if (right_adj == fullLCBList.length) {
+					throw new RuntimeException ("Unexpected error.");
+				}
+
+				if (left_adj == ENDPOINT || right_adj == ENDPOINT) {
+					continue; // can't collapse with a non-existant LCB!
+				}
+
+				// check whether this LCB has already been merged
+				if (left_adj != fullLCBList[left_adj].id
+						|| right_adj != fullLCBList[right_adj].id) {
+					// because adjacency pointers are always updated to point to
+					// the
+					// representative entry of an LCB, the lcb_id and the array
+					// index
+					// should always be identical.
+					throw new RuntimeException ("Improper Linking");
+				}
+
+				if (left_adj == REMOVED || right_adj == REMOVED) {
+					throw new RuntimeException ("Improper Linking");
+				}
+
+				// check whether the two LCBs are adjacent in each sequence
+				boolean orientation = !fullLCBList[left_adj].getReverse (g);
+				int seqJ;
+				for (seqJ = 0; seqJ < model.getSequenceCount (); seqJ++) {
+					Genome g2 = model.getGenomeBySourceIndex (seqJ);
+
+					boolean j_orientation = !fullLCBList[left_adj]
+							.getReverse (g2);
+					if (j_orientation == orientation
+							&& fullLCBList[left_adj].getRightAdjacency (g2) != right_adj)
+						break;
+					if (j_orientation != orientation
+							&& fullLCBList[left_adj].getLeftAdjacency (g2) != right_adj)
+						break;
+					// check that they are both in the same orientation
+					if ((!fullLCBList[right_adj].getReverse (g2)) != j_orientation)
+						break;
+				}
+
+				if (seqJ != model.getSequenceCount ())
+					continue;
+
+				// these two can be collapsed. repeatedly search the intervening
+				// region
+
+				fullLCBList[right_adj].id = left_adj;
+				if (fullLCBList[right_adj].id == ENDPOINT
+						|| fullLCBList[right_adj].id == REMOVED) {
+					// TODO: Is there some sort of proactive validation that can
+					// be done instead?
+					throw new RuntimeException ("Corrupt LCB list.");
+				}
+
+				fullLCBList[left_adj].weight += fullLCBList[right_adj].weight;
+				// unlink right_adj from the adjacency list and
+				// update left and right ends of left_adj
+				for (seqJ = 0; seqJ < model.getSequenceCount (); seqJ++) {
+					Genome g2 = model.getGenomeBySourceIndex (seqJ);
+
+					boolean j_orientation = !fullLCBList[left_adj]
+							.getReverse (g2);
+					int rr_adj = fullLCBList[right_adj].getRightAdjacency (g2);
+					int rl_adj = fullLCBList[right_adj].getLeftAdjacency (g2);
+					if (j_orientation == orientation) {
+						fullLCBList[left_adj].setRightEnd (g2,
+								fullLCBList[right_adj].getRightEnd (g2));
+						fullLCBList[left_adj].setRightAdjacency (g2, rr_adj);
+						if (rr_adj == fullLCBList.length) {
+							// TODO: Is there some sort of proactive validation
+							// that can be done instead?
+							throw new RuntimeException ("Corrupt LCB list.");
+						}
+						if (rr_adj != ENDPOINT) {
+							fullLCBList[rr_adj].setLeftAdjacency (g2, left_adj);
+						}
+					} else {
+						fullLCBList[left_adj].setLeftEnd (g2,
+								fullLCBList[right_adj].getLeftEnd (g2));
+						fullLCBList[left_adj].setLeftAdjacency (g2, rl_adj);
+						if (rl_adj == fullLCBList.length) {
+							// TODO: Is there some sort of proactive validation
+							// that can be done instead?
+							throw new RuntimeException ("Corrupt LCB list.");
+						}
+						if (rl_adj != ENDPOINT)
+							fullLCBList[rl_adj]
+									.setRightAdjacency (g2, left_adj);
+					}
+					// update lcbI's adjacency links to point nowhere
+					if (fullLCBList[lcbI].getLeftAdjacency (g2) == right_adj)
+						fullLCBList[lcbI].setLeftAdjacency (g2, left_adj);
+					if (fullLCBList[lcbI].getRightAdjacency (g2) == right_adj)
+						fullLCBList[lcbI].setRightAdjacency (g2, left_adj);
+				}
+
+				// just collapsed an lcb, decrement lcb_count
+				lcb_count--;
+			}
+		}
+
+		if (change_points != null) {
+			change_points.addElement (new Integer ((int) currentMinWeight));
+		}
+
+	}
+
+	/**
+	 * Computes the set of remaining LCBs after greedy breakpoint elimination.
+	 * ALWAYS call this function immediately after calling
+	 * greedyBreakpointElimination() Note: this code assumes that matches and
+	 * LCBs are sorted on their coordinates in the first genome sequence
+	 */
+	public static LCB [] filterLCBs (LCB [] interimList, BaseViewerModel model,
+			Vector removed_lcbs, boolean updateIDsAndColors) {
+		if (interimList.length == 0)
+			return interimList;
+
+		// The interimList arrives having been munged by
+		// greedyBreakpointElimination.
+		// The rest of the program expects that the lcb_id and array index of an
+		// LCB
+		// will be identical. The interimList instead provides a list with
+		// lcb.id's that
+		// are set to the id of another LCB with which it should be
+		// consolidated.
+		// Any LCB with an lcb.id that matches its array position is preserved;
+		// any that
+		// have an lcb.id of REMOVED will cause referring LCBs to also be
+		// removed.
+		for (int lcbIndex = 0; lcbIndex < interimList.length; lcbIndex++) {
+			// search and update the union/find structure for the target
+			LCB lcb = interimList[lcbIndex];
+			int currentID = lcb.id;
+
+			if (currentID != ENDPOINT && currentID != REMOVED
+					&& interimList[currentID].id != currentID) {
+				Stack visited = new Stack ();
+				visited.push (new Integer (lcbIndex));
+				while (currentID != ENDPOINT && currentID != REMOVED
+						&& interimList[currentID].id != currentID) {
+					visited.push (new Integer (currentID));
+					currentID = interimList[currentID].id;
+				}
+
+				while (visited.size () > 0) {
+					int index = ((Integer) visited.pop ()).intValue ();
+					interimList[index].id = currentID;
+				}
+			}
+		}
+
+		// update lcb_ids and colors for the matches
+		if (updateIDsAndColors) {
+			for (int matchI = 0; matchI < model.getMatchCount (); matchI++) {
+				Match m = model.getMatch (matchI);
+				m.lcb = interimList[m.lcb].id;
+				if (m.lcb >= 0) {
+					m.color = interimList[m.lcb].match_color;
+				}
+			}
+		}
+
+		// leave only the remaining LCBs
+		int remaining_count = 0;
+		for (int lcbI = 0; lcbI < interimList.length; lcbI++) {
+			if (interimList[lcbI].id == lcbI)
+				remaining_count++;
+		}
+		LCB [] remainders = new LCB [remaining_count];
+		remaining_count = 0;
+		for (int lcbI = 0; lcbI < interimList.length; lcbI++) {
+			if (interimList[lcbI].id == lcbI) {
+				remainders[remaining_count] = interimList[lcbI];
+				remaining_count++;
+			}
+		}
+
+		// update the lcb adjacencies!!
+		computeLCBAdjacencies (remainders, model);
+
+		// if we're supposed to return the removed LCBs then go get them
+		if (removed_lcbs != null) {
+			// count the removed LCBs
+			int removed_count = 0;
+			for (int lcbI = 0; lcbI < interimList.length; lcbI++)
+				if (interimList[lcbI].id == REMOVED)
+					removed_count++;
+			// go get the removed LCBs
+			LCB [] removed = new LCB [removed_count];
+			int removedI = 0;
+			for (int lcbI = 0; lcbI < interimList.length; lcbI++) {
+				if (interimList[lcbI].id == REMOVED)
+					removed[removedI++] = interimList[lcbI];
+			}
+			computeLCBAdjacencies (removed, model);
+			removed_lcbs.add (removed);
+		}
+
+		// Now we need to flatten the LCB references within matches.
+		if (updateIDsAndColors) {
+			int remainderIndex = -1;
+			int currentMatchIndex = -1;
+			for (int i = 0; i < model.getMatchCount (); i++) {
+				Match m = model.getMatch (i);
+				if (m.lcb != LCBlist.ENDPOINT && m.lcb != LCBlist.REMOVED) {
+					if (m.lcb != currentMatchIndex) {
+						remainderIndex++;
+						currentMatchIndex = m.lcb;
+					}
+					m.lcb = remainders[remainderIndex].id;
+				}
+			}
+		}
+
+		return remainders;
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/LcbIdComparator.java b/src/org/gel/mauve/LcbIdComparator.java
new file mode 100644
index 0000000..fb01ebf
--- /dev/null
+++ b/src/org/gel/mauve/LcbIdComparator.java
@@ -0,0 +1,15 @@
+package org.gel.mauve;
+
+import java.util.Comparator;
+
+/**
+ * Compares LCB ids.
+ */
+public class LcbIdComparator implements Comparator<LCB> {
+	public int compare (LCB o_a, LCB o_b) {
+
+		LCB a = (LCB) o_a;
+		LCB b = (LCB) o_b;
+		return a.id - b.id;
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/LcbViewerModel.java b/src/org/gel/mauve/LcbViewerModel.java
new file mode 100644
index 0000000..e687422
--- /dev/null
+++ b/src/org/gel/mauve/LcbViewerModel.java
@@ -0,0 +1,759 @@
+package org.gel.mauve;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Vector;
+
+import org.gel.mauve.analysis.PermutationExporter;
+import org.gel.mauve.color.LCBColorScheme;
+
+/**
+ * @author Paul Infield-Harm
+ * 
+ * Data model for viewer.
+ */
+public class LcbViewerModel extends BaseViewerModel {
+	// The sequence coordinates of currently viewed LCB boundaries
+	private LCB [] visibleLcbList = new LCB [0];
+
+	// The sequence coordinates of the complete set of LCB boundaries
+	private LCB [] fullLcbList = new LCB [0];
+	
+	// The sequence coordinate of contig-split LCB boundaries
+	private LCB [] splitLcbList = new LCB[0];
+
+	// The list of deleted LCBs that should not be shown
+	private LCB [] delLcbList = new LCB [0];
+
+	private List originalMatchLcbs = new ArrayList ();
+
+	// Tracks weight values which cause LCBs to drop out, each one of these
+	// becomes a legal value on the LCB weight slider
+	private Vector lcb_change_points;
+
+	private long lcbCount = 0;
+
+	private long minimumLCBWeight;
+
+	private boolean drawLCBbounds = true;
+
+	private boolean fillLCBboxes = false;
+	
+	private boolean displaySplitLCBs = false;
+
+	private long highlightCoordinateRight = -1;
+
+	private boolean nway_lcb_list = false;
+
+	public LcbViewerModel (File src) {
+		super (src);
+	}
+
+	public void updateHighlight (Genome g, long coordinate) {
+		highlightCoordinateRight = coordinate;
+		super.updateHighlight (g, coordinate);
+	}
+
+	public void updateHighlight (Genome g, long leftCoordinate,
+			long rightCoordinate) {
+		highlightCoordinateRight = rightCoordinate;
+		super.updateHighlight (g, leftCoordinate);
+	}
+
+	/**
+	 * @param lcbList
+	 *            sequence coordinates of currently viewed LCB boundaries.
+	 */
+	public void setVisibleLcbList (LCB [] lcbList) {
+		this.visibleLcbList = lcbList;
+	}
+
+	/**
+	 * @return Returns the sequence coordinates of currently viewed LCB
+	 *         boundaries.
+	 */
+	public LCB [] getVisibleLcbList () {
+		return visibleLcbList;
+	}
+
+	public LCB getVisibleLcb (int index) {
+		return visibleLcbList[index];
+	}
+
+	public void setVisibleLcb (int index, LCB lcb) {
+		visibleLcbList[index] = lcb;
+	}
+
+	public int getVisibleLcbCount () {
+		return visibleLcbList.length;
+	}
+
+	public boolean isNwayLcbList () {
+		return nway_lcb_list;
+	}
+
+	public LCB getLeftmostVisibleLCB (Genome g) {
+		for (int i = 0; i < visibleLcbList.length; i++) {
+			if (visibleLcbList[i].getLeftAdjacency (g) < 0) {
+				return visibleLcbList[i];
+			}
+		}
+		return null;
+	}
+
+	public LCB getVisibleRightNeighbor (LCB lcb, Genome g, long pos) {
+		int rightIndex = lcb.getRightAdjacency (g);
+		while (rightIndex >= 0
+				&& pos > visibleLcbList[rightIndex].getRightEnd (g)) {
+			rightIndex = visibleLcbList[rightIndex].getRightAdjacency (g);
+		}
+
+		if (rightIndex < 0) {
+			return null;
+		} else {
+			return visibleLcbList[rightIndex];
+		}
+
+	}
+
+	public LCB getDeletedRightNeighbor (LCB lcb, Genome g, long pos) {
+		int rightIndex = lcb.getRightAdjacency (g);
+		while (rightIndex >= 0 && pos > delLcbList[rightIndex].getRightEnd (g)) {
+			rightIndex = delLcbList[rightIndex].getRightAdjacency (g);
+		}
+
+		if (rightIndex < 0) {
+			return null;
+		} else {
+			return delLcbList[rightIndex];
+		}
+
+	}
+
+	public LCB getLeftmostDeletedLCB (Genome g) {
+		for (int i = 0; i < delLcbList.length; i++) {
+			if (delLcbList[i].getLeftAdjacency (g) < 0) {
+				return delLcbList[i];
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * @param fullLcbList
+	 *            sequence coordinates of the complete set of LCB boundaries.
+	 */
+	void setFullLcbList (LCB [] fullLcbList) {
+		this.fullLcbList = fullLcbList;
+	}
+
+	/**
+	 * @return Returns the sequence coordinates of the complete set of LCB
+	 *         boundaries.
+	 */
+	public LCB [] getFullLcbList () {
+		return fullLcbList;
+	}
+	
+	/**
+	 * @param delLcbList
+	 *            list of deleted LCBs that should not be shown.
+	 */
+	public void setDelLcbList (LCB [] delLcbList) {
+		this.delLcbList = delLcbList;
+	}
+
+	/**
+	 * @return Returns the list of deleted LCBs that should not be shown.
+	 */
+	public LCB [] getDelLcbList () {
+		return delLcbList;
+	}
+
+	/**
+	 * @param lcbCount
+	 *            The lcbCount to set.
+	 */
+	public void setLcbCount (long lcbCount) {
+		this.lcbCount = lcbCount;
+	}
+
+	/**
+	 * @return Returns the lcbCount.
+	 */
+	public long getLcbCount () {
+		return lcbCount;
+	}
+
+	public LCB getLCB (int match_start, int match_end) {
+		int seqI;
+		LCB lcb = new LCB (getSequenceCount ());
+
+		if (match_start >= match_end)
+			return null;
+
+		// find left end of lcb in each sequence
+		int matchI = match_start;
+		long count = 0;
+		for (; matchI < match_end; matchI++) {
+			Match m = getMatch (matchI);
+			for (seqI = 0; seqI < getSequenceCount (); seqI++) {
+				Genome g = getGenomeByViewingIndex (seqI);
+
+				if (lcb.getLeftEnd (g) == Match.NO_MATCH) {
+					if (m.getStart (g) != Match.NO_MATCH) {
+						if (!m.getReverse (g)) {
+							lcb.setLeftEnd (g, m.getStart (g));
+						} else {
+							lcb
+									.setLeftEnd (g, m.getStart (g)
+											+ m.getLength (g));
+						}
+					}
+				} else
+					count++;
+			}
+			if (count == getSequenceCount ())
+				break;
+		}
+		// if this LCB is only defined in a single sequence then bail out
+		count = 0;
+		for (seqI = 0; seqI < getSequenceCount (); seqI++) {
+			Genome g = getGenomeBySourceIndex (seqI);
+
+			if (lcb.getLeftEnd (g) != Match.NO_MATCH) {
+				count++;
+			}
+		}
+		if (count < 2) {
+			return null;
+		}
+
+		// find end in each sequence
+		count = 0;
+		for (matchI = match_end; matchI > match_start; matchI--) {
+			Match m = getMatch (matchI - 1);
+			for (seqI = 0; seqI < getSequenceCount (); seqI++) {
+				Genome g = getGenomeByViewingIndex (seqI);
+
+				if (lcb.getRightEnd (g) == Match.NO_MATCH) {
+					if (m.getStart (g) != Match.NO_MATCH) {
+						if (!m.getReverse (g))
+							lcb.setRightEnd (g, m.getStart (g)
+									+ m.getLength (g));
+						else
+							lcb.setRightEnd (g, m.getStart (g));
+					}
+				} else
+					count++;
+			}
+			if (count == getSequenceCount ())
+				break;
+		}
+		Match m = getMatch (match_start);
+		for (seqI = 0; seqI < getSequenceCount (); seqI++) {
+			Genome g = getGenomeByViewingIndex (seqI);
+			if (m.getReverse (g)) {
+				long tmp = lcb.getLeftEnd (g);
+				lcb.setLeftEnd (g, lcb.getRightEnd (g));
+				lcb.setRightEnd (g, tmp);
+			}
+		}
+
+		// set LCB weight
+		lcb.weight = 0;
+		Genome g0 = getGenomeByViewingIndex (0);
+		for (matchI = match_start; matchI < match_end; matchI++) {
+			m = getMatch (matchI);
+			lcb.weight += m.getLength (g0);
+		}
+
+		// set keep flag to false
+		lcb.keep = false;
+
+		for (int i = 0; i < getSequenceCount (); i++) {
+			Genome g = getGenomeBySourceIndex (i);
+			lcb.setReverse (g, m.getReverse (g));
+		}
+
+		return lcb;
+	}
+
+	protected void referenceUpdated () {
+		for (int lcbI = 0; lcbI < fullLcbList.length; lcbI++) {
+			LCB lcb = fullLcbList[lcbI];
+			lcb.setReference (getReference ());
+		}
+
+		for (int lcbI = 0; lcbI < visibleLcbList.length; lcbI++) {
+			LCB lcb = visibleLcbList[lcbI];
+			lcb.setReference (getReference ());
+		}
+
+		for (int lcbI = 0; lcbI < delLcbList.length; lcbI++) {
+			LCB lcb = delLcbList[lcbI];
+			lcb.setReference (getReference ());
+		}
+
+		/*
+		 * for (int matchI = 0; matchI < getMatchCount(); matchI++) { Match m =
+		 * getMatch(matchI); for (int genomeI = 0; genomeI < getSequenceCount();
+		 * genomeI++) { Genome g = getGenomeBySourceIndex(genomeI); if( m.lcb !=
+		 * LCBlist.ENDPOINT && m.lcb != LCBlist.REMOVED ) m.setReverse(g,
+		 * fullLcbList[m.lcb].getReverse(g)); } }
+		 */
+	}
+
+	public long [] getHighlightArray (Genome g) {
+		long high_c = getHighlightCoordinate ();
+		long high_cr = highlightCoordinateRight;
+		Genome high_g = getHighlightGenome ();
+		long [] no_highlight = new long [1];
+		no_highlight[0] = Match.NO_MATCH;
+		if (high_c == Match.NO_MATCH || high_g == null) {
+			return no_highlight;
+		}
+		Vector sortedMatches = high_g.getSortedMatches ();
+		int [] match_range = new int [2];
+		getMatchRange (high_g, high_c, high_cr, match_range);
+		if (match_range[1] == match_range[0])
+			return no_highlight;
+		long [] g_highlights = new long [match_range[1] - match_range[0]];
+		int highI = 0;
+		for (int matchI = match_range[0]; matchI < match_range[1]; matchI++) {
+			Match m = ((Match) sortedMatches.get (matchI));
+			if (m.getReverse (high_g) == m.getReverse (g))
+				g_highlights[highI] = m.getStart (g)
+						+ (high_c - m.getStart (high_g));
+			else
+				g_highlights[highI] = m.getStart (g) + m.getLength (g)
+						- (high_c - m.getStart (high_g));
+			highI++;
+		}
+		return g_highlights;
+	}
+
+	/**
+	 * aligns the display to a particular position of a particular sequence.
+	 * typically called by RRSequencePanel when the user clicks a part of the
+	 * sequence.
+	 */
+	public void alignView (Genome g, long left, long right) {
+		Vector sortedMatches = g.getSortedMatches ();
+		int [] match_range = new int [2];
+		getMatchRange (g, left, right, match_range);
+		if (match_range[1] == match_range[0])
+			return;
+		// construct the coords array
+		Match m = ((Match) sortedMatches.get (match_range[0]));
+		long [] coords = new long [genomes.length];
+		for (int gI = 0; gI < genomes.length; gI++) {
+			Genome cur_g = getGenomeBySourceIndex (gI);
+			if (m.getReverse (cur_g) == m.getReverse (g))
+				coords[gI] = m.getStart (cur_g) + (right - m.getStart (g));
+			else
+				coords[gI] = m.getStart (cur_g) + m.getLength (cur_g)
+						- (right - m.getStart (g));
+		}
+		alignView (coords, g);
+	}
+
+	public void alignView (Genome g, long coord) {
+		alignView (g, coord - 1, coord + 1);
+	}
+
+	public void updateLCBweight (int min_weight, boolean temporary) {
+		setMinLCBWeight (min_weight);
+
+		// Temporarily make all LCBs visible.
+		visibleLcbList = new LCB [fullLcbList.length];
+		for (int lcbI = 0; lcbI < fullLcbList.length; lcbI++) {
+			visibleLcbList[lcbI] = new LCB (fullLcbList[lcbI]);
+		}
+
+		// Eliminate LCBs with weight below min_weight.
+		LCBlist.greedyBreakpointElimination (min_weight, visibleLcbList, null,
+				this);
+
+		if (temporary) {
+			visibleLcbList = LCBlist.filterLCBs (visibleLcbList, this, null,
+					false);
+		} else {
+			for (int matchI = 0; matchI < getMatchCount (); matchI++) {
+				Match m = getMatch (matchI);
+				m.lcb = ((Integer) originalMatchLcbs.get (matchI)).intValue ();
+			}
+			Vector del_vec = new Vector ();
+			visibleLcbList = LCBlist.filterLCBs (visibleLcbList, this, del_vec,
+					true);
+
+			// TODO: This check indicates some misdesign of LCBlist.filterLCBs.
+			if (!del_vec.isEmpty ()) {
+				delLcbList = ((LCB []) del_vec.elementAt (0));
+			}
+
+			// Reapply the current color scheme.
+			// TODO: This is a temporary fix, since it breaks some encapsulation
+			// it deals with staleness problem when set of LCBs changes.
+			if (getColorScheme () instanceof LCBColorScheme) {
+				setColorScheme (new LCBColorScheme ());
+			} else {
+				getColorScheme ().apply (this);
+			}
+		}
+
+		// Since the LCB lists have been replaced, we need to make sure that
+		// the reference genome is correct.
+		referenceUpdated ();
+
+		fireWeightEvent ();
+
+	}
+
+	/**
+	 * Finds all LCBs that intersect the specified coordinate range in the
+	 * current view
+	 * 
+	 * @param start_coord
+	 *            The first coordinate of the intersection range
+	 * @param end_coord
+	 *            The last coordinate of the intersection range
+	 * @param lcbs
+	 *            An int array of the intersecting LCB indices
+	 */
+	public List getLCBRange (Genome g, long start_coord, long end_coord) {
+		List list = new LinkedList ();
+		for (int lcbI = 0; lcbI < getVisibleLcbCount (); lcbI++) {
+			LCB lcb = getVisibleLcb (lcbI);
+			long lend = lcb.getLeftEnd (g);
+			long rend = lcb.getRightEnd (g);
+			// check for intersection
+			if (rend >= start_coord && lend <= end_coord)
+				list.add (lcb);
+		}
+		return list;
+	}
+
+	public void setMinLCBWeight (long lcb_minimum_weight) {
+		this.minimumLCBWeight = lcb_minimum_weight;
+	}
+
+	public long getMinLCBWeight () {
+		return minimumLCBWeight;
+	}
+
+	public Vector getLcbChangePoints () {
+		return lcb_change_points;
+	}
+
+	public void setLcbChangePoints (Vector v) {
+		this.lcb_change_points = v;
+	}
+
+	public void sanityCheck () {
+		for (int i = 0; i < getVisibleLcbCount (); i++) {
+			LCB lcb = getVisibleLcb (i);
+
+			if (lcb.id != LCBlist.REMOVED) {
+				if (lcb.id != i) {
+					throw new Error ("LCB with incorrect id.  Expected " + i
+							+ " but was " + lcb.id);
+				}
+
+				for (int j = 0; j < getSequenceCount (); j++) {
+					Genome g = getGenomeBySourceIndex (j);
+
+					if (lcb.getLeftAdjacency (g) != LCBlist.ENDPOINT
+							&& lcb.getLeftAdjacency (g) != LCBlist.REMOVED) {
+						LCB left = getVisibleLcb (lcb.getLeftAdjacency (g));
+						if (lcb.id != left.getRightAdjacency (g)) {
+							throw new Error ("Right adjacency error.");
+						}
+					}
+
+					if (lcb.getRightAdjacency (g) != LCBlist.ENDPOINT
+							&& lcb.getRightAdjacency (g) != LCBlist.REMOVED) {
+						LCB right = getVisibleLcb (lcb.getRightAdjacency (g));
+						if (lcb.id != right.getLeftAdjacency (g)) {
+							throw new Error ("Left adjacency error.");
+						}
+					}
+				}
+			}
+		}
+
+		for (int i = 0; i < getMatchCount (); i++) {
+			Match m = getMatch (i);
+			if (m.lcb >= getVisibleLcbCount ()) {
+				throw new Error ("Match reference error.");
+			}
+		}
+	}
+
+	public void addMatch (Match m) {
+		super.addMatch (m);
+		originalMatchLcbs.add (new Integer (m.lcb));
+	}
+
+	public void setDrawLcbBounds (boolean value) {
+		if (value != drawLCBbounds) {
+			drawLCBbounds = value;
+			fireDrawingSettingsEvent ();
+		}
+	}
+
+	public boolean getDrawLcbBounds () {
+		return drawLCBbounds;
+	}
+
+	public void setFillLcbBoxes (boolean value) {
+		if (value != fillLCBboxes) {
+			fillLCBboxes = value;
+			fireDrawingSettingsEvent ();
+		}
+	}
+
+	public boolean getFillLcbBoxes () {
+		return fillLCBboxes;
+	}
+
+	public void setSplitLcbByCtg (boolean value) {
+		if (value){
+			visibleLcbList = splitLcbList;
+		} else {
+			visibleLcbList = fullLcbList;
+		}
+		displaySplitLCBs = value;
+		updateLCBweight((int) minimumLCBWeight, true);
+		fireDrawingSettingsEvent();
+	}
+	
+	public boolean getSplitLcbByCtg () {
+		return displaySplitLCBs;
+	}
+	
+	public void setSplitLcbList(LCB[] list){
+		splitLcbList = list;
+	}
+	
+	public LCB[] getSplitLcbList(){
+		return splitLcbList;
+	}
+	
+	public void initModelLCBs () {
+		nway_lcb_list = LCBlist.isNwayLcbList (getFullLcbList (), this);
+		LCBlist.computeLCBAdjacencies (getFullLcbList (), this);
+
+		// find minimum weight
+		long lcb_minimum_weight = -1;
+		for (int lcbI = 0; lcbI < getFullLcbList ().length; lcbI++) {
+			if (getFullLcbList ()[lcbI].weight < lcb_minimum_weight
+					|| lcb_minimum_weight == -1)
+				lcb_minimum_weight = getFullLcbList ()[lcbI].weight;
+		}
+
+		setVisibleLcbList (new LCB [getFullLcbList ().length]);
+
+		// Copy all of the data in the LCB list, in order to determine
+		// the change points without breaking everything else.
+		LCB [] tmp_lcb_list = new LCB [getFullLcbList ().length];
+		for (int lcbI = 0; lcbI < getFullLcbList ().length; lcbI++) {
+			setVisibleLcb (lcbI, new LCB (getFullLcbList ()[lcbI]));
+			tmp_lcb_list[lcbI] = new LCB (getFullLcbList ()[lcbI]);
+		}
+		Vector lcb_change_points = new Vector ();
+		if (nway_lcb_list) {
+			try {
+				LCBlist.greedyBreakpointElimination (Long.MAX_VALUE,
+						tmp_lcb_list, lcb_change_points, this);
+				LCBlist.filterLCBs (tmp_lcb_list, this, null, false);
+			} catch (Error e) {
+				lcb_minimum_weight = 0;
+				lcb_change_points.addElement (new Integer (0));
+				lcb_change_points.addElement (new Integer (0));
+			}
+			setLcbChangePoints (lcb_change_points);
+		}
+		setMinLCBWeight (lcb_minimum_weight);
+	}
+
+	/**
+	 * Uses currently visible LCBs to launch a DCJ window
+	 */
+/*	public void launchDCJ () {
+		String lcb_input = "";
+s
+		// first construct a matrix of chromosome lengths
+		int max_chr_count = 0;
+		for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+			int cur_count = this.getGenomeByViewingIndex (seqI)
+					.getChromosomes ().size ();
+			max_chr_count = cur_count > max_chr_count ? cur_count
+					: max_chr_count;
+		}
+		long [][] chr_lens = new long [getSequenceCount ()] [max_chr_count];
+		for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+			List chromo = this.getGenomeByViewingIndex (seqI).getChromosomes ();
+			for (int chrI = 0; chrI < chromo.size (); chrI++) {
+				chr_lens[seqI][chrI] = ((Chromosome) chromo.get (chrI))
+						.getEnd ();
+			}
+		}
+
+		for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+			Genome g = this.getGenomeByViewingIndex (seqI);
+			int leftmost_lcb = 0;
+			for (; leftmost_lcb < visibleLcbList.length; leftmost_lcb++)
+				if (visibleLcbList[leftmost_lcb].getLeftAdjacency (g) == LCBlist.ENDPOINT)
+					break;
+
+			int adjI = leftmost_lcb;
+			int cur_chromosome = 0;
+			while (adjI != LCBlist.ENDPOINT && adjI != LCBlist.REMOVED
+					&& adjI < visibleLcbList.length) {
+				if (visibleLcbList[adjI].getLeftEnd (g) > chr_lens[seqI][cur_chromosome]) {
+					lcb_input += " $ ";
+					cur_chromosome++;
+				} else if (adjI != leftmost_lcb)
+					lcb_input += " ";
+				if (visibleLcbList[adjI].getReverse (g))
+					lcb_input += "-";
+				lcb_input += adjI + 1;
+				adjI = visibleLcbList[adjI].getRightAdjacency (g);
+			}
+			if (seqI + 1 < getSequenceCount ())
+				lcb_input += " $,\n";
+		}
+		System.err.print(lcb_input);
+		if (this instanceof XmfaViewerModel){
+			lcb_input = PermutationExporter.getPermStrings((XmfaViewerModel) this,  genomes);			
+		}
+		System.err.print(lcb_input);
+		org.gel.mauve.dcj.DCJWindow.startDCJ (lcb_input);
+	}
+*/
+	
+	
+	public void launchGrimmMGR () {
+		String grimm_url = "http://nbcr.sdsc.edu/GRIMM/grimm.cgi?";
+		String url_data = "ismult=1&ngenomewins=" + getSequenceCount ();
+		url_data += "&signedperm=signed&action=run";
+		String [] per_genome = new String [getSequenceCount ()];
+
+		// first construct a matrix of chromosome lengths
+		int max_chr_count = 0;
+		for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+			int cur_count = this.getGenomeByViewingIndex (seqI)
+					.getChromosomes ().size ();
+			max_chr_count = cur_count > max_chr_count ? cur_count
+					: max_chr_count;
+		}
+		long [][] chr_lens = new long [getSequenceCount ()] [max_chr_count];
+		for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+			List chromo = this.getGenomeByViewingIndex (seqI).getChromosomes ();
+			for (int chrI = 0; chrI < chromo.size (); chrI++) {
+				chr_lens[seqI][chrI] = ((Chromosome) chromo.get (chrI))
+						.getEnd ();
+			}
+		}
+
+		boolean single_chromosome = true;
+		boolean all_circular = true;
+		for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+			url_data += "&genome" + (seqI + 1) + "=";
+			per_genome[seqI] = "";
+			Genome g = this.getGenomeByViewingIndex (seqI);
+			List chromo = g.getChromosomes ();
+			int leftmost_lcb = 0;
+			for (; leftmost_lcb < visibleLcbList.length; leftmost_lcb++)
+				if (visibleLcbList[leftmost_lcb].getLeftAdjacency (g) == LCBlist.ENDPOINT)
+					break;
+
+			int adjI = leftmost_lcb;
+			int cur_chromosome = 0;
+			all_circular = all_circular
+					&& ((Chromosome) chromo.get (cur_chromosome))
+							.getCircular ();
+			while (adjI != LCBlist.ENDPOINT && adjI != LCBlist.REMOVED
+					&& adjI < visibleLcbList.length) {
+				if (visibleLcbList[adjI].getLeftEnd (g) > chr_lens[seqI][cur_chromosome]) {
+					url_data += "%20$%20";
+					per_genome[seqI] += " $ ";
+					cur_chromosome++;
+					single_chromosome = false;
+					all_circular = all_circular
+							&& ((Chromosome) chromo.get (cur_chromosome))
+									.getCircular ();
+				} else if (adjI != leftmost_lcb) {
+					url_data += "%20";
+					per_genome[seqI] += " ";
+				}
+				if (visibleLcbList[adjI].getReverse (g)) {
+					url_data += "-";
+					per_genome[seqI] += "-";
+				}
+				url_data += adjI + 1;
+				per_genome[seqI] += adjI + 1;
+				adjI = visibleLcbList[adjI].getRightAdjacency (g);
+			}
+			url_data += "%20$%20";
+			per_genome[seqI] += " $ ";
+		}
+
+		if (single_chromosome && all_circular)
+			url_data += "&nchromosomes=circular";
+		else
+			url_data += "&nchromosomes=multichromosomal";
+
+		System.out.println ("Launching GRIMM with URL:\n" + grimm_url
+				+ url_data);
+		if (url_data.length () < 2000) {
+			try {
+				BrowserLauncher.openURL (grimm_url + url_data);
+			} catch (IOException ioe) {
+				System.err.println ("Error launching GRIMM with URL...");
+			}
+		} else {
+			try {
+				File tmp_form = File.createTempFile ("grimm", ".htm");
+				FileWriter fw = new FileWriter (tmp_form);
+				BufferedWriter bw = new BufferedWriter (fw);
+				bw.write ("<html><body>\n");
+				bw.write ("Click below to run the GRIMM/MGR analysis!<br>\n");
+				bw
+						.write ("<form method=POST action=\"http://nbcr.sdsc.edu/GRIMM/grimm.cgi\">\n");
+				bw
+						.write ("<input type=\"hidden\" name=\"ismult\" value=\"1\">\n");
+				bw
+						.write ("<input type=\"hidden\" name=\"ngenomewins\" value=\""
+								+ getSequenceCount () + "\">\n");
+				for (int seqI = 0; seqI < getSequenceCount (); seqI++) {
+					bw.write ("<input type=\"hidden\" name=\"genome"
+							+ (seqI + 1) + "\" value=\"" + per_genome[seqI]
+							+ "\">\n");
+				}
+				if (single_chromosome && all_circular)
+					bw
+							.write ("<input type=\"hidden\" name=\"nchromosomes\" value=\"circular\">\n");
+				else
+					bw
+							.write ("<input type=\"hidden\" name=\"nchromosomes\" value=\"multichromosomal\">\n");
+				bw
+						.write ("<input type=\"hidden\" name=\"signedperm\" value=\"signed\">\n");
+				bw
+						.write ("<input type=\"submit\" name=\"action\" value=\"run\"");
+				bw.write ("</form>\n</body>\n</html>");
+				bw.close ();
+				BrowserLauncher.openURL ("file://"
+						+ tmp_form.getCanonicalPath ());
+			} catch (IOException ioe) {
+				ioe.printStackTrace ();
+			}
+		}
+	}
+}
diff --git a/src/org/gel/mauve/Match.java b/src/org/gel/mauve/Match.java
new file mode 100644
index 0000000..e10e9e8
--- /dev/null
+++ b/src/org/gel/mauve/Match.java
@@ -0,0 +1,170 @@
+package org.gel.mauve;
+
+import java.awt.Color;
+import java.io.Serializable;
+
+import org.gel.mauve.analysis.Segment;
+
+/**
+ * Records an ungapped local alignment among multiple sequences. Sequence
+ * coordinates start at 1 and a coordinate of 0 indicates that the alignment is
+ * undefined in that sequence.
+ */
+public class Match extends Segment //implements Serializable
+{
+	static final long serialVersionUID = 1;
+    public static final int NO_MATCH = 0;
+    // The color code of this match
+    public Color color;
+    // true if the match is highlighted on screen
+    public boolean highlighted = false;
+    // The lcb which this match belongs to
+    public int lcb = 0;
+
+    // The start coordinate of this match in each sequence
+    //private long[] starts;
+    // The lengths of this match in each sequence
+    //private long[] ends;
+    // The direction of each match. false is forward, true is reverse
+    //private boolean[] reverse;
+
+    public Match(int sequenceCount)
+    {
+        left = new long[sequenceCount];
+        right = new long[sequenceCount];
+        reverse = new boolean[sequenceCount];
+    }
+
+    public Match(Match m)
+    {
+        left = new long[m.left.length];
+        right = new long[m.right.length];
+        reverse = new boolean[m.reverse.length];
+        System.arraycopy(m.left, 0, left, 0, left.length);
+        System.arraycopy(m.right, 0, right, 0, right.length);
+        System.arraycopy(m.reverse, 0, reverse, 0, reverse.length);
+
+        color = m.color;
+        lcb = m.lcb;
+    }
+
+    public long getStart(Genome g)
+    {
+        return left[g.getSourceIndex()]; 
+    }
+    
+    /**
+     * @deprecated
+     * @param sourceIndex
+     * @return
+     */
+    public long getStart(int sourceIndex)
+    {
+        return left[sourceIndex];
+    }
+    
+    public void setStart(Genome g, long start)
+    {
+        left[g.getSourceIndex()] = start;
+    }
+    
+    /**
+     * @deprecated
+     * @param sourceIndex
+     * @param start
+     */public void setStart(int sourceIndex, long start)
+    {
+        left[sourceIndex] = start;
+    }
+    
+    public long getLength(Genome g)
+    {
+        return right[g.getSourceIndex()];
+    }
+    
+    /**
+     * @deprecated
+     * @param sourceIndex
+     * @return
+     */
+    public long getLength(int sourceIndex)
+    {
+        return right[sourceIndex];
+    }
+    
+    public void setLength(Genome g, long length)
+    {
+        right[g.getSourceIndex()] = length;
+    }
+    
+    /**
+     * @deprecated
+     * @param sourceIndex
+     * @param length
+     */
+    public void setLength(int sourceIndex, long length)
+    {
+        right[sourceIndex] = length;
+    }
+    
+    public boolean getReverse(Genome g)
+    {
+        return reverse[g.getSourceIndex()];
+    }
+    
+
+    /**
+     * @deprecated
+     * @param sourceIndex
+     * @return
+     */public boolean getReverse(int sourceIndex)
+    {
+        return reverse[sourceIndex];
+    }
+    
+    public void setReverse(Genome g, boolean r)
+    {
+        reverse[g.getSourceIndex()] = r;
+    }
+    
+    /**
+     * @deprecated
+     * @param sourceIndex
+     * @param r
+     */
+    public void setReverse(int sourceIndex, boolean r)
+    {
+        reverse[sourceIndex] = r;
+    }
+    
+    /** Compute and return the generalized offset */
+    public long offset()
+    {
+        int seqI = 0;
+        long ref;
+        long g_offset = 0;
+        for (; seqI < left.length; seqI++)
+        {
+            if (left[seqI] != NO_MATCH)
+                break;
+        }
+        ref = left[seqI];
+        for (; seqI < left.length; seqI++)
+        {
+            long cur_start = left[seqI];
+            if (reverse[seqI])
+                cur_start = -cur_start;
+            g_offset += ref - cur_start;
+        }
+
+        return g_offset;
+    }
+
+    public void copyArrays(LCB lcb, long[] starts, long[] lengths, boolean[] reverse, int seq_count)
+    {
+        System.arraycopy(this.left, 0, starts, 0, seq_count);
+        System.arraycopy(this.right, 0, lengths, 0, seq_count);
+        System.arraycopy(this.reverse, 0, reverse, 0, seq_count);
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/MatchStartComparator.java b/src/org/gel/mauve/MatchStartComparator.java
new file mode 100644
index 0000000..bdc91e4
--- /dev/null
+++ b/src/org/gel/mauve/MatchStartComparator.java
@@ -0,0 +1,41 @@
+package org.gel.mauve;
+
+import java.util.Comparator;
+
+import org.gel.mauve.analysis.SegmentComparator;
+
+/**
+ * Compare the start coordinates of ungapped local alignments in a particular
+ * sequence Note: this comparator imposes orderings that are inconsistent with
+ * the equals operator.
+ */
+
+public class MatchStartComparator extends SegmentComparator implements
+		Comparator {
+	private Genome g;
+
+	public MatchStartComparator (Genome g) {
+		super (g.getSourceIndex ());
+		this.g = g;
+	}
+
+	public MatchStartComparator (MatchStartComparator m) {
+		super (m.g.getSourceIndex ());
+		g = m.g;
+	}
+
+	public int compare (Object o1, Object o2) {
+		return super.compare (o1, o2);
+	}
+
+	public boolean equals (Object c) {
+		if (c == null)
+			return false;
+
+		if (c instanceof MatchStartComparator) {
+			return g == ((MatchStartComparator) c).g;
+		} else {
+			return false;
+		}
+	}
+}
diff --git a/src/org/gel/mauve/MauveConstants.java b/src/org/gel/mauve/MauveConstants.java
new file mode 100644
index 0000000..645ca3d
--- /dev/null
+++ b/src/org/gel/mauve/MauveConstants.java
@@ -0,0 +1,182 @@
+package org.gel.mauve;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.OptimizableFilter;
+
+public interface MauveConstants {
+
+	/**
+	 * Strings representing choices for ways to navigate
+	 */
+	public static final String PRODUCT_NAME = "product/alternate_product_name";
+
+	public static final String LOC_NAME = "gene/synonym/standard_name/name/"
+			+ "locus_tag/label/old_locus_tag";
+
+	public static final String GO_FEATS = "go_biological_process/"
+			+ "go_cellular_component/go_molecular_function";
+
+	public static final String ID_NUMBER = "ec_number/db_xref/protein_id";
+
+	/**
+	 * Strings representing prebuilt groupings of search fields
+	 */
+	public static final String PRODUCT = "Product";
+
+	public static final String NAME = "Name";
+
+	public static final String GO = "Go Features";
+
+	public static final String ID = "ID Number";
+
+	/**
+	 * Strings representing keys possibly present in a SegmentDataProcessor
+	 * object
+	 */
+	public static final String ISLAND_MIN = "island_min";
+
+	public static final String BACKBONE_MIN = "backbone_min";
+
+	public static final String MAX_LENGTH_RATIO = "max_length_ratio";
+
+	public static final String MINIMUM_PERCENT_CONTAINED = "minimum_percent_contained";
+	
+	public static final String FILE_STUB = "output_file_stub";
+
+	public static final String DEFAULT_TITLES = "default_titles";
+
+	public static final String REFERENCE = "reference";
+
+	public static final String FIRSTS = "firsts";
+
+	public static final String BACKBONE = "backbone_vector";
+
+	public static final String ALL_MULTIPLICITY = "all_multiplicity_int";
+
+	public static final String SEQUENCE_INDEX = "sequence index";
+	
+	public static final String MODEL = "model";
+	
+	public static final String GENOME_LENGTHS = "genome_lengths";
+	
+	public static final String CONTIG_HANDLER = "contig_handler";
+	
+	public static final String NUM_GENES_PER_MULT = "number_of_genes_per_multiplicity";
+	
+	public static final String TOTAL_GENES = "total_genes";
+	
+	public static final String TOTALS = "totals";
+	
+	/** */
+	public static final String CIRCULAR_CHAR = "*";
+	
+	/**
+	 * Strings representing annotation types
+	 */
+	public static final String DB_XREF = "db_xref";
+	
+	/**
+	 * Integer codes for how to cycle through segments for processing
+	 */
+	public static final int BY_GENOMES = 1;
+
+	public static final int BY_BB_LIST = 2;
+
+	public static final int BY_ALL_AND_BB = 3;
+
+	public static final int BY_ONE_GENOME = 4;
+
+	public static final int BY_ONE_AND_BB = 6;
+
+	/**
+	 * default values for backbone processing for analysis package
+	 */
+	public static final int DEFAULT_ISLAND_MIN = 1;
+
+	public static final int DEFAULT_BACKBONE_MIN = 1;
+
+	public static final double DEFAULT_MAX_LENGTH_RATIO = .2;
+	
+	public static final double DEFAULT_MIN_PERCENT_CONTAINED = 99.8;
+
+	/**
+	 * dummy string representing all genomes rather than one specific sequence
+	 */
+	public static final String ALL_SEQUENCES = "All Sequences";
+
+	/**
+	 * Represents a feature type corresponding to any feature; used for
+	 * FeatureFilter.ByType or FeatureFilter.ByAnnotationType
+	 */
+	public static final String ANY_FEATURE = "any";
+
+	public static final Hashtable READ_TO_ACTUAL = new Hashtable ();
+
+	/**
+	 * contains default annotation keys to use for filters, etc.
+	 */
+	public static final HashSet ANNOTATION_KEYS = new HashSet ();
+
+	/**
+	 * ints representing indexes of data in data array used to represent
+	 * constraints
+	 */
+	public static final int FIELD = 0;
+
+	public static final int VALUE = 1;
+
+	public static final int EXACT = 2;
+
+	/**
+	 * border between separate components in the navigation guis
+	 */
+	public static final int BORDER = 10;
+	
+	/**
+	 * represents subfolder output from analysis package is written to
+	 */
+	public static final String ANALYSIS_OUTPUT = "analysis";
+	
+	/**
+	 * represents subfolder output from contig package is written to
+	 */
+	public static final String CONTIG_OUTPUT = "contigs";
+	
+	/**
+	 * height that should be added when a new type of feature is displayed
+	 */
+	public static final int FEATURE_HEIGHT = 25;
+	
+	/**
+	 * String present in all ASAP generated db_xref values
+	 */
+	public static final String ASAP = "asap";
+	
+	public static final String ERIC = "eric";
+	
+	/**
+	 * Fields biojava uses as constants that are protected for some reason
+	 */
+	public static final String LOCUS = "LOCUS";
+	
+	public static final OptimizableFilter NULL_AVOIDER = new OptimizableFilter () {
+		public boolean accept (Feature f) {
+			return f.getAnnotation () != null;
+		}
+
+		public boolean isDisjoint (FeatureFilter filt) {
+			return false;
+		}
+
+		public boolean isProperSubset (FeatureFilter sup) {
+			return this.equals (sup);
+		}
+
+	};
+
+	public final static String DEFAULT_CONTIG = "chromosome";
+}
diff --git a/src/org/gel/mauve/MauveFormatException.java b/src/org/gel/mauve/MauveFormatException.java
new file mode 100644
index 0000000..3f991d4
--- /dev/null
+++ b/src/org/gel/mauve/MauveFormatException.java
@@ -0,0 +1,11 @@
+package org.gel.mauve;
+
+public class MauveFormatException extends Exception {
+	public MauveFormatException (String msg) {
+		super (msg);
+	}
+
+	public MauveFormatException (String msg, Exception e) {
+		super (msg, e);
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/MauveHelperFunctions.java b/src/org/gel/mauve/MauveHelperFunctions.java
new file mode 100644
index 0000000..9297eb8
--- /dev/null
+++ b/src/org/gel/mauve/MauveHelperFunctions.java
@@ -0,0 +1,303 @@
+package org.gel.mauve;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.biojava.bio.AnnotationType;
+import org.biojava.bio.seq.ComponentFeature;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.symbol.Location;
+import org.gel.air.util.MathUtils;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.gui.sequence.FlatFileFeatureConstants;
+import org.gel.mauve.summary.output.SegmentDataProcessor;
+
+/**
+ * contains file writing routines and variables useful to multiple classes.
+ * 
+ * @author Anna I Rissman
+ * 
+ */
+public class MauveHelperFunctions implements FlatFileFeatureConstants {
+
+	
+	public static final Comparator FEATURE_COMPARATOR = new Comparator () {
+		public int compare (Object a, Object b) {
+			Location one = ((Feature) a).getLocation ();
+			Location two = ((Feature) b).getLocation ();
+			return MathUtils.compareByStartThenLength (one.getMin (), one.getMax (),
+					two.getMin (), two.getMax ());
+		}
+	};
+	
+	/**
+	 * Returns a genome name with ".fas" appended to it
+	 * if it is not already there.
+	 * 
+	 * @param genome the genome whose name to convert
+	 * @return <code>genome.getDisplayName() + .fas</code>
+	 */
+	public static String genomeNameToFasta (Genome genome) {
+		String file = genome.getDisplayName ();
+		if (file.toLowerCase ().indexOf (".fas") == -1) {
+			if (!file.endsWith("."))
+				file += ".";
+			file += "fas";
+		}
+		return file;
+	}
+	
+	public static File getRootDirectory (BaseViewerModel model) {
+		return model.getSrc ().getParentFile ();
+	}
+	
+	public static File getChildOfRootDir (BaseViewerModel model, String child) {
+		return new File (getRootDirectory (model), child);
+	}
+	
+	public static String getFileStub (BaseViewerModel model) {
+		String name = model.getSrc().getName ();
+		if (!getChildOfRootDir (model, name + ".backbone").exists())
+			name = name.substring(0, name.indexOf(".alignment"));
+		return name;
+	}
+
+		
+	/**
+	 * writes out genome names and associates each with a unique index
+	 * 
+	 * @param model
+	 *            The model containing the genomes of interest
+	 * @param out
+	 *            The stream to output to
+	 */
+	public static void writeGenomesWithIndices (BaseViewerModel model,
+			PrintStream out) {
+		try {
+			for (int i = 0; i < model.getSequenceCount (); i++) {
+				out.println (model.getGenomeBySourceIndex (i).getDisplayName ()
+						+ "--" + i);
+			}
+		} catch (Exception e) {
+			System.out.println ("couldn't output genomes and indeces");
+		}
+	}
+/*
+	public static void printSegment (Segment segment,
+			AbstractTabbedDataWriter writer) {
+		String [] data = new String [segment.ends.length * 2];
+		for (int i = 0, j = 0; i < segment.ends.length; i++, j++) {
+			String start = segment.reverse[i] ? "-" : "";
+			data[j++] = start + segment.starts[i];
+			data[j] = start + segment.ends[i];
+		}
+		writer.printRow (data);
+	}
+*/
+	public static String doubleToString (double number, int decimals) {
+		DecimalFormat format = new DecimalFormat ();
+		format.setMaximumFractionDigits (decimals);
+		return format.format (number);
+	}
+	
+	public static String getSeqPartOfFile (SegmentDataProcessor processor) {
+		return "seq_" + processor.get (SEQUENCE_INDEX).toString ();
+	}
+	
+	public static String getReadableMultiplicity (Segment segment) {
+		return getReadableMultiplicity (segment.multiplicityType (), segment.left.length);
+	}
+	
+	public static String getReadableMultiplicity (long multiplicity, int count) {
+		String val = Long.toBinaryString (multiplicity).replace ('0', '.').replace ('1', '*');
+		while (val.length () < count)
+			val = "." + val;
+		return val;
+	}
+	
+	public static void addChromByStart (Hashtable table, Chromosome chrom) {
+		table.put (new Long (chrom.getStart ()), chrom);
+	}
+	
+	public static Chromosome getChromByStart (Hashtable table, Chromosome chrom) {
+		return (Chromosome) table.get (new Long (chrom.getStart ()));
+	}
+	
+	public static Feature getFeatByStart (Hashtable table, Chromosome chrom) {
+		return (Feature) table.get (new Long (chrom.getStart ()));
+	}
+	
+	public static Chromosome removeChromByStart (Hashtable table, Chromosome chrom) {
+		return (Chromosome) table.remove (new Long (chrom.getStart ()));
+	}
+	
+	public static Iterator getFeatures (BaseViewerModel model, int genome) {
+		Sequence holder = (Sequence) model.getGenomeBySourceIndex (genome)
+				.getAnnotationSequence ();
+		FeatureHolder hold = holder.filter (new FeatureFilter.And (NULL_AVOIDER,
+				new FeatureFilter.ByAnnotationType (
+				AnnotationType.ANY)), true);
+		return hold.features ();
+	}
+
+
+	/**
+	 * returns asap dbxref if exists, then any other dbxref, then label or gene
+	 * annotation if one exists.
+	 * 
+	 * @param feat
+	 * @return
+	 */
+	public static String getUniqueId (Feature feat) {
+		String val = getDBXrefID (feat, ASAP);
+		if (val == null)
+			val = getDBXrefID (feat, ERIC);
+		if (val == null)
+			val = getDBXrefID (feat, "");
+		if (val == null) {
+			if (feat.getAnnotation ().containsProperty (LABEL_STRING)) {
+				val = (String) feat.getAnnotation ().getProperty (LABEL_STRING);
+			}
+			else if (feat.getAnnotation ().containsProperty ("gene")) {
+				val = (String) feat.getAnnotation ().getProperty ("gene");
+			}
+			else if (feat.getAnnotation().containsProperty("locus_tag"))
+				val = (String) feat.getAnnotation ().getProperty ("locus_tag");
+		}
+		return val;
+	}
+	
+	public static String getTruncatedDBXrefID (Feature feat, String header) {
+		String val = getDBXrefID (feat, header);
+		return val.substring(val.indexOf(':') + 1);
+	}
+	
+	
+	public static String getDBXrefID (Feature feat, String header) {
+		String id = null;
+		if (!feat.getAnnotation().containsProperty(DB_XREF))
+			return null;
+		Object val = feat.getAnnotation ().getProperty (DB_XREF);
+		if (val != null) {
+			if (val instanceof Collection) {
+				Collection ids = (Collection) val;
+				Iterator itty = ids.iterator ();
+				while (itty.hasNext ()) {
+					id = (String) itty.next ();
+					if (id.toLowerCase ().indexOf (header) > -1)
+						return id;
+				}
+			}
+			else if (val instanceof String) {
+				id = (String) val;
+				if (id.toLowerCase ().indexOf (header) > -1)
+					return id;
+			}
+			else
+				System.out.println ("class " + val.getClass ());
+		}
+		return null;
+			}
+	
+	public static Collection getDBXrefCollection (Feature feat) {
+		Object val = feat.getAnnotation ().getProperty (DB_XREF);
+		if (val != null && val instanceof String) {
+			ArrayList temp = new ArrayList ();
+			temp.add(val);
+			val = temp;
+		}
+		if (!(val instanceof Collection))
+			return null;
+		else
+			return (Collection) val;
+	}
+
+	public static Hashtable getContigFeatures (Genome genome) {
+		Sequence seq = genome.getAnnotationSequence();
+		Iterator itty = seq.features();
+		Hashtable all_contigs = new Hashtable (seq.countFeatures());
+		while (itty.hasNext()) {
+			Object obj = itty.next ();
+			if (obj instanceof ComponentFeature) {
+				ComponentFeature feat = (ComponentFeature) obj;
+				all_contigs.put(new Long (feat.getLocation().getMin()), feat);
+			}
+		}
+		return all_contigs;
+	}
+	
+	/**
+	 * Splits the byte array into an array of arrays of length <code>k</code>
+	 * 
+	 * @param ar the array to split up
+	 * @param k the length of the mers to split <code>ar</code> into
+	 * @return an array of length <i>l</i>, where <i>l</i> = <code> r == 0 ? q : q+1 </code> </br>
+	 * 		   and <code>ar.length = q*k + r</code> 
+	 */
+	public static byte[][] splitIntoKmers(byte[] ar, int k){
+		// ar.length = q*k + r   good ol' Division Algorithm
+		int r = ar.length % k;
+		int q = ar.length/k;
+		byte[][] ret = null;
+		if (r == 0){
+			ret = new byte[q][];
+		} else {
+			ret = new byte[q+1][];
+		}
+		int curr = 0;
+		for (int merI = 0; merI < q; merI++){
+			ret[merI] = new byte[k];
+			System.arraycopy(ar, curr, ret[merI], 0, k);
+			curr = curr+k;
+		}
+		
+		if (r != 0) {
+			ret[ret.length-1] = new byte[r];
+			System.arraycopy(ar,curr,ret[ret.length-1],0,r);
+		}
+		
+		return ret;
+	}
+
+	/**
+	 * Splits the char array into an array of arrays of length <code>k</code>
+	 * 
+	 * @param ar the array to split up
+	 * @param k the length of the mers to split <code>ar</code> into
+	 * @return an array of length <i>l</i>, where <i>l</i> = <code> r == 0 ? q : q+1 </code> </br>
+	 * 		   and <code>ar.length = q*k + r</code> 
+	 */
+	public static char[][] splitIntoKmers(char[] ar, int k){
+		// ar.length = q*k + r   good ol' Division Algorithm
+		int r = ar.length % k;
+		int q = ar.length/k;
+		char[][] ret = null;
+		if (r == 0){
+			ret = new char[q][];
+		} else {
+			ret = new char[q+1][];
+		}
+		int curr = 0;
+		for (int merI = 0; merI < q; merI++){
+			ret[merI] = new char[k];
+			System.arraycopy(ar, curr, ret[merI], 0, k);
+			curr = curr+k;
+		}
+		
+		if (r != 0) {
+			ret[ret.length-1] = new char[r];
+			System.arraycopy(ar,curr,ret[ret.length-1],0,r);
+		}
+		
+		return ret;
+	}
+}
diff --git a/src/org/gel/mauve/ModelBuilder.java b/src/org/gel/mauve/ModelBuilder.java
new file mode 100644
index 0000000..c15fc92
--- /dev/null
+++ b/src/org/gel/mauve/ModelBuilder.java
@@ -0,0 +1,566 @@
+package org.gel.mauve;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+import org.gel.mauve.color.LCBColorScheme;
+import org.gel.mauve.color.NormalizedOffsetColorScheme;
+
+/**
+ * 
+ * Methods to parse data files into models.
+ *  
+ */
+public class ModelBuilder
+{
+
+    // Delimiter for headers
+    private final static String DELIMS = "\t";
+
+    public static BaseViewerModel buildModel(URL url, String auth_token, ModelProgressListener listener) throws IOException, MauveFormatException
+    {
+        if (listener != null)
+        {
+            listener.buildStart();
+            listener.downloadStart();
+        }
+        
+        
+        // Find cached directory, if it exists.
+        File dir = null;
+        try
+        {
+            dir = getCachedDirectory(url);
+        }
+        catch (BackingStoreException e)
+        {
+            System.err.println("Error reading preferences.  Error follows.  Will load from server.");
+            e.printStackTrace();
+        }
+        
+        if (dir == null)
+        {
+	        // create the Jar URL
+	        String jar_url_str = "jar:" + url;
+	        if( auth_token != null )
+	        	jar_url_str += "&" + auth_token;
+			jar_url_str += "!/";
+	        URL jar_url = new URL(jar_url_str);
+	        System.err.println("Loading alignment from " + url.toString());
+	
+	        // Get URL connection.
+	        URLConnection urlConn = jar_url.openConnection();
+	        if (! (urlConn instanceof JarURLConnection))
+	        {
+	            throw new IOException("URL does not point to a jar file.");
+	        }
+	        JarURLConnection conn = (JarURLConnection) urlConn;
+	
+	        // Create a temporary directory.
+	        dir = File.createTempFile("mauve", "dir");
+	        dir.delete();
+	        dir.mkdir();
+	        if (!dir.exists() || !dir.isDirectory())
+	        {
+	            throw new IOException("Couldn't create temporary directory.");
+	        }
+	        System.err.println("Saving alignment to: " + dir.toString());
+	        
+	        // Unpack contents into directory.
+	        JarFile jarFile = conn.getJarFile();
+	        System.err.println("jar location: " + jarFile.getName() );
+	        
+	        // Create directory structure.
+	        Enumeration entries = jarFile.entries();
+	        while (entries.hasMoreElements())
+	        {
+	            JarEntry entry = (JarEntry) entries.nextElement();
+	            
+	            if (entry.isDirectory())
+	            {
+	                File newFile = new File(dir, entry.getName());
+	                newFile.mkdirs();
+	            }
+	        }
+	
+	        // Output files.
+	        entries = jarFile.entries();
+	        while (entries.hasMoreElements())
+	        {
+	            JarEntry entry = (JarEntry) entries.nextElement();
+	
+	            if (!entry.isDirectory())
+	            {
+	                File newFile = new File(dir, entry.getName());
+	                FileOutputStream fs = new FileOutputStream(newFile);
+	                InputStream is = jarFile.getInputStream(entry);
+	                byte[] buf = new byte[1024];
+	                int len;
+	                while((len = is.read(buf)) != -1)
+	                {
+	                    fs.write(buf, 0, len);
+	                }
+	                is.close();
+	                fs.close();
+	            }
+	        }
+
+	        saveCachedDirectory(url, dir);
+        }
+        
+        // Find alignment file and other data in manifest.
+        Manifest mf = new Manifest(new FileInputStream(new File(dir, "META-INF/MANIFEST.MF")));
+        String alignmentName = mf.getMainAttributes().getValue("Mauve-Alignment");
+        File alignmentFile = new File(dir, alignmentName);
+        BaseViewerModel model = buildModel(alignmentFile, listener);
+        
+        model.setSourceURL(url);
+        
+        // Assign database ids.
+        for (int i = 0; i < model.getSequenceCount(); i++)
+        {
+            Genome g = model.getGenomeByViewingIndex(i);
+            g.setID(mf.getMainAttributes().getValue("Sequence-" + (i + 1) + "-ID"));
+        }
+        return model;
+    }
+    
+    public static File getCachedDirectory(URL url) throws BackingStoreException
+    {
+        Preferences prefs = getPreferencesForUrl(url);
+        String path = prefs.get("dir", null);
+        if (path == null) return null;
+        File f = new File(path);
+        if (!f.canRead() || !f.isDirectory()) return null;
+        return f;
+    }
+    
+    static void saveCachedDirectory(URL url, File dir) throws IOException
+    {
+        try
+        {
+            Preferences prefs = getPreferencesForUrl(url);
+            prefs.put("dir", dir.getCanonicalPath());
+            prefs.flush();
+        }
+        catch (BackingStoreException e)
+        {
+            System.err.println("Couldn't store preferences.  Exception follows.");
+            e.printStackTrace();
+        }
+        
+    }
+
+    /** configures whether a disk cache can be used to speed up repeated loading of alignments */
+    static protected boolean useDiskCache = true;
+    
+    /** configures whether a disk cache can be used to speed up repeated loading of alignments */
+    public static void setUseDiskCache(boolean useCache)
+    {
+    	useDiskCache = useCache;
+    }
+    /** returns whether a disk cache should be used to speed up repeated loading of alignments */
+    public static boolean getUseDiskCache()
+    {
+    	return useDiskCache;
+    }
+    
+    private static boolean deleteDirectory(File path) 
+    {
+    	if (path.exists()) {
+			File[] files = path.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				if (files[i].isDirectory()) {
+					deleteDirectory(files[i]);
+				} else {
+					files[i].delete();
+				}
+			}
+		}
+		return (path.delete());
+	}
+    
+    public static void clearDataCache() throws BackingStoreException
+    {
+        Preferences prefs = Preferences.userNodeForPackage(ModelBuilder.class);
+        String[] children = prefs.childrenNames();
+        
+        for (int i = 0; i < children.length; i++)
+        {
+            Preferences child = prefs.node(children[i]);
+            String dir = child.get("dir", null);
+            if (dir != null)
+            {
+            	// delete this directory
+            	deleteDirectory(new File(dir));
+            }
+            child.removeNode();
+        }
+    }
+    
+    private static Preferences getPreferencesForUrl(URL url) throws BackingStoreException
+    {
+        Preferences prefs = Preferences.userNodeForPackage(ModelBuilder.class);
+        String[] children = prefs.childrenNames();
+        
+        for (int i = 0; i < children.length; i++)
+        {
+            Preferences child = prefs.node(children[i]);
+            String urlValue = child.get("url", null);
+            if (url.toString().equals(urlValue))
+            {
+                return child;
+            }
+        }
+        
+        // Didn't find one, so create one.
+        
+        int nodeNumber = 0;
+        Preferences newChild = prefs.node("child" + nodeNumber);
+        while (newChild.get("url", null) != null)
+        {
+            nodeNumber++;
+            newChild = prefs.node("child" + nodeNumber);
+        }
+        
+        newChild.put("url", url.toString());
+        return newChild;
+    }
+    
+    /**
+     * @throws IOException
+     * @throws MauveFormatException
+     * 
+     * Read some genome comparison data from a file. Attempts to detect the file
+     * format. Valid input file formats are XMFA, Mauve FormatVersion 4
+     * (possibly containing gapped alignments), and Mauve FormatVersion 3,
+     * containing only ungapped alignments.
+     */
+    public static BaseViewerModel buildModel(File src, ModelProgressListener listener) throws IOException, MauveFormatException
+    {
+        if (listener != null)
+        {
+            listener.buildStart();
+            listener.alignmentStart();
+        }
+        
+        RandomAccessFile inputFile = new RandomAccessFile(src, "r");
+        String inputLine = inputFile.readLine();
+        if(inputLine == null)
+        	throw new IOException("Empty alignment file.  If the alignment file was generated by Mauve then the genomes may be unrelated.");
+        String[] params = inputLine.split(DELIMS);
+
+        // Try to find a version number at the head of the file.
+        int versionNumber;
+        try
+        {
+            versionNumber = Integer.parseInt(params[1]);
+        }
+        catch (Exception e)
+        {
+            // probably a NumberFormatException or ArrayIndexOfOutBounds because
+            // this is not an XMFA file
+            versionNumber = -1;
+            inputFile.seek(0);
+        }
+
+        if (versionNumber == -1) // XMFA file
+        {
+            XmfaViewerModel model = new XmfaViewerModel(src, listener);
+            if (listener != null)
+            {
+                listener.done();
+            }
+            model.setReference(model.getGenomeBySourceIndex(0));
+            return model;
+        }
+        else if (versionNumber == 3) // MUMS file
+        {
+            BaseViewerModel model = new BaseViewerModel(src);
+            readCommon(inputFile, model);
+            readMums(inputFile, model);
+            model.setColorScheme(new NormalizedOffsetColorScheme());
+            if (listener != null)
+            {
+                listener.done();
+            }
+            model.setReference(model.getGenomeBySourceIndex(0));
+            return model;
+        }
+        else if (versionNumber == 4) // Mauve file
+        {
+            LcbViewerModel model = new LcbViewerModel(src);
+            readCommon(inputFile, model);
+            readMauveAlignment(inputFile, model);
+            initMauveLCBs(model);
+            model.setColorScheme(new LCBColorScheme());
+            model.initModelLCBs();
+            if (listener != null)
+            {
+                listener.done();
+            }
+            model.setReference(model.getGenomeBySourceIndex(0));
+            return model;
+        }
+        else if (versionNumber == 1)
+        {
+            throw new MauveFormatException("Format version 1 no longer supported.");
+        }
+        else if (versionNumber == 2)
+        {
+            throw new MauveFormatException("Format version 2 no longer supported.");
+        }
+        else
+        {
+            throw new MauveFormatException("Rearrangement data format version unsupported\n");
+        }
+    }
+
+    /**
+     * @param inputFile
+     * @param model
+     * @throws IOException
+     * 
+     * All non-XMFA formats begin with a common header: FormatVersion <int>
+     * SequenceCount <int>Sequence <N>File <String>Sequence <N>Length <int>
+     * 
+     * This reads in this data and constructs genomes for each genetic sequence,
+     * adding them to the model.
+     */
+    private static void readCommon(RandomAccessFile inputFile, BaseViewerModel model) throws IOException
+    {
+        String inputLine = inputFile.readLine();
+        String[] params = inputLine.split(DELIMS);
+        model.setSequenceCount(Integer.parseInt(params[1]));
+
+        for (int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+        {
+            // read file name
+            inputLine = inputFile.readLine();
+            StringTokenizer st = new StringTokenizer(inputLine);
+            st.nextToken();
+            String name = st.nextToken();
+
+            // read sequence length
+            inputLine = inputFile.readLine();
+            st = new StringTokenizer(inputLine, DELIMS);
+            st.nextToken();
+            long length = Long.parseLong(st.nextToken());
+
+            Genome g = GenomeBuilder.buildGenome(length, name, model, seqI);
+            model.setGenome(seqI, g);
+        }
+
+        // The next line is ignored, regardless of format!
+        // it either holds a number called "IntervalCount" (MAUVE) or
+        // "MatchCount" (MUMS)
+        // of the form <Name> <int>
+        inputFile.readLine();
+    }
+
+    /**
+     * @param inputFile
+     * @param model
+     * @throws IOException
+     * @throws MauveFormatException
+     * 
+     * Read a MUMS-format file, which contains only ungapped alignments.
+     *  
+     */
+    private static void readMums(RandomAccessFile inputFile, BaseViewerModel model) throws IOException, MauveFormatException
+    {
+
+        String inputLine;
+
+        while ((inputLine = inputFile.readLine()) != null)
+        {
+
+            Match new_match = new Match(model.getSequenceCount());
+            long m_length = 0;
+            int tokenI = 0;
+            StringTokenizer st = new StringTokenizer(inputLine, DELIMS);
+            int genomeIndex = 0;
+            while (st.hasMoreTokens())
+            {
+                String s = st.nextToken();
+
+                if (tokenI == 0)
+                {
+                    m_length = Integer.parseInt(s);
+                }
+                else if (tokenI == model.getSequenceCount() + 1)
+                {
+                    tokenI++;
+                    continue;
+                }
+                else if (tokenI > model.getSequenceCount())
+                {
+                    if (Long.parseLong(s) != 0)
+                    {
+                        throw new MauveFormatException("Match data must have all linked inclusions removed\n");
+                    }
+                }
+                else
+                {
+                    Genome g = model.getGenomeByViewingIndex(genomeIndex);
+                    
+                    new_match.setLength(g, m_length);
+                    new_match.setStart(g, Long.parseLong(s));
+                    if (new_match.getStart(g) < 0)
+                    {
+                        new_match.setReverse(g, true);
+                        new_match.setStart(g, -new_match.getStart(g));
+                    }
+                    else if (new_match.getStart(g) == Match.NO_MATCH)
+                    {
+                        new_match.setLength(g, 0);
+                    }
+                    genomeIndex++;
+                }
+                tokenI++;
+            }
+            model.addMatch(new_match);
+            if (model.getMatchCount() % 1000 == 0)
+            {
+                MyConsole.out().println("Processed matches: " + model.getMatchCount());
+            }
+        }
+    }
+
+    /**
+     * Read matches from a Mauve FormatVersion 4 alignment. Ignores any gapped
+     * alignments that may be contained in the file. Creates a locally collinear
+     * block (LCB) object for each aligned "interval" in the file.
+     */
+    private static void readMauveAlignment(RandomAccessFile match_reader, LcbViewerModel model) throws IOException
+    {
+
+        String inputLine;
+        int clustal_line = 0;
+        int cur_lcb = 0;
+        int lcb_start = 0;
+        model.setLcbCount(1);
+        Vector lcbVector = new Vector();
+        
+        while ((inputLine = match_reader.readLine()) != null)
+        {
+            if (clustal_line == model.getSequenceCount() + 2)
+                clustal_line = 0;
+            if (clustal_line > 0)
+            {
+                clustal_line++;
+                continue;
+            }
+
+            Match new_match = new Match(model.getSequenceCount());
+            new_match.lcb = cur_lcb;
+            long m_length = 0;
+            int tokenI = 0;
+            StringTokenizer st = new StringTokenizer(inputLine, DELIMS);
+            int genomeIndex = 0;
+            while (st.hasMoreTokens())
+            {
+                String s = st.nextToken();
+
+                if (tokenI == 0)
+                {
+                    if (s.equals("GappedAlignment") || s.equals("ClustalResult"))
+                    {
+                        clustal_line = 1;
+                        new_match = null;
+                        break;
+                    }
+                    else if (s.startsWith("Interval"))
+                    {
+                        if (model.getMatchCount() > 0)
+                        {
+                            LCB new_lcb = model.getLCB(lcb_start, model.getMatchCount());
+                            lcb_start = model.getMatchCount();
+                            if (new_lcb != null)
+                            {
+                                lcbVector.add(new_lcb);
+                                cur_lcb++;
+                                model.setLcbCount(model.getLcbCount() + 1);
+                            }
+                            new_match = null;
+                        }
+                        break;
+                    }
+                    else
+                        m_length = Integer.parseInt(s);
+                }
+                else
+                {
+                    Genome g = model.getGenomeByViewingIndex(genomeIndex);
+                    
+                    new_match.setLength(g, m_length);
+                    new_match.setStart(g, Long.parseLong(s));
+                    if (new_match.getStart(g) < 0)
+                    {
+                        new_match.setReverse(g, true);
+                        new_match.setStart(g, -new_match.getStart(g));
+                    }
+                    else if (new_match.getStart(g) == Match.NO_MATCH)
+                    {
+                        new_match.setLength(g, 0);
+                    }
+                    genomeIndex++;
+                }
+                tokenI++;
+            }
+            if (tokenI != model.getSequenceCount() + 1)
+            {
+                continue;
+            }
+            int count = 0;
+            for (int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+            {
+                Genome g = model.getGenomeByViewingIndex(seqI);
+                if (new_match.getLength(g) != Match.NO_MATCH)
+                {
+                    count++;
+                }
+            }
+            if (count > 1)
+            {
+                model.addMatch(new_match);
+            }
+            if (model.getMatchCount() % 1000 == 0)
+            {
+                MyConsole.out().println("Processed matches: " + model.getMatchCount());
+            }
+        }
+        LCB new_lcb = model.getLCB(lcb_start, model.getMatchCount());
+        if (new_lcb != null)
+            lcbVector.add(new_lcb);
+
+        model.setFullLcbList(new LCB[lcbVector.size()]);
+        model.setFullLcbList((LCB[]) lcbVector.toArray(model.getFullLcbList()));
+    }
+
+    /**
+     * @param model
+     */
+    private static void initMauveLCBs(LcbViewerModel model)
+    {
+        model.setVisibleLcbList(new LCB[model.getFullLcbList().length]);
+        model.setDelLcbList(new LCB[0]);
+        System.arraycopy(model.getFullLcbList(), 0, model.getVisibleLcbList(), 0, model.getVisibleLcbList().length);
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/ModelEvent.java b/src/org/gel/mauve/ModelEvent.java
new file mode 100644
index 0000000..df0a039
--- /dev/null
+++ b/src/org/gel/mauve/ModelEvent.java
@@ -0,0 +1,13 @@
+package org.gel.mauve;
+
+import java.util.EventObject;
+
+
+public class ModelEvent extends EventObject
+{
+	static final long serialVersionUID = 234234;
+    public ModelEvent(BaseViewerModel source)
+    {
+        super(source);
+    }
+}
diff --git a/src/org/gel/mauve/ModelListener.java b/src/org/gel/mauve/ModelListener.java
new file mode 100644
index 0000000..82082b6
--- /dev/null
+++ b/src/org/gel/mauve/ModelListener.java
@@ -0,0 +1,22 @@
+package org.gel.mauve;
+
+import java.util.EventListener;
+
+public interface ModelListener extends EventListener
+{
+    public void colorChanged(ModelEvent event);
+    public void weightChanged(ModelEvent event);
+    public void drawingSettingsChanged(ModelEvent event);
+    public void modeChanged(ModelEvent event);
+    public void modelReloadStart(ModelEvent event);
+    public void modelReloadEnd(ModelEvent event);
+    public void viewableRangeChangeStart(ModelEvent event);
+    public void viewableRangeChanged(ModelEvent event);
+    public void viewableRangeChangeEnd(ModelEvent event);
+    public void printingStart(ModelEvent event);
+    public void printingEnd(ModelEvent event);
+    public void genomesReordered(ModelEvent event);
+    public void referenceChanged(ModelEvent event);
+    public void genomeVisibilityChanged(ModelEvent event);
+	public void attributesChanged(ModelEvent modelEvent);
+}
diff --git a/src/org/gel/mauve/ModelProgressListener.java b/src/org/gel/mauve/ModelProgressListener.java
new file mode 100644
index 0000000..4d7e3a4
--- /dev/null
+++ b/src/org/gel/mauve/ModelProgressListener.java
@@ -0,0 +1,15 @@
+package org.gel.mauve;
+
+public interface ModelProgressListener {
+	void buildStart ();
+
+	void downloadStart ();
+
+	void alignmentStart ();
+
+	void alignmentEnd (int sequenceCount);
+
+	void featureStart (int sequenceIndex);
+
+	void done ();
+}
diff --git a/src/org/gel/mauve/MyConsole.java b/src/org/gel/mauve/MyConsole.java
new file mode 100644
index 0000000..1781510
--- /dev/null
+++ b/src/org/gel/mauve/MyConsole.java
@@ -0,0 +1,47 @@
+package org.gel.mauve;
+
+import gr.zeus.ui.JConsole;
+
+
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.io.PrintStream;
+
+public class MyConsole {
+	private static boolean useSwing = false;
+
+	private static JConsole console;
+
+	public static void setUseSwing (boolean b) {
+		if (b && !useSwing) {
+			console = JConsole.getConsole ();
+			console.setTitle ("Mauve Console");
+			console.setSize (400, 400);
+			Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+			console.setLocation(dim.width-400, 0);
+			console.startConsole ();
+		} else if (!b && useSwing) {
+			console.stopConsole ();
+			console = null;
+		}
+
+		useSwing = b;
+	}
+
+	public static void showConsole () {
+		if (useSwing) {
+			console.showConsole ();
+		}
+	}
+
+	public static PrintStream err () {
+		if (useSwing) {
+			console.showConsole ();
+		}
+		return System.err;
+	}
+
+	public static PrintStream out () {
+		return System.out;
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/OptionsBuilder.java b/src/org/gel/mauve/OptionsBuilder.java
new file mode 100644
index 0000000..5ed75a7
--- /dev/null
+++ b/src/org/gel/mauve/OptionsBuilder.java
@@ -0,0 +1,84 @@
+package org.gel.mauve;
+
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.MissingArgumentException;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.UnrecognizedOptionException;
+
+public class OptionsBuilder {
+	
+	private Options options;
+	
+	public OptionsBuilder(){
+		options = new Options();
+	}
+	
+	public Option addBoolean(String opt, String desc){
+		Option option = new Option(opt, desc);
+		options.addOption(option);
+		return option;
+	}
+	
+	@SuppressWarnings("static-access")
+	public Option addArgument(String argName, String desc, String opt, boolean required){
+		Option option = OptionBuilder.withArgName(argName)
+									   .hasArg()
+									   .isRequired(required)
+									   .withDescription(desc)
+									   .create(opt);
+		options.addOption(option);
+		return option;
+	}
+	
+	public Options getOptions(){
+		return options;
+	}
+	
+	public OptionGroup addMutExclOptions(Option a, Option b){
+		OptionGroup grp = new OptionGroup();
+		grp.addOption(a);
+		grp.addOption(b);
+		options.addOptionGroup(grp);
+		return grp;
+	}
+	
+	public OptionGroup addOptionGroup(OptionGroup grp){
+		options.addOptionGroup(grp);
+		return grp;
+	}
+	
+	public static CommandLine getCmdLine(Options opts, String[] args){
+		CommandLineParser parser = new GnuParser();
+		try {
+			return parser.parse(opts, args);
+			
+		} catch (UnrecognizedOptionException e) { 
+			String opt = e.getOption();
+			System.err.println("Unrecognized option: " + opt);
+		//	System.exit(-1);
+		} catch (MissingArgumentException e){
+			Option opt = e.getOption();
+			System.err.println("Error: " + opt.getOpt() + " not specified.");
+		//	System.exit(-1);
+		} catch (MissingOptionException e){
+			Iterator it = e.getMissingOptions().iterator();
+			while (it.hasNext()){
+				System.err.println("Missing required option: " + it.next());
+			}
+		//	System.exit(-1);
+		} catch (ParseException e){
+			System.err.println("Error parsing arguments. Reason: " + e.getMessage());
+		//	System.exit(-1);
+		} 
+		return null;
+	}
+}
diff --git a/src/org/gel/mauve/PreferencesDump.java b/src/org/gel/mauve/PreferencesDump.java
new file mode 100644
index 0000000..fbe9b00
--- /dev/null
+++ b/src/org/gel/mauve/PreferencesDump.java
@@ -0,0 +1,23 @@
+package org.gel.mauve;
+
+import java.util.prefs.Preferences;
+
+public class PreferencesDump {
+	public static void main (String [] args) {
+		try {
+			Preferences p = Preferences.userNodeForPackage (ModelBuilder.class);
+			String [] childName = p.childrenNames ();
+			for (int i = 0; i < childName.length; i++) {
+				Preferences c = p.node (childName[i]);
+				System.out.println ("Node: " + c.name ());
+				String [] keys = c.keys ();
+				for (int j = 0; j < keys.length; j++) {
+					System.out.println (keys[j] + ": "
+							+ c.get (keys[j], "Not set"));
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace ();
+		}
+	}
+}
diff --git a/src/org/gel/mauve/SeqFeatureData.java b/src/org/gel/mauve/SeqFeatureData.java
new file mode 100644
index 0000000..359ddd1
--- /dev/null
+++ b/src/org/gel/mauve/SeqFeatureData.java
@@ -0,0 +1,315 @@
+package org.gel.mauve;
+
+import java.awt.Component;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.JOptionPane;
+
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.symbol.Location;
+import org.gel.mauve.gui.navigation.AnnotationContainsFilter;
+import org.gel.mauve.gui.navigation.NavigationPanel;
+
+/**
+ * Utility class for data specific to sequence navigator involving genomes and
+ * their features.
+ * 
+ * @author rissman
+ * 
+ */
+public class SeqFeatureData implements MauveConstants {
+
+	public static FeatureComparator feature_comp = new FeatureComparator ();
+
+	/**
+	 * private constructor prevents instantiation; all static
+	 * 
+	 */
+	private SeqFeatureData () {
+	}
+
+	/**
+	 * Returns a filter that will accept annotations matching the desired keys
+	 * and values.
+	 * 
+	 * @param data
+	 *            An array of length 3 arrays, each corresponding to a key,
+	 *            value, and whether the value is exact (array indeces specified
+	 *            in MauveConstants)
+	 * @return A filter that can be used to accept matching features
+	 */
+	public static FeatureFilter getFilter (String [][] data) {
+		FeatureFilter and = null;
+		for (int i = 0; i < data.length; i++) {
+			StringTokenizer toke = SeqFeatureData
+					.separateFields (SeqFeatureData
+							.readToActual (data[i][NavigationPanel.FIELD]));
+			FeatureFilter or = null;
+			while (toke.hasMoreTokens ()) {
+				FeatureFilter cur = null;
+				cur = new AnnotationContainsFilter (toke.nextToken (),
+						data[i][NavigationPanel.VALUE].toLowerCase (), Boolean
+								.valueOf (data[i][NavigationPanel.EXACT])
+								.booleanValue ());
+				if (or != null) {
+					or = new FeatureFilter.Or (or, cur);
+				} else
+					or = cur;
+			}
+			if (and != null)
+				and = new FeatureFilter.And (and, or);
+			else
+				and = or;
+		}
+		and = new FeatureFilter.And (NULL_AVOIDER, and);
+		// and = new FeatureFilter.And (ass)), and);
+		return and;
+	}
+
+	/**
+	 * Finds all features associated with specified genomes matching the
+	 * specified key value pairs.
+	 * 
+	 * @param nomes
+	 *            An array of which genomes whose features should be searched.
+	 * @param data
+	 *            An array of arrays containing desired key-value pairs, indeces
+	 *            specified in MauveConstants
+	 * @return An array of LinkedLists. The first value in each list is the
+	 *         genome the list corresponds to; all other values are features
+	 *         associated with that genome
+	 */
+	public static LinkedList [] findFeatures (Genome [] nomes, String [][] data) {
+		LinkedList [] nome_data = new LinkedList [nomes.length];
+		FeatureFilter filter = getFilter (data);
+		for (int i = 0; i < nomes.length; i++) {
+			FeatureHolder hold = nomes[i].getAnnotationSequence ();
+			hold = hold.filter (filter, true);
+			LinkedList list = new LinkedList ();
+			list.add (nomes[i]);
+			Iterator itty = hold.features ();
+			while (itty.hasNext ())
+				list.add (itty.next ());
+			nome_data[i] = list;
+		}
+		return nome_data;
+	}
+
+	/**
+	 * Removes multiple features with the same location, so only one is
+	 * displayed in the tree
+	 * 
+	 * @param feats
+	 *            The list of features that might contain duplicates
+	 */
+	public static void removeLocationDuplicates (LinkedList feats) {
+		if (feats.size () > 1) {
+			Collections.sort (feats, feature_comp);
+			Object first = feats.get (0);
+			Object compare = null;
+			int index = 1;
+			do {
+				compare = feats.get (index);
+				if (feature_comp.compare (first, compare) == 0)
+					feats.remove (index);
+				else {
+					first = compare;
+					index++;
+				}
+			} while (index < feats.size ());
+		}
+	}
+
+	/**
+	 * A comparator for features.
+	 * 
+	 */
+	static class FeatureComparator implements Comparator {
+		/**
+		 * Returns -1 if Feature a is first in the sequence, 1 if b is first,
+		 * and 0 if they are at the same place
+		 * 
+		 * @param a
+		 *            The first object to compare
+		 * @param b
+		 *            The second object to compare
+		 */
+		public int compare (Object a, Object b) {
+			int one = ((Feature) a).getLocation ().getMin ();
+			int two = ((Feature) b).getLocation ().getMin ();
+			return (one == two) ? 0 : (one < two) ? -1 : 1;
+		}
+	}
+
+	/**
+	 * returns a tokenizer with string separated at '/'
+	 * 
+	 * @param fields
+	 * @return
+	 */
+	public static StringTokenizer separateFields (String fields) {
+		return new StringTokenizer (fields, "/", false);
+	}
+
+	/**
+	 * converts spaces in string to underscores
+	 * 
+	 * @param field
+	 *            The human readible version of the field
+	 * @return The qualifier name as it appears in data
+	 */
+	public static String readToActual (String field) {
+		String actual = (String) READ_TO_ACTUAL.get (field);
+		if (actual != null)
+			field = actual;
+		else
+			field = field.toLowerCase ();
+		return field.replace (' ', '_');
+	}
+
+	/**
+	 * finds the center sequence coordinate of a feature
+	 * 
+	 * @param feature
+	 *            The feature whose center should be found
+	 * @return A long representing the sequence position of the center of the
+	 *         feature
+	 */
+	public static long centerOfFeature (Feature feature) {
+		Location loca = feature.getLocation ();
+		return loca.getMin () + (loca.getMax () - loca.getMin ()) / 2;
+	}
+
+	/**
+	 * Parses a FilterCacheSpec to see if it contains a filter that is
+	 * associated with a particular type of Feature. Will only return one type;
+	 * so will not work for some or filters.
+	 * 
+	 * @param spec
+	 *            The spec to search
+	 * @return A string corresponding to the type associated with the spec, or
+	 *         MauveConstants.ANY_FEATURE
+	 */
+	public static String getTypeFromFilterSpec (FilterCacheSpec spec) {
+		if (spec.getFilter () != null)
+			return getTypeFromFilter (spec.getFilter ());
+		else
+			return null;
+	}
+
+	/**
+	 * Parses a FeatureFilter to see if it contains a filter that is associated
+	 * with a particular type of Feature. Will only return one type; so will not
+	 * work for some or filters.
+	 * 
+	 * @param filter
+	 *            The filter to search
+	 * @return A string corresponding to the type associated with the filter, or
+	 *         MauveConstants.ANY_FEATURE
+	 */
+	public static String getTypeFromFilter (FeatureFilter filter) {
+		String type = null;
+		if (filter instanceof FeatureFilter.ByType)
+			type = ((FeatureFilter.ByType) filter).getType ();
+		else if (filter instanceof FeatureFilter.And) {
+			type = getTypeFromFilter (((FeatureFilter.And) filter).getChild1 ());
+			if (type == null)
+				type = getTypeFromFilter (((FeatureFilter.And) filter)
+						.getChild2 ());
+		}
+		return type;
+	}
+
+	/**
+	 * Pops up a dialog box that allows a user to choose a specific (or all)
+	 * genome/s
+	 * 
+	 * @param parent
+	 *            The frame the JDialogs for user input should block
+	 * @param model
+	 *            The BaseViewerModel containing all possible genome choices
+	 * @param all_ok
+	 *            True if user should have the option to choose "All Genomes".
+	 * @param needs_annotations
+	 *            True if a genome should only be selectable if it has
+	 *            annotations loaded
+	 * @return An array of genomes that contains all genomes the user picked.
+	 */
+	public static Genome [] userSelectedGenomes (Component parent, BaseViewerModel model, 
+			boolean all_ok, boolean needs_annotations) {
+		Vector choices = userSelectableGenomes (model, all_ok, needs_annotations);
+		Object chosen = JOptionPane.showInputDialog(parent, "Choose Sequence to Navigate", 
+				"Go To. . .", JOptionPane.QUESTION_MESSAGE,
+				null, choices.toArray(), choices.get (0));
+		if (chosen == null)
+			return null;
+		else
+			return SeqFeatureData.convertIndexToSequence (choices, choices
+					.indexOf (chosen));
+	}
+
+	/**
+	 * constructs an array of all possible genomes a user should be able to pick
+	 * from. Will have an "All" option depending on parameters
+	 * 
+	 * @param model
+	 *            The model whose genomes are possible user selections
+	 * @param all_ok
+	 *            True if user should have the option to choose "All Genomes".
+	 * @param needs_annotations
+	 *            True if a genome should only be selectable if it has
+	 *            annotations loaded
+	 * @return An vector of genomes a user can choose between
+	 */
+	public static Vector userSelectableGenomes (BaseViewerModel model,
+			boolean all_ok, boolean needs_annotations) {
+		Vector choices = model.getGenomes ();
+		if (needs_annotations) {
+			Iterator itty = choices.iterator ();
+			while (itty.hasNext ()) {
+				Genome genome = (Genome) itty.next ();
+				if (genome.getAnnotationSequence () == null)
+					itty.remove ();
+			}
+		}
+		if (all_ok && choices.size () > 1)
+			choices.add (0, ALL_SEQUENCES);
+		return choices;
+	}
+
+	/**
+	 * returns an array representing the user selected genomes. If the first
+	 * choices in the
+	 * 
+	 * 
+	 * @param choices
+	 *            The vector of possible genomes
+	 * @param index
+	 *            The index selected from the vector of choices
+	 * @return An array of 1 containing the selected sequence unless the user
+	 *         chose all sequences, in which case the array will return all of
+	 *         them
+	 */
+	public static Genome [] convertIndexToSequence (Vector choices, int index) {
+		Genome [] nomes = null;
+		if (index == 0 && choices.get (0) instanceof String
+				&& ((String) choices.get (0)).equals (ALL_SEQUENCES)) {
+			int max = choices.size ();
+			nomes = new Genome [max - 1];
+			for (int i = 1; i < max; i++)
+				nomes[i - 1] = (Genome) choices.get (i);
+		} else {
+			nomes = new Genome [1];
+			nomes[0] = (Genome) choices.get (index);
+		}
+		return nomes;
+	}
+
+}
diff --git a/src/org/gel/mauve/SimilarityIndex.java b/src/org/gel/mauve/SimilarityIndex.java
new file mode 100644
index 0000000..063f300
--- /dev/null
+++ b/src/org/gel/mauve/SimilarityIndex.java
@@ -0,0 +1,380 @@
+package org.gel.mauve;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.gel.mauve.backbone.Backbone;
+import org.gel.mauve.backbone.BackboneList;
+import org.gel.mauve.histogram.ZoomHistogram;
+
+/**
+ * SimilarityIndex generates a queryable in-memory index of the similarity among
+ * a group of aligned sequences. This implementation uses the average entropy
+ * over a sliding window of alignment columns
+ */
+public class SimilarityIndex extends ZoomHistogram implements Serializable {
+	/** object format version */
+	static final long serialVersionUID = 3;
+
+
+	int window_size = 100;
+
+	/** < Average the similarity using a sliding window over this many columns */
+	
+	
+
+
+	SimilarityIndex (Genome g, XMFAAlignment xmfa, BackboneList bb_list)
+			throws IOException {
+		super(g);
+		calculateIndex (g, xmfa, bb_list);
+	}
+	
+	public SimilarityIndex(long seq_length, int level, int max_res, long[] sizes, long[] res,
+			byte [] sims) {
+		init (seq_length, level, max_res, sizes, res);
+		sim_index = sims;
+	}
+
+	/** Change the sequence indexed to seqI */
+	public void setSequence (Genome g) {
+		this.seq_length = g.getLength ();
+	}
+
+
+	void advanceIndex (String [] cols, int [] col_index, int seq) {
+		while (true) {
+			if (cols[seq].charAt (col_index[seq]) == '\r'
+					|| cols[seq].charAt (col_index[seq]) == '\n') {
+				col_index[seq]++;
+				continue;
+			}
+			break;
+		}
+	}
+
+	void skipGapColumns (Genome g, Object [] cols, int [] col_index) {
+		while (true) {
+			for (int seqI = 0; seqI < cols.length; seqI++) {
+				col_index[seqI]++; // automatically advance every sequence by
+				// one character
+				while (true) {
+					// ensure the character is valid
+
+					byte [] col = (byte []) cols[seqI];
+					int index = col_index[seqI];
+					byte c = col[index];
+					if (c == '\r' || c == '\n') {
+						col_index[seqI]++;
+						continue;
+					}
+					break;
+				}
+			}
+			// don't let the sequence of interest remain a gap
+			if (((byte []) cols[g.getSourceIndex ()])[col_index[g
+					.getSourceIndex ()]] == '-') {
+				continue;
+			}
+			break;
+		}
+	}
+
+	/**
+	 * Calculates index values
+	 */
+	public void calculateIndex (Genome g, XMFAAlignment alignment,
+			BackboneList bb_list) {
+		// for each nucleotide in the window, calculate its column's entropy,
+		// considering gaps as non-matching alignment characters
+		// question: should gaps in seqI be ignored? yes... think HGT.
+		long cur_offset = 0;
+		long buffer_left = -1;
+		long buffer_right = -1;
+		long buffer_size = 100000;
+		byte[][] cols = null;
+		double [] chars = new double [5];
+		double [] entropies = null;
+
+		calculateLog2Table ();
+		calculateFracTable ();
+		initCharMap ();
+			
+
+		for (int indexI = 0; level_sizes.length > 0 && indexI < level_sizes[0]; indexI++) {
+			// calculate the left and right ends of the current sliding window
+			long lend = cur_offset + 1;
+			lend = lend < 1 ? 1 : lend;
+			long rend = lend + max_resolution;
+			if (rend > seq_length) {
+				rend = seq_length;
+				lend = rend - max_resolution;
+				lend = lend < 1 ? 1 : lend;
+			}
+			// fill the alignment buffer if necessary
+			if (rend > buffer_right) {
+				// read in a new buffer
+				buffer_left = lend;
+				buffer_right = lend + buffer_size;
+				if (buffer_right > seq_length) {
+					buffer_right = seq_length;
+					buffer_left = buffer_right - buffer_size;
+					buffer_left = buffer_left < 1 ? 1 : buffer_left;
+				}
+				cols = alignment.getRange (g, buffer_left, buffer_right);
+
+				Backbone bb = null;
+				Backbone next_bb = null;
+				if (bb_list != null) {
+					// if we have backbone information then find out whether
+					// we're
+					// inside a backbone segment. if we're not in a bb segment,
+					// find the next segment since querying repeatedly would be
+					// too expensive
+					bb = bb_list.getBackbone (g, buffer_left);
+					if (bb == null)
+						next_bb = bb_list.getNextBackbone (g, buffer_left);
+				}
+				// calculate entropies for this set of alignment columns
+				// assume that newlines and gaps haven't been filtered
+				int [] col_index = new int [cols.length];
+				entropies = new double [cols[g.getSourceIndex ()].length];
+				int ent_count = (int) (buffer_right - buffer_left);
+				for (int entI = 0; entI < ent_count; entI++) {
+					int i;
+					for (i = 0; i < 5; i++)
+						chars[i] = 0;
+
+					// tally up character counts, using backbone info if
+					// available
+					if (bb_list == null) {
+						// store nt frequencies as a, c, g, t, gap
+						for (i = 0; i < cols.length; i++) {
+							chars[char_map[(char) cols[i][col_index[i]]]]++;
+						}
+					} else {
+						// first check whether we're still in range of the
+						// backbone
+						// update the current bb segment as necessary
+						if (bb != null
+								&& buffer_left + entI > bb.getRightEnd (g)) {
+							bb = bb_list.getBackbone (g, buffer_left + entI);
+							if (bb == null)
+								next_bb = bb_list.getNextBackbone (g,
+										buffer_left + entI);
+						} else if (bb == null
+								&& next_bb != null
+								&& next_bb.getLeftEnd (g) <= buffer_left + entI
+								&& buffer_left + entI <= next_bb
+										.getRightEnd (g)) {
+							bb = next_bb;
+							next_bb = null;
+						}
+
+						// use bb to inform...
+						// if we're outside of bb just add this seq
+						// if we're inside bb, add the seqs designated by the bb
+						int sI = g.getSourceIndex ();
+						if (bb == null) {
+							chars[char_map[(char) cols[sI][col_index[sI]]]]++;
+							chars[char_map['-']] += cols.length - 1; // add
+							// gap
+							// for
+							// the
+							// rest
+						} else {
+							boolean seqs[] = bb.getSeqs ();
+							for (i = 0; i < cols.length; i++) {
+								if (seqs[i])
+									chars[char_map[(char) cols[i][col_index[i]]]]++;
+							}
+						}
+					}
+
+					entropies[entI] = 0;
+					// count number of different characters in this column
+					// each gap counts as a different type
+					int char_types = 0;
+					for (i = 0; i < 5; i++)
+						char_types += chars[i];
+
+					// calculate entropy
+					for (i = 0; i < 4; i++) {
+						if (chars[i] == 0)
+							continue;
+						entropies[entI] -= frac ((int) chars[i], char_types)
+								* log_2 ((int) chars[i], char_types);
+					}
+					for (i = 0; i < chars[4]; i++) {
+						entropies[entI] -= frac (1, char_types)
+								* log_2 (1, char_types);
+					}
+					skipGapColumns (g, cols, col_index);
+				}
+
+			}
+
+			// now calculate average entropy for each index value
+			double entropy_sum = 0;
+			int col_left = (int) (lend - buffer_left);
+			int col_right = (int) (rend - buffer_left);
+			for (int colI = col_left; colI < col_right; colI++) {
+				entropy_sum += entropies[colI];
+			}
+			double tmp = (entropy_sum / (double) (col_right - col_left));
+			tmp = 1 - tmp; // make lower entropy values higher
+			tmp -= .5; // convert to a value between -128 and 127
+			tmp *= 255;
+			sim_index[indexI] = (byte) tmp;
+			if (tmp < -127)
+				sim_index[indexI] = -128;
+			cur_offset += max_resolution;
+		}
+		calculateHigherLevels();
+	}
+	
+
+
+	public static final int [] char_map = initCharMap();
+
+	static int[] initCharMap () {
+		int[] char_map = new int [128];
+		char_map['a'] = 0;
+		char_map['A'] = 0;
+		char_map['c'] = 1;
+		char_map['C'] = 1;
+		char_map['g'] = 2;
+		char_map['G'] = 2;
+		char_map['t'] = 3;
+		char_map['T'] = 3;
+		char_map['-'] = 4;
+		char_map['r'] = 0;
+		char_map['R'] = 0;
+		char_map['k'] = 2;
+		char_map['K'] = 2;
+		char_map['s'] = 1;
+		char_map['S'] = 1;
+		char_map['m'] = 0;
+		char_map['M'] = 0;
+		char_map['y'] = 1;
+		char_map['Y'] = 1;
+		char_map['w'] = 0;
+		char_map['W'] = 0;
+		char_map['b'] = 1;
+		char_map['B'] = 1;
+		char_map['v'] = 0;
+		char_map['V'] = 0;
+		char_map['d'] = 0;
+		char_map['D'] = 0;
+		char_map['h'] = 0;
+		char_map['H'] = 0;
+		// May as well treat these as gap characters since they
+		// don't really affect the entropy
+		char_map['n'] = 4;
+		char_map['N'] = 4;
+		char_map['x'] = 4;
+		char_map['X'] = 4;
+		return char_map;
+	}
+
+	double fracs[][] = null;
+
+	public void calculateFracTable () {
+		// calculate the table
+		fracs = new double [127] [127];
+
+		for (double x = 1; x < 128; x++) {
+			for (double y = 1; y < 128; y++) {
+				fracs[(int) x - 1][(int) y - 1] = x / y;
+			}
+		}
+	}
+
+	double frac (int numerator, int denominator) {
+		// if it's not in the matrix do it manually
+		if (numerator < 1 || denominator < 1 || numerator > 127
+				|| denominator > 127)
+			return (double) numerator / (double) denominator;
+
+		// otherwise return the pre-computed value
+		return fracs[numerator - 1][denominator - 1];
+	}
+
+	/**
+	 * Methods to precompute log values of x/y fractions where x and y range
+	 * from 0-127 Intended to save time by replacing logarithm computation with
+	 * a table lookup
+	 */
+	double logs[][] = null;
+
+	public void calculateLog2Table () {
+		// calculate the table
+		logs = new double [127] [127];
+
+		for (double x = 1; x < 128; x++) {
+			for (double y = 1; y < 128; y++) {
+				logs[(int) x - 1][(int) y - 1] = Math.log (x / y)
+						/ Math.log (2d);
+			}
+		}
+	}
+
+	double log_2 (double numerator, double denominator) {
+		return Math.log (numerator / denominator) / Math.log (2d);
+	}
+
+	double log_2 (int numerator, int denominator) {
+		if (denominator == 0) {
+			// return NaN
+			return Double.NaN;
+		}
+		if (numerator == 0) {
+			// return -inf
+			return Double.MIN_VALUE;
+		}
+		// if it's not in the matrix do it manually
+		if (numerator > 127 || denominator > 127)
+			return Math.log ((double) numerator / (double) denominator)
+					/ Math.log (2d);
+
+		// otherwise return the pre-computed value
+		return logs[numerator - 1][denominator - 1];
+	}
+	
+	
+	/**
+	 * Method for writing a range of indexes to an output stream
+	 * @param out
+	 * @param start_ind
+	 * @param end_ind
+	 * @return
+	 */
+	public boolean writeVals (OutputStream out, int start_ind, int end_ind) {
+		try {
+			out.write(sim_index, start_ind, end_ind - start_ind + 1);
+			return true;
+		} catch (IOException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+	
+	/**
+	 * For writing all similarity indeces to an output stream
+	 * 
+	 * @param out
+	 * @return
+	 */
+	public boolean writeAllVals (OutputStream out) {
+		return writeVals (out, 0, sim_index.length - 1);
+	}
+	
+	
+	public boolean equals (Object compare) {
+		return Arrays.equals(sim_index, ((SimilarityIndex) compare).sim_index);
+	}
+
+
+}
diff --git a/src/org/gel/mauve/SupportedFormat.java b/src/org/gel/mauve/SupportedFormat.java
new file mode 100644
index 0000000..738c3e6
--- /dev/null
+++ b/src/org/gel/mauve/SupportedFormat.java
@@ -0,0 +1,90 @@
+package org.gel.mauve;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+
+/**
+ * A definition of a format supported for annotation/feature reading. All
+ * formats must support delegation, that is, the smart caching of features and
+ * such.
+ */
+public interface SupportedFormat {
+	/**
+	 * @param reader
+	 * @return
+	 * 
+	 * Create a sequenceIterator over the set of sequences within the file.
+	 * 
+	 */
+	SequenceIterator readFile (File file);
+
+	/**
+	 * @param s
+	 * @param file
+	 * @throws FileNotFoundException
+	 * 
+	 * Do whatever initial validation of sequence or file that is necessary
+	 * 
+	 */
+	void validate (Sequence s, File file, int index)
+			throws FileNotFoundException;
+
+	/**
+	 * 
+	 * @param file
+	 * @param index
+	 * @return
+	 */
+	Sequence readInnerSequence (File file, int index);
+
+	/**
+	 * 
+	 * @param s
+	 * @param source
+	 * @param index
+	 * @return
+	 * @throws FileNotFoundException
+	 */
+	SequenceIterator makeIterator (File file);
+
+	/**
+	 * @param s
+	 * @param source
+	 * @param index -
+	 *            the ordinal of the sequence that should be delegated.
+	 * @return
+	 * @throws FileNotFoundException
+	 * 
+	 * Create a delegate based on the source file. The sequence s must
+	 * correspond to the contents of the file.
+	 */
+	Sequence makeDelegate (Sequence s, File source, int index)
+			throws FileNotFoundException;
+
+	/**
+	 * @param s
+	 * @return
+	 * 
+	 * Return a human-readable name for the entire sequence.
+	 */
+	String getSequenceName (Sequence s);
+
+	/**
+	 * @param s
+	 * @return
+	 * 
+	 * Return a human-readable name for the subsequence.
+	 */
+	String getChromosomeName (Sequence s);
+
+	/**
+     * True if this parsing files of this format results in biojavax RichSequence objects
+     * @return
+     */
+    boolean isRich();
+
+    FilterCacheSpec[] getFilterCacheSpecs();
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/ViewerMode.java b/src/org/gel/mauve/ViewerMode.java
new file mode 100644
index 0000000..d27d3c8
--- /dev/null
+++ b/src/org/gel/mauve/ViewerMode.java
@@ -0,0 +1,10 @@
+package org.gel.mauve;
+
+public class ViewerMode {
+	public final static ViewerMode NORMAL = new ViewerMode ();
+
+	public final static ViewerMode ZOOM = new ViewerMode ();
+
+	private ViewerMode () {
+	};
+}
diff --git a/src/org/gel/mauve/XMFAAlignment.java b/src/org/gel/mauve/XMFAAlignment.java
new file mode 100644
index 0000000..94b620f
--- /dev/null
+++ b/src/org/gel/mauve/XMFAAlignment.java
@@ -0,0 +1,885 @@
+package org.gel.mauve;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.gel.mauve.analysis.SnpExporter;
+import org.gel.mauve.tree.FileKey;
+import org.gel.mauve.tree.GISTree;
+import org.gel.mauve.tree.GapKey;
+import org.gel.mauve.tree.TreeStore;
+
+/**
+ * XMFA file class A technical cross-product of insomnia and indulgence
+ */
+public class XMFAAlignment implements Serializable {
+	/** Versioning for serializations of this object */
+	static final long serialVersionUID = 5;
+
+	// The file containing alignment data (transient, can't be serialized)
+	protected transient RandomAccessFile xmfa_file;
+
+	// The match class instances store the boundaries of each aligned segment
+	protected Match [] intervals;
+
+	// These store the offset within each file where an interval's sequence
+	protected Match [] file_pos;
+
+	// The number of newline bytes, as detected on the first line of the file
+	protected int newline_size = 0;
+
+	// The number of chars on each line
+	protected int line_width = 80;
+
+	// The number of sequences aligned
+	protected int seq_count = 0;
+
+	// The length of each sequence
+	protected long [] seq_length;
+
+	// A list of LCBs contained in this alignment
+	protected LCB [] lcb_list;
+
+	// A list of LCBs as they appear in the file
+	private LCB [] source_lcb_list;
+
+	// Array of comment lines for each alignment entry in the XMFA
+	protected String [] comments;
+
+	// Array of names for each sequence in the first alignment entry
+	protected String [] names;
+
+	// A set of gist's that map sequence index and column index to file
+	// offset. indexed by [interval][sequence]
+	GISTree [][] gis_tree;
+
+	protected TreeStore ts = new TreeStore ();
+
+	// The size of data chunks to read from disk
+	protected int buffer_size = 500000;
+
+	public Properties metadata = new Properties ();
+
+	/**
+	 * Sets the file used as backing store for this alignment Call this method
+	 * to set the file after reading a serialized XMFAAlignment
+	 * 
+	 * @param f
+	 *            The xmfa file corresponding to this alignment
+	 */
+	public void setFile (RandomAccessFile f) {
+		xmfa_file = f;
+	}
+
+	/**
+	 * Constructor reads an XMFA alignment from an input file and indexes the
+	 * location of alignment bounds
+	 */
+	@SuppressWarnings(/*"deprecation"*/ "unchecked")
+	public XMFAAlignment (RandomAccessFile ir) throws java.io.IOException {
+		xmfa_file = ir;
+		// simple parse of the input
+		Vector seq_nums = new Vector ();
+		Vector lend = new Vector ();
+		Vector rend = new Vector ();
+		Vector reverse = new Vector ();
+		Vector f_offset = new Vector ();
+		Vector f_end_offset = new Vector ();
+		Vector tmp_ivs = new Vector ();
+		Vector tmp_offs = new Vector ();
+		Vector tmp_lcbs = new Vector ();
+		Vector gis_tree_tmp = new Vector ();
+		Vector gist_seqnums = new Vector ();
+		Vector gist_ivnums = new Vector ();
+		Vector tmp_comments = new Vector ();
+		Vector tmp_names = new Vector ();
+		String cur_comment = null;
+
+		/** < true when in the midst of reading gaps */
+		long seq_offset = 0;
+		GISTree gist = null;
+
+		FileKey fk = null;
+		GapKey gk = null;
+
+		// XMFA file parse states
+		final int wait_defline = 0;
+		final int read_defline = 1;
+		final int read_comment = 2;
+		final int read_sequence = 3;
+		final int process_sequence = 4;
+		final int read_metadata = 5;
+
+		byte [] buf = new byte [buffer_size];
+		int bufI = 0;
+		int remaining_bytes = 0;
+		long cur_base = 0;
+		int state = wait_defline;
+		int section_start = buffer_size;
+
+		// detect the number of bytes in a newline
+		xmfa_file.seek (0);
+		String first_line = xmfa_file.readLine ();
+		newline_size = (int) (xmfa_file.getFilePointer () - first_line
+				.length ());
+		xmfa_file.seek (0);
+
+		// begin parse loop
+		while (true) {
+			if (remaining_bytes == 0) {
+				// it's important to preserve the defline read thus far
+				// shift it to the beginning of the array and then read
+				// from that point onward
+				if (state != read_defline && state != read_comment)
+					section_start = buf.length;
+				int copy_len = buf.length - section_start;
+				System.arraycopy (buf, section_start, buf, 0, copy_len);
+
+				if (state == read_defline || state == read_comment)
+					section_start = 0;
+
+				// read more data into the buffer
+				cur_base += bufI;
+				xmfa_file.seek (cur_base);
+				remaining_bytes = xmfa_file.read (buf, copy_len, buf.length
+						- copy_len);
+				cur_base -= copy_len;
+				if (remaining_bytes <= 0)
+					break;
+				bufI = copy_len; // continue wherever we left off
+			}
+
+			switch (state) {
+				case wait_defline:
+					if (buf[bufI] == '#') {
+						// read metadata or a user comment
+						section_start = bufI + 1;
+						state = read_metadata;
+					}
+					if (buf[bufI] == '>') {
+						section_start = bufI;
+						state = read_defline;
+						bufI--;
+						remaining_bytes++;
+					}
+					break;
+				case read_metadata:
+					if (buf[bufI] == '\r' || buf[bufI] == '\n') {
+						String cur_line = new String (buf, section_start, bufI
+								- section_start);
+
+						// is this a sequence count specifier?
+						int sc_index = cur_line.indexOf ("SequenceCount");
+						if (sc_index >= 0) {
+							seq_count = Integer.parseInt (cur_line.substring (
+									sc_index + 13).trim ());
+						} else
+						// Otherwise, add it to the properties collection.
+						{
+							String [] parts = cur_line.split ("\\s", 2);
+
+							if (parts.length == 0) {
+								// Do nothing
+							} else if (parts.length == 1) {
+								metadata.setProperty (parts[0].trim (), "");
+							} else {
+								metadata.setProperty (parts[0].trim (),
+										parts[1].trim ());
+							}
+						}
+						// go back to waiting for a defline
+						state = wait_defline;
+					}
+					break;
+				// when a newline rolls around, parse out the defline
+				case read_defline:
+					if (buf[bufI] == '\r' || buf[bufI] == '\n') {
+						// read the goods into cur_line
+						String cur_line = new String (buf, section_start, bufI
+								- section_start);
+
+						// parse a sequence left-end, right-end, and strand
+						// combination
+						// > number:start-end strand(+/-)
+						int colon_pos = cur_line.indexOf (':');
+						int seq_num = -1;
+						java.util.StringTokenizer seq_num_strtok = new java.util.StringTokenizer (
+								cur_line.substring (1, colon_pos));
+						if (seq_num_strtok.hasMoreTokens ()) {
+							seq_num = Integer.parseInt (seq_num_strtok
+									.nextToken ()) - 1;
+							seq_nums.addElement (new Integer (seq_num));
+						}
+						String tmp_str = cur_line.substring (colon_pos + 1);
+						int dash_pos = tmp_str.indexOf ('-');
+						int space_pos = tmp_str.indexOf (' ');
+						long first = Long.parseLong (tmp_str.substring (0,
+								dash_pos));
+						long second = Long.parseLong (tmp_str.substring (
+								dash_pos + 1, space_pos));
+						long left = first < second ? first : second;
+						long right = first < second ? second : first;
+						boolean cur_reverse = true;
+						// parse strand -- if + isn't found then assume - strand
+						if (tmp_str.indexOf ('+', space_pos + 1) < 0)
+							reverse.addElement (new Boolean (true));
+						else {
+							reverse.addElement (new Boolean (false));
+							cur_reverse = false;
+						}
+						lend.addElement (new Long (left));
+						rend.addElement (new Long (right));
+
+						// if we haven't yet completed the first alignment entry
+						// try to parse the sequence name
+						if (tmp_comments.size () == 0) {
+							int strand_pos = 0;
+							if (!cur_reverse)
+								strand_pos = tmp_str.indexOf ('+',
+										space_pos + 1);
+							else
+								strand_pos = tmp_str.indexOf ('-',
+										space_pos + 1);
+
+							if (tmp_str.length () > strand_pos + 2)
+								// there's a name to parse
+								tmp_names.add (tmp_str
+										.substring (strand_pos + 2));
+							else
+								tmp_names.add (new String (""));
+						}
+
+						// prepare for seq parsing
+						seq_offset = 0;
+						state = read_sequence;
+
+						gist = new GISTree (ts);
+						gis_tree_tmp.addElement (gist);
+						gist_seqnums.addElement (new Integer (seq_num));
+						gist_ivnums.addElement (new Integer (tmp_ivs.size ()));
+					}
+					break;
+
+				// ride out the comment
+				case read_comment:
+					if (buf[bufI] == '\r' || buf[bufI] == '\n') {
+						// store the comment
+						if (cur_comment == null) {
+							cur_comment = new String (buf, section_start, bufI
+									- section_start);
+							tmp_comments.add (cur_comment);
+						}
+						if (buf[bufI] == '\n') {
+							state = wait_defline;
+							cur_comment = null;
+						}
+					}
+					break;
+
+				// wait for a sequence entry to finish, add gist keys, etc.
+				case read_sequence:
+					if (buf[bufI] == '>' || buf[bufI] == '=') {
+						state = process_sequence;
+						remaining_bytes++;
+						bufI--;
+						break;
+					}
+
+					// do nothing on newlines
+					if (buf[bufI] == '\r' || buf[bufI] == '\n') {
+						break;
+					}
+
+					// set the sequence data file offset if this is the
+					// beginning of
+					// the entry
+					if (fk == null && gk == null) {
+						f_offset.addElement (new Long (cur_base + bufI));
+					}
+
+					// it's all sequence data
+					long cur_len = gist.length ();
+					if (buf[bufI] == '-') {
+						if (fk != null) {
+							gist.insert (fk, GISTree.end);
+							if (cur_len + fk.getLength () != gist.length ()) {
+								throw new RuntimeException ("Corrupt GisTree.");
+							}
+							fk = null;
+						}
+						if (gk == null)
+							gk = new GapKey (0);
+						gk.length++;
+					} else {
+						if (gk != null) {
+							gist.insert (gk, GISTree.end);
+							if (cur_len + gk.getLength () != gist.length ()) {
+								throw new RuntimeException ("Corrupt GisTree");
+							}
+							gk = null;
+						}
+						if (fk == null) {
+							fk = new FileKey (seq_offset, cur_base + bufI);
+						}
+						fk.incrementLength ();
+						fk.setFLength (cur_base + bufI - fk.getOffset () + 1);
+					}
+
+					break;
+
+				// process a completed sequence entry
+				case process_sequence:
+
+					// do final processing from a previously parsed sequence
+					long curry_len = gist.length ();
+					if (fk != null) {
+						gist.insert (fk, GISTree.end);
+						if (curry_len + fk.getLength () != gist.length ()) {
+							throw new RuntimeException ("Corrupt GisTree");
+						}
+					}
+					if (gk != null) {
+						gist.insert (gk, GISTree.end);
+						if (curry_len + gk.getLength () != gist.length ()) {
+							throw new RuntimeException ("Corrupt GisTree");
+						}
+					}
+					fk = null;
+					gk = null;
+
+					// sequence ends here
+					f_end_offset.addElement (new Long (cur_base + bufI));
+
+					if (buf[bufI] == '>') {
+						section_start = bufI;
+						state = read_defline;
+						remaining_bytes++;
+						bufI--;
+					} else if (buf[bufI] == '=') {
+						// process a sequence set!
+
+						if (seq_count == 0)
+							seq_count = lend.size ();
+						// create a new Match element
+						Match m = new Match (seq_count);
+						// copy file offsets
+						Match f_off_m = new Match (seq_count);
+						// copy left ends into start
+						int aligned_count = 0;
+						// only set values for sequence numbers that were
+						// actually
+						// part
+						// of this interval
+						for (int seq_numI = 0; seq_numI < seq_nums.size (); seq_numI++) {
+							int seqI = ((Integer) seq_nums.elementAt (seq_numI))
+									.intValue ();
+							m.setStart (seqI,
+									((Long) lend.elementAt (seq_numI))
+											.longValue ());
+							if (m.getStart (seqI) != 0)
+								aligned_count++;
+							m.setLength (seqI, ((Long) rend
+									.elementAt (seq_numI)).longValue ());
+							m.setReverse (seqI, ((Boolean) reverse
+									.elementAt (seq_numI)).booleanValue ());
+							f_off_m.setStart (seqI, ((Long) f_offset
+									.elementAt (seq_numI)).longValue ());
+							f_off_m.setLength (seqI, ((Long) f_end_offset
+									.elementAt (seq_numI)).longValue ());
+						}
+						// if this interval contains alignment of more than one
+						// sequence then call it an LCB
+						// there may be problems with the assumption that an
+						// interval will always contain
+						// some aligned sequence if it has more than one set of
+						// genome coordinates defined,
+						// but let's not worry about that now since mauveAligner
+						// won't have that problem
+						if (aligned_count > 1) {
+							LCB lcb = new LCB (m, tmp_lcbs.size (), seq_count);
+							tmp_lcbs.addElement (lcb);
+						}
+
+						// add to tmp_ivs
+						tmp_ivs.addElement (m);
+						tmp_offs.addElement (f_off_m);
+
+						// clear data structures for the next alignment interval
+						lend = new Vector ();
+						rend = new Vector ();
+						reverse = new Vector ();
+						f_offset = new Vector ();
+						f_end_offset = new Vector ();
+						seq_nums = new Vector ();
+
+						state = read_comment;
+						section_start = bufI;
+						remaining_bytes++;
+						bufI--;
+					}
+					break;
+
+			}
+
+			bufI++;
+			remaining_bytes--;
+		}
+
+		intervals = new Match [tmp_ivs.size ()];
+		intervals = (Match []) tmp_ivs.toArray (intervals);
+		file_pos = new Match [tmp_offs.size ()];
+		file_pos = (Match []) tmp_offs.toArray (file_pos);
+		lcb_list = new LCB [tmp_lcbs.size ()];
+		lcb_list = (LCB []) tmp_lcbs.toArray (lcb_list);
+		setSourceLcbList(new LCB [lcb_list.length]);
+		for(int ll = 0; ll < lcb_list.length; ll++)
+			getSourceLcbList()[ll] = new LCB(lcb_list[ll]);
+		seq_length = new long [seq_count];
+		gis_tree = new GISTree [intervals.length] [seq_count];
+		for (int gistI = 0; gistI < gis_tree_tmp.size (); gistI++) {
+			int ivI = ((Integer) gist_ivnums.elementAt (gistI)).intValue ();
+			int seqI = ((Integer) gist_seqnums.elementAt (gistI)).intValue ();
+			gis_tree[ivI][seqI] = (GISTree) gis_tree_tmp.elementAt (gistI);
+		}
+
+		for (int seqI = 0; seqI < seq_count; seqI++) {
+			long seq_len = 0;
+			int treeI = 0;
+			for (int ivI = 0; ivI < intervals.length; ivI++) {
+				if (intervals[ivI].getLength (seqI) > seq_len) {
+					seq_len = intervals[ivI].getLength (seqI);
+				}
+				if (gis_tree[ivI][seqI] == null) {
+					gis_tree[ivI][seqI] = new GISTree (ts);
+					// insert a GapKey of the full length of this interval
+					long iv_length = 0;
+					for (int seqJ = 0; seqJ < seq_count; seqJ++) {
+						if (gis_tree[ivI][seqJ] != null
+								&& iv_length < gis_tree[ivI][seqJ].length ()) {
+							iv_length = gis_tree[ivI][seqJ].length ();
+						}
+					}
+					gis_tree[ivI][seqI].insert (new GapKey (iv_length), 0);
+				}
+				treeI++;
+			}
+			seq_length[seqI] = seq_len;
+		}
+
+		names = new String [tmp_names.size ()];
+		names = (String []) tmp_names.toArray (names);
+
+		comments = new String [tmp_comments.size ()];
+		comments = (String []) tmp_comments.toArray (comments);
+
+		// Prune the arrays.
+		ts.pruneArrays ();
+	}
+
+	/**
+	 * Read a comment line from an LCB The LCB comment line is the terminator
+	 * line that begins with =
+	 * 
+	 * @param lcbI
+	 *            The LCB to read a comment line from
+	 * @return The comment line
+	 */
+	public String getComment (int lcbI) {
+		return comments[lcbI];
+	}
+
+	/**
+	 * Read the source file name of a given sequence
+	 * 
+	 * @param seqI
+	 *            The sequence index (starting at 0)
+	 * @return The source file name
+	 */
+	public String getName (int seqI) {
+		return names[seqI];
+	}
+
+	/**
+	 * Extracts columns from the sequence alignment containing the specified
+	 * range of the specified sequence. The returned alignment columns will
+	 * contain gaps and any whitespace in the XMFA source (e.g. newlines)
+	 * 
+	 * 
+	 * @param g
+	 * 			the genome whose sequence is of interest
+	 * @param lend
+	 *            The left end coordinate of the range to be extracted
+	 * @param rend
+	 *            The right end coordinate of the range to be extracted
+	 * @return A set of alignment columns stored as an array of byte arrays
+	 *         indexed as [sequence][column]
+	 *         
+	 */
+	public byte[][] getRange (Genome g, long lend, long rend) {
+		long cur_offset = lend;
+		byte[][] cols = new byte [seq_count][];
+		int seqJ = 0;
+		for (seqJ = 0; seqJ < seq_count; seqJ++)
+			cols[seqJ] = new byte [0];
+
+		while (cur_offset <= rend) {
+			// determine which LCB we start in
+			int ivI = getLCB (g, cur_offset);
+
+			// determine the file offset of the left end of this sequence
+			// read a bunch o' columns
+			long cur_iv_lend = intervals[ivI].getStart (g);
+			long read_size = intervals[ivI].getLength (g) - rend > 0 ? rend
+					- cur_offset + 1 : intervals[ivI].getLength (g)
+					- cur_offset + 1;
+			while (read_size > 0) {
+				// assume forward orientation
+				// plan b: get the range of columns that need to be read
+				// for each seq, read the cols directly into a byte buffer
+				// reverse the sequences if necessary
+				long lcb_offset = cur_offset - cur_iv_lend;
+				long lcb_right_offset = intervals[ivI].getReverse (g) ? intervals[ivI]
+						.getLength (g)
+						- intervals[ivI].getStart (g) - lcb_offset + 1
+						: lcb_offset + read_size;
+				lcb_offset = intervals[ivI].getReverse (g) ? lcb_right_offset
+						- read_size : lcb_offset;
+
+				long fk_left_col = gis_tree[ivI][g.getSourceIndex ()]
+						.seqPosToColumn (lcb_offset);
+				long fk_right_col = gis_tree[ivI][g.getSourceIndex ()]
+						.seqPosToColumn (lcb_right_offset);
+
+				byte[][] byte_bufs = new byte [seq_count][];
+				for (seqJ = 0; seqJ < seq_count; seqJ++) {
+					byte_bufs[seqJ] = readRawSequence (ivI, seqJ, fk_left_col,
+							fk_right_col - fk_left_col);
+				}
+
+				// just assume that the correct number of columns was read
+				cur_offset += read_size;
+				read_size = 0;
+
+				// reverse the columns if necessary
+				if (intervals[ivI].getReverse (g)) {
+					for (seqJ = 0; seqJ < seq_count; seqJ++) {
+						reverse (byte_bufs[seqJ]);
+					}
+				}
+				// append the columns to cols
+				for (seqJ = 0; seqJ < seq_count; seqJ++) {
+					byte [] tmp_buf = new byte [cols[seqJ].length + byte_bufs[seqJ].length];
+					System.arraycopy (cols[seqJ], 0, tmp_buf, 0, cols[seqJ].length);
+					System.arraycopy (byte_bufs[seqJ], 0, tmp_buf, cols[seqJ].length, byte_bufs[seqJ].length);
+					cols[seqJ] = tmp_buf;
+				}
+			}
+		}
+		return cols;
+	}
+
+	/**
+	 * reverse entries in a byte array
+	 * 
+	 */
+	void reverse (byte [] byte_buf) {
+		for (int byteI = 0; byteI < byte_buf.length / 2; byteI++) {
+			byte tmp = (byte) SnpExporter.revtab[byte_buf[byteI]];
+			byte_buf[byteI] = (byte) SnpExporter.revtab[ byte_buf[byte_buf.length - byteI - 1] ];
+			byte_buf[byte_buf.length - byteI - 1] = tmp;			
+		}
+		if(byte_buf.length%2==1){
+			byte_buf[byte_buf.length/2] = (byte) SnpExporter.revtab[ byte_buf[byte_buf.length/2] ];
+		}
+	}
+
+	/**
+	 * filter gaps from one sequence while maintaining the columns of the
+	 * alignment return the number of columns remaining after filtering
+	 */
+	int filterGapsInOneSequence (int seqI, Object [] byte_bufs) {
+		// filter gaps from a particular sequence while maintaining column
+		// integrity
+		int col_offset = 0;
+		for (int colI = 0; colI < ((byte []) byte_bufs[seqI]).length; colI++) {
+			if (((byte []) byte_bufs[seqI])[colI] != '-') {
+				// copy the column to the current col_offset
+				for (int seqJ = 0; seqJ < seq_count; seqJ++) {
+					((byte []) byte_bufs[seqJ])[col_offset] = ((byte []) byte_bufs[seqJ])[colI];
+				}
+				col_offset++;
+			}
+		}
+		return col_offset;
+	}
+
+	/**
+	 * Read sequence data (and any gap characters) from a file, filtering
+	 * newlines column index starts at 0!!
+	 * 
+	 * @param ivI
+	 *            The interval to read
+	 * @param seqI
+	 *            The sequence to read
+	 * @param left_col
+	 *            Left column index (interval local coordinates)
+	 * @param length
+	 *            Length to read in columns (includes gaps)
+	 */
+	public byte [] readRawSequence (int ivI, int seqI, long left_col, long length) {
+		// check boundary condition
+		if (gis_tree[ivI][seqI].length () == 0) {
+			if (length == 0)
+				return new byte [0];
+			else
+				throw new ArrayIndexOutOfBoundsException ();
+		}
+
+		int l_iter = gis_tree[ivI][seqI].find (left_col);
+		int r_iter = gis_tree[ivI][seqI].find (left_col + length - 1);
+		FileKey l_fk, r_fk;
+		long l_gaps = 0;
+
+		// if the requested region contains nothing but gap then just return a
+		// buffer of gaps
+		long seq_off = gis_tree[ivI][seqI].getSequenceStart (l_iter);
+		if (gis_tree[ivI][seqI].sequenceLength () <= left_col
+				&& gis_tree[ivI][seqI].getSeqLength (l_iter) == 0
+				&& seq_off <= left_col) {
+			if (left_col + length - 1 > gis_tree[ivI][seqI].length ())
+				throw new ArrayIndexOutOfBoundsException ();
+			byte [] bb = new byte [(int) length];
+			for (int bbI = 0; bbI < bb.length; bbI++)
+				bb[bbI] = (byte) '-';
+			return bb;
+		}
+
+		// check for the case where the requested region lies within
+		// the same gap in the middle of the sequence
+		if (gis_tree[ivI][seqI].getKey (l_iter) instanceof GapKey
+				&& gis_tree[ivI][seqI].getKey (r_iter) instanceof GapKey
+				&& gis_tree[ivI][seqI].getKey (l_iter) == gis_tree[ivI][seqI]
+						.getKey (r_iter)) {
+			byte [] bb = new byte [(int) length];
+			for (int bbI = 0; bbI < bb.length; bbI++)
+				bb[bbI] = (byte) '-';
+			return bb;
+		}
+
+		if (gis_tree[ivI][seqI].getKey (l_iter) instanceof FileKey) {
+			l_fk = (FileKey) gis_tree[ivI][seqI].getKey (l_iter);
+		} else {
+			// if we started in a gap the next one should be a FileKey
+			seq_off = gis_tree[ivI][seqI].getSequenceStart (l_iter);
+			l_iter = gis_tree[ivI][seqI].find_seqindex (seq_off);
+			l_fk = (FileKey) gis_tree[ivI][seqI].getKey (l_iter);
+		}
+
+		if (gis_tree[ivI][seqI].getKey (r_iter) instanceof FileKey) {
+			r_fk = (FileKey) gis_tree[ivI][seqI].getKey (r_iter);
+		} else {
+			// if we started in a gap the next one should be a FileKey
+			seq_off = gis_tree[ivI][seqI].getSequenceStart (r_iter) - 1;
+			r_iter = gis_tree[ivI][seqI].find_seqindex (seq_off);
+			r_fk = (FileKey) gis_tree[ivI][seqI].getKey (r_iter);
+		}
+
+		// should have l_fk and r_fk now.
+		// get the file offsets to read from these
+		long l_off = left_col - gis_tree[ivI][seqI].getStart (l_iter);
+		// calculate the number of newlines in the space between the first
+		// desired
+		// column and the first character in this key...
+		long key_col_pos = gis_tree[ivI][seqI].getStart (l_iter) % line_width;
+		long l_newlines = ((key_col_pos + l_off) / 80) * newline_size;
+		if (l_off < 0) {
+			// this happens when the desired column is a gap. just skip ahead
+			l_gaps = -l_off; // track how many gaps we'll need later
+			l_newlines = 0;
+			l_off = 0;
+		}
+		l_off += l_fk.getOffset () + l_newlines;
+
+		long r_off = left_col + length - 1
+				- gis_tree[ivI][seqI].getStart (r_iter);
+		long r_key_col = gis_tree[ivI][seqI].getStart (r_iter) % line_width;
+		long r_newlines = ((r_key_col + r_off) / line_width) * newline_size;
+		r_off += r_fk.getOffset () + r_newlines;
+
+		// now that the exact file offsets of the desired sequence have been
+		// calculated, read it from the XMFA.
+		if (r_off - l_off + 1 + l_gaps < 0) {
+			throw new RuntimeException ("Unexpected Error.");
+		}
+		byte [] byte_buf = new byte [(int) (r_off - l_off + 1 + l_gaps)];
+		for (int l_gapI = 0; l_gapI < l_gaps; l_gapI++) {
+			byte_buf[l_gapI] = (byte) '-';
+		}
+
+		try {
+			xmfa_file.seek (l_off);
+			xmfa_file.read (byte_buf, (int) l_gaps, byte_buf.length
+					- (int) l_gaps);
+		} catch (IOException e) {
+			throw new RuntimeException ("Unexpected file reading error.", e);
+		}
+		return byte_buf;
+	}
+
+	static public byte [] filterNewlines (byte [] byte_buf) {
+		// filter the newlines
+		int byte_off = 0;
+		for (int byteI = 0; byteI < byte_buf.length; byteI++) {
+			if (byte_buf[byteI] == '\r' || byte_buf[byteI] == '\n')
+				continue;
+			byte_buf[byte_off] = byte_buf[byteI];
+			byte_off++;
+		}
+		byte [] bb2 = new byte [byte_off];
+		System.arraycopy (byte_buf, 0, bb2, 0, byte_off);
+		return bb2;
+	}
+
+	/**
+	 * return the LCB index that contains the given position of the given
+	 * sequence
+	 */
+	int getLCB (Genome g, long position) {
+		int ivI = 0;
+		for (; ivI < intervals.length; ivI++) {
+			if (intervals[ivI].getStart (g) <= position
+					&& position <= intervals[ivI].getLength (g))
+				break; // found the starting interval -- remember lengths array
+			// contains r_end
+		}
+		// throw an exception if the requested range couldn't be found
+		if (ivI == intervals.length)
+			throw new ArrayIndexOutOfBoundsException ("genome " + g
+					+ " position " + position);
+		return ivI;
+	}
+
+	// FIXME: get rev. comp right in these two functions!
+	long revCompify (long position, Genome g, int ivI) {
+		try {
+			return intervals[ivI].getReverse (g) ? intervals[ivI].getLength (g)
+					- intervals[ivI].getStart (g) - position : position;
+		} catch (Exception e) {
+			e.printStackTrace ();
+			return -1;
+		}
+	}
+	
+	/**
+	 * converts a global sequence coordinate to an LCB local coordinate, taking
+	 * rev. comp. into account
+	 */
+	long globalToLCB (long position, Genome g, int ivI) {
+		long offset = position - intervals[ivI].getStart (g);
+		return revCompify (offset, g, ivI);
+	}
+
+	// 
+	
+	/**
+	 *  converts an LCB local coordinate to a global sequence coordinate, taking
+	 *  rev. comp. into account
+	 */
+	long LCBToGlobal (long position, Genome g, int ivI) {
+		long offset = revCompify (position, g, ivI);
+		offset += intervals[ivI].getStart (g);
+		if (intervals[ivI].getStart (g) == 0)
+			return 0;
+		return offset;
+	}
+
+	/**
+	 * Identifies the LCB and the column within the LCB of a given sequence
+	 * position
+	 * 
+	 * @return an array of 2 longs. The first being the LCB by index,
+	 *         and the second the column 
+	 */
+	public long [] getLCBAndColumn (Genome g, long position) {
+		// determine the LCB
+		int ivI = getLCB (g, position);
+
+		long lcb_offset = globalToLCB (position, g, ivI);
+		long fk_left_col = gis_tree[ivI][g.getSourceIndex ()]
+				.seqPosToColumn (lcb_offset);
+
+		long [] lcb_and_col = new long [2];
+		lcb_and_col[0] = ivI;
+		lcb_and_col[1] = fk_left_col;
+		return lcb_and_col;
+	}
+
+	/**
+	 * Returns the sequence coordinates aligned in a given column, ordered
+	 * according to source index.
+	 * 
+	 * @param seq_offsets
+	 *            The sequence coordinates (output)
+	 * @param gap
+	 *            True whenever a given sequence has a gap in the query column
+	 */
+	public void getColumnCoordinates (XmfaViewerModel model, int lcb,
+			long column, long [] seq_offsets, boolean [] gap) {
+		for (int seqI = 0; seqI < seq_count; seqI++) {
+			Genome g = model.getGenomeBySourceIndex (seqI);
+
+			seq_offsets[seqI] = gis_tree[lcb][g.getSourceIndex ()]
+					.columnToSeqPos (column);
+			gap[seqI] = column != gis_tree[lcb][g.getSourceIndex ()]
+					.seqPosToColumn (seq_offsets[seqI]);
+			seq_offsets[seqI] = LCBToGlobal (seq_offsets[seqI], g, lcb);
+			if(seq_offsets[seqI]>g.getLength())
+				gap[seqI]=true;
+		}
+	}
+	/**
+	 * Returns a sequence coordinate aligned in a given column for a specific genome
+	 * 
+	 * @return 	The sequence coordinate
+	 */
+	public long getCoordinate (XmfaViewerModel model, Genome g, int lcb,
+			long column, Boolean gap) {
+
+		long seq_offset = gis_tree[lcb][g.getSourceIndex ()]
+				.columnToSeqPos (column);
+		gap = column != gis_tree[lcb][g.getSourceIndex ()]
+				.seqPosToColumn (seq_offset);
+		seq_offset = LCBToGlobal (seq_offset, g, lcb);
+		return seq_offset;
+	}
+	
+	/**
+	 * Returns the length in alignment columns of a particular LCB
+	 * @param lcbId	The index of the LCB in question
+	 * @return	a long int with the length
+	 */
+	public long getLcbLength(int lcbId)
+	{
+		return gis_tree[lcbId][0].length();
+	}
+
+	/**
+	 * Reorder the sequences to conform to the order given in new_order[]
+	 */
+	public void setReference (Genome g) {
+		for (int lcbI = 0; lcbI < lcb_list.length; lcbI++) {
+			lcb_list[lcbI].setReference (g);
+		}
+	}
+
+	public void setSourceLcbList(LCB [] source_lcb_list) {
+		this.source_lcb_list = source_lcb_list;
+	}
+
+	public LCB [] getSourceLcbList() {
+		return source_lcb_list;
+	}
+}
diff --git a/src/org/gel/mauve/XmfaViewerModel.java b/src/org/gel/mauve/XmfaViewerModel.java
new file mode 100644
index 0000000..9995aa5
--- /dev/null
+++ b/src/org/gel/mauve/XmfaViewerModel.java
@@ -0,0 +1,561 @@
+package org.gel.mauve;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.RandomAccessFile;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.prefs.BackingStoreException;
+
+import org.biojava.bio.seq.DNATools;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.symbol.SymbolList;
+import org.gel.mauve.analysis.PermutationExporter;
+import org.gel.mauve.backbone.BackboneList;
+import org.gel.mauve.backbone.BackboneListBuilder;
+import org.gel.mauve.color.BackboneLcbColor;
+import org.gel.mauve.color.LCBColorScheme;
+import org.gel.mauve.histogram.HistogramBuilder;
+import org.gel.mauve.remote.MauveDisplayCommunicator;
+import org.gel.mauve.remote.WargDisplayCommunicator;
+
+/**
+ * @author pinfield
+ * 
+ * A viewer model backed by an XMFA file. Models a global gapped sequence
+ * alignment in an XMFA format file
+ */
+public class XmfaViewerModel extends LcbViewerModel {
+	private XMFAAlignment xmfa;
+	// Sequence similarity profiles calculated over the length of each sequence
+	// represented in an XMFA file
+	private SimilarityIndex [] sim;
+	private long [] highlights;
+	private BackboneList bb_list;
+
+	public void setSequenceCount (int sequenceCount) {
+		super.setSequenceCount (sequenceCount);
+		sim = new SimilarityIndex [sequenceCount];
+	}
+
+	public XmfaViewerModel (File src, ModelProgressListener listener)
+			throws IOException {
+		super (src);
+		super.setDrawLcbBounds(false);
+		init (listener, false);
+	}
+
+    /**
+     * @param listener
+     * @throws FileNotFoundException
+     * @throws IOException
+     */
+    private void init(ModelProgressListener listener, boolean isReloading) throws FileNotFoundException, IOException
+    {
+        // attempt to load data such as SimilarityIndexes from a
+    	// disk-based cache file
+        // Find cached directory, if it exists.
+    	URL xmfa_url = new URL("file://" + getSrc());
+        File dir = null;
+        if(ModelBuilder.getUseDiskCache())
+        {
+	        try
+	        {
+	            dir = ModelBuilder.getCachedDirectory(xmfa_url);
+	        }
+	        catch (BackingStoreException e)
+	        {
+	            System.err.println("Error reading preferences.  Error follows.  Will load from server.");
+	            e.printStackTrace();
+	        }
+        }
+        if( ModelBuilder.getUseDiskCache() && dir == null ){
+	        // Create a temporary directory.
+	        dir = File.createTempFile("mauve", "dir");
+	        dir.delete();
+	        dir.mkdir();
+	        if (!dir.exists() || !dir.isDirectory())
+	        {
+	            throw new IOException("Couldn't create temporary directory.");
+	        }
+	        ModelBuilder.saveCachedDirectory(xmfa_url, dir);
+        }
+        
+        if (listener != null)
+        {
+            listener.alignmentStart();
+        }
+
+        // check whether XMFA has changed since the
+        // cache was created!!
+        // open object I/O for caching
+        File cache_file = null;
+        ObjectInputStream cache_instream = null;
+        if(ModelBuilder.getUseDiskCache())
+        {
+            cache_file = new File(dir, "mauve.cache");
+	        if(cache_file.exists() && cache_file.canRead() &&
+	        		getSrc().lastModified() < cache_file.lastModified())
+	        {
+	        	cache_instream = new ObjectInputStream(new java.io.FileInputStream(cache_file));
+	        }
+        }
+
+        RandomAccessFile inputFile = new RandomAccessFile(getSrc(), "r");
+        
+        // read XMFA from object cache if possible
+        if(cache_instream != null){
+            try{
+            	xmfa = (XMFAAlignment)cache_instream.readObject();
+            	xmfa.setFile(inputFile);
+            }catch(ClassNotFoundException cnfe){
+            	// cache must be corrupt
+            	cache_instream = null;
+            }catch(ClassCastException cce){
+            	// cache must be corrupt
+            	cache_instream = null;
+            }catch(InvalidClassException ice){
+            	cache_instream = null;
+            }
+        }
+        // it didn't get read from the cache
+        if(cache_instream == null)
+        {
+        	// try to read the alignment file itself
+        	try{
+        		xmfa = new XMFAAlignment(inputFile);
+        	}catch(Exception e){}
+        }
+        // If no sequences are found, this is certainly an invalid file.
+        if (xmfa==null || xmfa.seq_count == 0)
+        {
+            throw new IOException("Not an XMFA file.  Please check that the" +
+            		" input file is a properly formatted alignment.");
+        }
+        
+        if (listener != null)
+        {
+            listener.alignmentEnd(xmfa.seq_count);
+        }
+
+        if (!isReloading)
+        {
+            setSequenceCount(xmfa.seq_count);
+        }
+
+        // now build genomes
+        for (int seqI = 0; seqI < xmfa.seq_count; seqI++)
+        {
+            if (listener != null)
+            {
+                listener.featureStart(seqI);
+            }
+
+            Genome g = null;
+            if (!isReloading)
+            {
+                g = GenomeBuilder.buildGenome(seqI, this);
+                setGenome(seqI, g);
+            }
+            else
+            {
+                // If reloading, reorder the genomes to the same order as
+                // in the file.  reload() will take care of the reordering.
+            	g = getGenomeBySourceIndex(seqI);
+            }
+        }
+        
+        // now try to read a backbone list
+        try{
+        	bb_list = BackboneListBuilder.build(this,xmfa);
+        	if( bb_list != null )
+        	{
+            	// if the backbone is newer than the cache then clear the cache
+	        	File bb_file = BackboneListBuilder.getFileByKey(this,xmfa,"BackboneFile");
+	            if(	ModelBuilder.getUseDiskCache() && bb_file.lastModified() > cache_file.lastModified())
+	            	cache_instream = null;
+        	}
+        }catch(IOException ioe)
+        {
+        	bb_list = null;
+        }
+
+        // now compute SimilarityIndex
+        for (int seqI = 0; seqI < xmfa.seq_count; seqI++)
+        {
+            Genome g = getGenomeBySourceIndex(seqI);
+            // read the SimilarityIndex from object cache if possible
+            if(cache_instream != null){
+                try{
+                	sim[seqI] = (SimilarityIndex)cache_instream.readObject();
+                }catch(ClassNotFoundException cnfe){
+                	// cache must be corrupt
+                	cache_instream = null;
+                }catch(ClassCastException cce){
+                	// cache must be corrupt
+                	cache_instream = null;
+                }catch(InvalidClassException ice){
+                	cache_instream = null;
+                }
+            }
+            // it didn't get read from the cache
+            if(cache_instream == null)
+            	sim[seqI] = new SimilarityIndex(g, xmfa, bb_list);
+        }
+
+        // if cache_instream is null there must have been a problem
+        // reading the cache.  write out all objects that should be cached
+        if(cache_instream == null && ModelBuilder.getUseDiskCache()){
+        	ObjectOutputStream cache_outstream = null;
+        	cache_outstream = new ObjectOutputStream(new FileOutputStream(cache_file));
+        	cache_outstream.writeObject(xmfa);
+        	for( int seqI = 0; seqI < xmfa.seq_count; seqI++ )
+            	cache_outstream.writeObject(sim[seqI]);
+        }
+        
+        // copy the LCB list
+        setFullLcbList(new LCB[xmfa.lcb_list.length]);
+        System.arraycopy(xmfa.lcb_list, 0, getFullLcbList(), 0, xmfa.lcb_list.length);
+
+        setDelLcbList(new LCB[0]);
+        setLcbCount(getFullLcbList().length);
+        
+        highlights = new long[getSequenceCount()];
+        Arrays.fill(highlights, Match.NO_MATCH);
+
+        // set LCB colors first
+    	setColorScheme(new LCBColorScheme());
+        if( bb_list != null )
+        	setColorScheme(new BackboneLcbColor());
+        initModelLCBs();
+        
+        // check if there's a histogram
+        File histFile = BackboneListBuilder.getFileByKey(this, xmfa, "HistogramFile");
+        if(histFile != null){
+	        RandomAccessFile raf = new RandomAccessFile(histFile, "r");
+	        HistogramBuilder.build(raf, this);
+        }
+        
+        // Now that we've initialized everything, we can make split LCBs
+        LCB[] splitLCBs = PermutationExporter.getSplitLCBs(this); 
+        setSplitLcbList(splitLCBs);
+      //  LCB[] testSplitLCBs = PermutationExporter.splitLcbList(this, splitLCBs, genomes);
+      //  System.err.println("");
+        // try publishing this viewer model via DBus
+    }
+
+    MauveDisplayCommunicator mdCommunicator = null;
+    WargDisplayCommunicator wdCommunicator = null;
+    /*
+     * Attempts to open two-way DBus communication with the weakarg app for this viewer model
+     * Warning, when this fails, it currently fails silently!!
+     */
+    public void initDbusCommunication(){
+        try{
+    	try{
+        try{
+	       	mdCommunicator = new MauveDisplayCommunicator(this);
+        }catch(UnsatisfiedLinkError ule){}
+        }catch(NoClassDefFoundError ncdfe){}
+        }catch(Exception e){}
+        // try connecting to a warg instance
+        try{
+    	try{
+        try{
+        	wdCommunicator = new WargDisplayCommunicator(this);
+        	
+        }catch(UnsatisfiedLinkError ule){}
+        }catch(NoClassDefFoundError ncdfe){}
+        }catch(Exception e){ }
+    }
+
+	protected void referenceUpdated () {
+		super.referenceUpdated ();
+		xmfa.setReference (getReference ());
+	}
+
+	/**
+     * 
+     * @return
+     */
+    public XMFAAlignment getXmfa()
+    {
+        return xmfa;
+    }
+
+	// NEWTODO: Sort sims on sourceIndex instead!
+	public SimilarityIndex getSim (Genome g) {
+		return sim[g.getSourceIndex ()];
+	}
+
+	/**
+	 * Identifies the LCB and the column within the LCB of a given sequence
+	 * position
+	 * 
+	 * @return an array of 2 longs. The first being the LCB by index,
+	 *         and the second the column 
+	 */
+	public long [] getLCBAndColumn (Genome g, long position) {
+		return xmfa.getLCBAndColumn (g, position);
+	}
+	
+	public long[] getLCBAndColumn(int genSrcIdx, long position){
+		return this.getLCBAndColumn(genomes[genSrcIdx], position);
+	}
+
+	public int getLCBIndex (Genome g, long position) {
+		return xmfa.getLCB (g, position);
+	}
+	
+	/**
+	 * Extracts columns from the sequence alignment containing the specified
+	 * range of the specified sequence. 
+	 * 
+	 * 
+	 * @param g
+	 * 			the genome whose sequence is of interest
+	 * @param lend
+	 *            The left end coordinate of the range to be extracted
+	 * @param rend
+	 *            The right end coordinate of the range to be extracted
+	 * @return A set of alignment columns stored as an array of byte arrays
+	 *         indexed as [sequence][column]
+	 *         
+	 */
+	public byte[][] getSequenceRange(Genome g, long left, long right){
+		byte[][] tmp = xmfa.getRange(g, left, right);
+		for (int i = 0; i < tmp.length; i++){
+			tmp[i] = XMFAAlignment.filterNewlines(tmp[i]);
+		}
+		return tmp;
+	}
+	
+	/**
+	 * Extracts columns from the sequence alignment containing the specified
+	 * range of the specified sequence. 
+	 * 
+	 * 
+	 * @param genSrcIdx
+	 * 			the source index of the genome whose sequence is of interest
+	 * @param lend
+	 *            The left end coordinate of the range to be extracted
+	 * @param rend
+	 *            The right end coordinate of the range to be extracted
+	 * @return A set of alignment columns stored as an array of byte arrays
+	 *         indexed as [sequence][column]
+	 * 
+	 *     
+	 */
+	/* FIXME */
+	public byte[][] getSequenceRange(int genSrcIdx, long left, long right){
+		return this.getSequenceRange(genomes[genSrcIdx], left, right);
+	}
+
+	/**
+	 * The backbone list or null if none exists
+	 * 
+	 * @return The backbone list or null if none exists
+	 */
+	public BackboneList getBackboneList () {
+		return bb_list;
+	}
+
+	/**
+	 * 
+	 * Returns column coordinates in source genome order
+	 * 
+	 * @param lcb LCB id
+	 * @param column column of interest
+	 * @param seq_coords  The sequence coordinates (output)
+	 * @param gap True whenever a given sequence has a gap in the query column
+	 * @return
+	 */
+	public void getColumnCoordinates (int lcb, long column, long [] seq_coords, boolean [] gap) {
+		xmfa.getColumnCoordinates (this, lcb, column, seq_coords, gap);
+	}
+	
+	/**
+	 * Returns the position in genome <code>genY</code> that is homologous
+	 * to position <code>pos</code> in genome <code>genX</code>.
+	 * 
+	 * 
+	 * @param genX input genome
+	 * @param pos input position
+	 * @param genY query genome
+	 * @return 0 if no positions in genome <code>genY</code> align to 
+	 * 		   <code>pos</code> in <code>genX</code>, else the homologous
+	 *         position in genome <code>genY</code>
+	 */
+	public long getHomologousCoordinate(int genX, long pos, int genY){
+		long[] lcb = getLCBAndColumn(genomes[genX], pos);
+		long[] seq_coords = new long[genomes.length];
+		boolean[] gap = new boolean[genomes.length];
+		getColumnCoordinates((int) lcb[0], lcb[1], seq_coords, gap);
+		if (!gap[genY])
+			return seq_coords[genY];
+		else
+			return 0;
+	}
+	
+	/**
+	 * Returns the sequence between <code>start</code> and <code>end</code>, inclusive,
+	 * in the specified genome.
+	 * <br>
+	 * If <code>start</code> > <code>end</code>, the reverse complement is returned. 
+	 * </br>
+	 * @param start start of the sequence to extract
+	 * @param end end of the sequence to extract
+	 * @param genSrcIdx the genome
+	 * @return a <code>char</code> array representation of the sequence
+	 * 
+	 * @author atritt
+	 */
+	public char[] getSequence(long start, long end, int genSrcIdx){
+		Sequence annSeq = genomes[genSrcIdx].getAnnotationSequence();
+		if (annSeq == null){
+			return null;
+		} else {
+			try {
+				if (start > end){
+					return DNATools.reverseComplement(annSeq.subList((int)end, (int)start)).seqString().toCharArray();
+				} else {
+					return annSeq.subList((int) start, (int)end).seqString().toCharArray();
+				}
+			} catch (Exception e){
+				System.err.println("Error getting sequence coordinates (" 
+						+start+", "+end+ ") for " + genomes[genSrcIdx].getDisplayName());
+				System.err.println("Sequence : " + annSeq.length());
+				e.printStackTrace();
+			}
+		}
+		return null;
+	}
+
+	public void updateHighlight (Genome g, long coordinate) {
+		highlights = null;
+		// Wait til end to call super, since it fires event.
+		super.updateHighlight (g, coordinate);
+	}
+
+	public long getHighlight (Genome g) {
+		if (highlights == null) {
+			long [] iv_col = getLCBAndColumn (getHighlightGenome (),
+					getHighlightCoordinate ());
+			boolean [] gap = new boolean [this.getSequenceCount ()];
+			;
+			highlights = new long [this.getSequenceCount ()];
+			getColumnCoordinates ((int) iv_col[0], iv_col[1], highlights, gap);
+			for (int i = 0; i < highlights.length; ++i) {
+				highlights[i] = Math.abs (highlights[i]);
+				if (gap[i])
+					highlights[i] *= -1;
+			}
+		}
+		return highlights[g.getSourceIndex ()];
+	}
+
+	/**
+	 * aligns the display to a particular position of a particular sequence.
+	 * typically called by RRSequencePanel when the user clicks a part of the
+	 * sequence. Used for display mode 3
+	 */
+	public void alignView (Genome g, long position) {
+		long [] iv_col;
+		try {
+			iv_col = getLCBAndColumn (g, position);
+		} catch (ArrayIndexOutOfBoundsException e) {
+			// User clicked outside of bounds of sequence, so do nothing.
+			return;
+		}
+		long [] coords = new long [this.getSequenceCount ()];
+		;
+		boolean [] gap = new boolean [this.getSequenceCount ()];
+		;
+		getColumnCoordinates ((int) iv_col[0], iv_col[1], coords, gap);
+		alignView (coords, g);
+	}
+
+	/**
+	 * Overrides setFocus() in BaseViewerModel to also align the display
+	 * appropriately
+	 */
+	public void setFocus (String sequenceID, long start, long end, String contig) {
+		super.setFocus (sequenceID, start, end, contig);
+		Genome g = null;
+		for (int i = 0; i < genomes.length; i++) {
+			if (sequenceID.equals (genomes[i].getID ())) {
+				g = genomes[i];
+				break;
+			}
+		}
+		if (g == null) {
+			System.err
+					.println ("Received focus request for nonexistent sequence id "
+							+ sequenceID);
+			return;
+		}
+		if (contig != null)
+			start = contig_handler.getPseudoCoord(g.getSourceIndex(), start, contig);
+		alignView (g, start);
+	}
+
+	public void reload () {
+		fireReloadStartEvent ();
+
+		try {
+			init (null, true);
+		} catch (FileNotFoundException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace ();
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace ();
+		}
+
+		fireReloadEndEvent ();
+	}
+
+	protected void fireReloadEndEvent () {
+		// Guaranteed to return a non-null array
+		Object [] listeners = listenerList.getListenerList ();
+		// Process the listeners last to first, notifying
+		// those that are interested in this event
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1]).modelReloadEnd (modelEvent);
+			}
+		}
+	}
+
+	protected void fireReloadStartEvent () {
+		// Guaranteed to return a non-null array
+		Object [] listeners = listenerList.getListenerList ();
+		// Process the listeners last to first, notifying
+		// those that are interested in this event
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ModelListener.class) {
+				((ModelListener) listeners[i + 1])
+						.modelReloadStart (modelEvent);
+			}
+		}
+	}
+
+	boolean drawSimilarityRanges = true;	// whether or not the whole range of similarity values should be drawn
+	public boolean getDrawSimilarityRanges() {
+		return drawSimilarityRanges;
+	}
+	public void setDrawSimilarityRanges(boolean drawSimilarityRanges) {
+		if (this.drawSimilarityRanges != drawSimilarityRanges) {
+			this.drawSimilarityRanges = drawSimilarityRanges;
+			fireDrawingSettingsEvent ();
+		}
+	}
+}
diff --git a/src/org/gel/mauve/analysis/BrokenCDS.java b/src/org/gel/mauve/analysis/BrokenCDS.java
new file mode 100644
index 0000000..c5ec61f
--- /dev/null
+++ b/src/org/gel/mauve/analysis/BrokenCDS.java
@@ -0,0 +1,208 @@
+package org.gel.mauve.analysis;
+
+import java.text.NumberFormat;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+
+public class BrokenCDS {
+	
+	private LiteWeightFeature cds;
+	
+	
+	private TreeMap<Integer, Character> prmtrStops;
+	
+	/**
+	 * Values are length-2 arrays where element 0
+	 * contains the initial amino acid at this postion
+	 * and element 1 contains the final amino acid at 
+	 * this position
+	 */
+	private TreeMap<Integer,char[]> naSubs;
+	
+	private TreeMap<Integer, char[]> aaSubs;
+	
+	private TreeMap<Integer,Integer> frmShfts;
+	
+	private TreeSet<Integer> insStops;
+	
+	/** Segments in amino acid sequence where the assembly is out of frame */
+	private Vector<int[]> bfSegs;
+	
+	private Vector<int[]> gapSegs;
+
+	private double aaSubRate;
+	
+	public BrokenCDS(LiteWeightFeature cds){
+		if (cds.getLength() % 3 != 0) {
+			throw new IllegalArgumentException("CDS length must be divisible by 3.");
+		}
+		this.cds = cds;
+		prmtrStops = new TreeMap<Integer,Character>();
+		naSubs = new TreeMap<Integer, char[]>();
+		aaSubs = new TreeMap<Integer, char[]>();
+		frmShfts = new TreeMap<Integer, Integer>();
+		bfSegs = new Vector<int[]>();
+		gapSegs = new Vector<int[]>();
+		insStops = new TreeSet<Integer>();
+	}
+	
+	public void addNASubstitution(int pos, char from, char to){
+		char[] pat = {from, to};
+		naSubs.put(pos, pat);
+	}
+	
+	public void addAASubstitution(int pos, char from, char to) {
+		char[] pat = {from, to};
+		aaSubs.put(pos, pat);
+	}
+	
+	public void setAASubRate(double perc) {
+		if (perc < 0 || perc > 1) {
+			throw new IllegalArgumentException("Error rates must be between 0-1, inclusive.");
+		}
+		aaSubRate = perc;
+	}
+	
+	public void addPrmtrStop(int pos, char from){
+		prmtrStops.put(pos,from);
+	}
+	
+	public void addInsertionStop(int pos){
+		insStops.add(pos);
+	}
+	
+	public void addBFSegment(int[] range) {
+		if (bfSegs.contains(range)){
+			System.out.flush();
+		}
+		bfSegs.add(range);
+	}
+	
+	public void addGapSegment(int[] range) {
+		gapSegs.add(range);
+	}
+	
+	public void addFrameShift(int pos, int length) throws IllegalArgumentException{
+		if (length !=1 && length !=2){
+			throw new IllegalArgumentException("Illegal length: " + length + " - Frameshifts can only be of length 1 or 2.");
+		} else {
+			frmShfts.put(pos, length);
+		}
+	}
+	
+	public String toString(){
+		/*FeatureID Peptide_Length PercIncAAs BrokenFrameSegments GapSegments Subst_Positions Subst_Patterns PrmtrStop_Positions PrmtrStop_OrigRes Frame_Shift_Stops*/
+		StringBuilder sb = new StringBuilder();
+		NumberFormat nf = NumberFormat.getInstance();
+		nf.setMaximumFractionDigits(6);
+		
+		sb.append(cds.getID()+"\t"+getPeptideLength()+"\t"+nf.format(aaSubRate));
+		
+		String tmp = sb.toString();
+		if (bfSegs.size() > 0) {
+			Iterator<int[]> bfIt = bfSegs.iterator();
+			boolean first = true;
+			while(bfIt.hasNext()){
+				int[] seg = bfIt.next();
+				if (first) {
+					first = false;
+					sb.append("\t["+seg[0]+","+seg[1]+"]");
+				} else {
+					sb.append(",["+seg[0]+","+seg[1]+"]");
+				}
+			}
+		} else {
+			sb.append("\t-");
+		}
+		
+		tmp = sb.toString();
+		if (gapSegs.size() > 0) {
+			Iterator<int[]> gapIt = gapSegs.iterator();
+			boolean first = true;
+			while (gapIt.hasNext()) {
+				int[] seg = gapIt.next();
+				if (first) {
+					first = false;
+					sb.append("\t["+seg[0]+","+seg[1]+"]");
+				} else {
+					sb.append(",["+seg[0]+","+seg[1]+"]");
+				}
+			}
+		} else {
+			sb.append("\t-");
+		}
+		
+		
+		tmp = sb.toString();
+		if (aaSubs.size() > 0) {
+			Iterator<Integer> it = aaSubs.keySet().iterator();
+			StringBuilder subPos = new StringBuilder();
+			StringBuilder subPat = new StringBuilder();
+			boolean first = true;
+			while(it.hasNext()){
+				int pos = it.next();
+				char[] pat = aaSubs.get(pos);
+				if (first){
+					subPat.append(pat[0] +"->"+ pat[1]);
+					subPos.append(pos);
+					first = false;
+				} else {
+					subPat.append(","+pat[0] +"->"+ pat[1]);
+					subPos.append(","+pos);
+				}
+			}
+			sb.append("\t"+subPos.toString()+"\t"+subPat.toString());
+		} else {
+			sb.append("\t-\t-");
+		}
+		if (prmtrStops.size() > 0) {
+			Iterator<Integer> it = prmtrStops.keySet().iterator();
+			StringBuilder subPos = new StringBuilder();
+			StringBuilder subPat = new StringBuilder();
+			subPat = new StringBuilder();
+			boolean first = true;
+			while(it.hasNext()){
+				int pos = it.next();
+				char c = prmtrStops.get(pos);
+				if (first) {
+					subPos.append(pos);
+					subPat.append(c);
+					first = false;
+				} else {
+					subPos.append(","+pos);
+					subPat.append(","+c);
+				}
+			}
+			sb.append("\t"+subPos.toString()+"\t"+subPat.toString());
+		} else {
+			sb.append("\t-\t-");
+		}
+		
+		if (insStops.size() > 0) {
+			Iterator<Integer> it = insStops.iterator();
+			boolean first = true;
+			while (it.hasNext()) {
+				int pos = it.next();
+				if (first) {
+					sb.append("\t"+Integer.toString(pos));
+					first = false;
+				} else {
+					sb.append(","+Integer.toString(pos));
+				}
+			}
+		}
+		
+		return sb.toString();
+	}
+	
+	public int getPeptideLength(){
+		return (cds.getRight() - cds.getLeft() + 1) / 3;
+	}
+	
+	public double getPercIncPeptides(){
+		return aaSubRate;
+	}
+	
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/analysis/CDSErrorExporter.java b/src/org/gel/mauve/analysis/CDSErrorExporter.java
new file mode 100644
index 0000000..af983c2
--- /dev/null
+++ b/src/org/gel/mauve/analysis/CDSErrorExporter.java
@@ -0,0 +1,640 @@
+package org.gel.mauve.analysis;
+ 
+import java.util.Arrays;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.HashSet;
+import java.util.Vector;
+
+import org.biojava.bio.seq.DNATools;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.RNATools;
+import org.biojava.bio.symbol.AtomicSymbol;
+import org.biojava.bio.symbol.IllegalAlphabetException;
+import org.biojava.bio.symbol.IllegalSymbolException;
+import org.biojava.bio.symbol.Symbol;
+import org.biojava.bio.symbol.SymbolList;
+import org.biojava.bio.symbol.SymbolListFactory;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.XmfaViewerModel;
+
+/**
+ * A class for extracting broken CDS features from a draft genome. 
+ * 
+ * @author atritt
+ *
+ */
+public class CDSErrorExporter {
+
+	private static final char BAD_CODON = '!';
+	
+	/**
+	 * A mapping of CDS ids to vectors containing 
+	 * a list of all errors found in that CDS
+	 */
+	private HashMap<LiteWeightFeature,Vector<SNP>> snpErrors;
+	
+	private HashMap<LiteWeightFeature,Vector<Gap>> gapErrors;
+	
+	private XmfaViewerModel model;
+	
+	private HashMap<LiteWeightFeature,BrokenCDS> brokenCDS; 
+	
+	private int numCDS;
+	
+	private int numBrokenCDS;
+	
+	
+	public CDSErrorExporter(XmfaViewerModel model, SNP[] snps, Gap[] assGaps, Gap[] refGaps){
+		snpErrors = new HashMap<LiteWeightFeature,Vector<SNP>>();
+		gapErrors = new HashMap<LiteWeightFeature,Vector<Gap>>();
+		this.model = model;
+		brokenCDS = new HashMap<LiteWeightFeature, BrokenCDS>();
+		projectBrokenCDS(model, snps, assGaps, refGaps);
+		refineBrokenCDS();
+		HashSet<LiteWeightFeature> bcds = new HashSet<LiteWeightFeature>(snpErrors.keySet());
+		Iterator<LiteWeightFeature> it = gapErrors.keySet().iterator();
+		while(it.hasNext())
+			bcds.add(it.next());
+		bcds.addAll(gapErrors.keySet());
+		numBrokenCDS = bcds.size();
+	}
+	
+	public BrokenCDS[] getBrokenCDS(){
+		return brokenCDS.values().toArray(new BrokenCDS[brokenCDS.size()]);
+	}
+	
+	public int numBrokenCDS(){
+		return numBrokenCDS;
+	}
+	
+	public int numCompleteCDS(){
+		return numCDS - numBrokenCDS;
+	}
+	
+	/**
+	 * Finds all CDS that overlap with any of the SNPs or Gaps passed in.
+	 * 
+	 * @param model
+	 * @param snpsAr
+	 * @param assGaps gaps in the assembly
+	 * @return
+	 */
+	private void projectBrokenCDS(XmfaViewerModel model, SNP[] snpsAr, Gap[] assGaps, Gap[] refGaps){
+		if (model.getGenomes().size() > 2)
+			return;
+		LiteWeightFeature[] cds = OneToOneOrthologExporter.getFeaturesByType(0,
+				model.getGenomeBySourceIndex(0).getAnnotationSequence().features(), "CDS");
+		numCDS = cds.length;
+		System.err.println("numCDS: " + numCDS);
+		SNP[] snps = new SNP[snpsAr.length];
+		System.arraycopy(snpsAr, 0, snps, 0, snps.length);
+		Arrays.sort(snps, SNP.getLoopingComparator(0));
+		Arrays.sort(cds, LiteWeightFeature.getLoopingComparator());
+		int snpI = 0;
+		int cdsI = 0;
+		while (cdsI < cds.length && snpI < snps.length){
+			int comp = snps[snpI].relativePos(cds[cdsI]);
+			if (comp > 0){
+				cdsI++;
+			} else if (comp < 0){
+				snpI++;
+			} else {// add this SNP and get the next one
+				if (snpErrors.containsKey(cds[cdsI])) {
+					snpErrors.get(cds[cdsI]).add(snps[snpI]);
+				} else {
+					Vector<SNP> v = new Vector<SNP>();
+					snpErrors.put(cds[cdsI], v);
+				}
+				snpI++;
+			}
+		}
+		
+		Gap[] gaps = new Gap[assGaps.length];
+		System.arraycopy(assGaps, 0, gaps, 0, gaps.length);
+		Arrays.sort(gaps, Gap.getAlnmtPosComparator());
+		int gapI = 0;
+		cdsI = 0;
+		while (cdsI < cds.length && gapI < gaps.length){
+			int comp = gaps[gapI].relativePos(cds[cdsI]);
+			if (comp > 0){
+				cdsI++;
+			} else if (comp < 0) {
+				gapI++;
+			} else {
+				if (gapErrors.containsKey(cds[cdsI])){
+					gapErrors.get(cds[cdsI]).add(gaps[gapI]);
+				} else {
+					Vector<Gap> v = new Vector<Gap>();
+					gapErrors.put(cds[cdsI], v);
+				}
+				gapI++;
+			}
+		}
+		
+		gaps = new Gap[refGaps.length];
+		System.arraycopy(refGaps, 0, gaps, 0, refGaps.length);
+		gapI = 0;
+		cdsI = 0;
+		while(cdsI < cds.length && gapI < gaps.length){
+			int comp = gaps[gapI].relativePos(cds[cdsI]);
+			if (comp > 0) {
+				cdsI++;
+			} else if (comp < 0){
+				gapI++;
+			} else {
+				if (gapErrors.containsKey(cds[cdsI])){
+					gapErrors.get(cds[cdsI]).add(gaps[gapI]);
+				} else {
+					Vector<Gap> v = new Vector<Gap>();
+					gapErrors.put(cds[cdsI],v);
+				}
+				gapI++;
+			}
+		}
+		
+	}
+	
+	private void refineBrokenCDS(){
+		loadSnpErrors();
+		loadDelErrors();
+	}
+	
+	/**
+	 * 
+	 */
+	private void loadSnpErrors(){
+		Iterator<LiteWeightFeature> it = snpErrors.keySet().iterator();
+		while(it.hasNext()){
+			LiteWeightFeature feat = it.next();
+			if (gapErrors.containsKey(feat)){
+				continue;
+			} else {
+				char[] refSeq = model.getSequence(feat.getLeft(), feat.getRight(), 0);
+				long[] leftLCB = model.getLCBAndColumn(0, feat.getLeft());
+				long[] rightLCB = model.getLCBAndColumn(0,feat.getRight());
+				long[] left_pos = new long[model.getGenomes().size()];
+				boolean[] gap = new boolean[leftLCB.length];
+				long[] right_pos = new long[model.getGenomes().size()];
+				model.getColumnCoordinates((int)leftLCB[0], leftLCB[1], left_pos, gap);
+				model.getColumnCoordinates((int)rightLCB[0], rightLCB[1], right_pos, gap);
+				if (left_pos[1] == 0 || right_pos[1] == 0) {
+//					System.err.println("BAD_SNP_ERROR : feature " + feat.getID());
+					continue;
+				} else if (left_pos[0] != right_pos[0]) {
+//					System.err.println("Different LCBs");
+				} else {
+					char[] assSeq = model.getSequence(left_pos[1], right_pos[1], 1);
+					computeSubstitutions(feat,refSeq,assSeq);
+				}
+			}
+		}
+	}
+
+	private void loadDelErrors(){
+		Iterator<LiteWeightFeature> it = gapErrors.keySet().iterator();
+		
+		BrokenCDS bcds = null;
+		while (it.hasNext()) {
+			LiteWeightFeature feat = it.next();
+			try{
+				refineBrokenCDS(feat);
+			}catch(Exception e){
+				System.err.println("Warning, unable to process reference gene " + feat.getID());
+			}
+		}
+		
+		it = snpErrors.keySet().iterator();
+		while(it.hasNext()) {
+			LiteWeightFeature feat = it.next();
+			try{
+				refineBrokenCDS(feat);
+			}catch(Exception e){
+				System.err.println("Warning, unable to process reference gene " + feat.getID());
+			}
+		}
+		
+	}
+	
+	/**
+	 * Runs over the gene alignment with a fine tooth comb. 
+	 * Here, we look for frameshift errors, AA substitution errors,
+	 * including premature stops, and gaps in the AA sequence.
+	 * 
+	 * @param feat
+	 */
+	private void refineBrokenCDS(LiteWeightFeature feat) {
+	//	LiteWeightFeature feat = it.next();
+		BrokenCDS bcds = null;
+		if (brokenCDS.containsKey(feat)) {
+			bcds = brokenCDS.get(feat);
+		} else {
+			bcds = new BrokenCDS(feat);
+			brokenCDS.put(feat, bcds);
+		}
+		byte[][] alnmt = model.getSequenceRange(0, feat.getLeft(), feat.getRight());
+		if (alnmt[0].length != alnmt[1].length) {
+//			System.err.println("Different sequence lengths");
+		}
+		
+		byte[][][] codons = splitOnRefCodons(alnmt);
+		
+		int numCodons = 0;
+		int numBadCodons = 0;
+		int frameShift = 0;
+		boolean inFrame = true;
+		int lastInFrame = 1;
+		boolean inGap = false;
+		int lastNoGap = 1;
+		int assNACount = 0;
+		
+		for (int cdnI = 0; cdnI < codons.length; cdnI++) {
+			byte[][] codon = codons[cdnI];
+			String ref = new String(codon[0]);
+			String ass = new String(codon[1]);
+			int refGapCount = numGaps(codon[0]);
+			int assGapCount = numGaps(codon[1]);
+			assNACount = assNACount + codon[1].length - assGapCount;
+			// FIXME 
+			if (frameShift % 3 == 0){ // in-frame
+				
+				
+				if (refGapCount == 0){ // make sure this isn't an inter-codon gap
+					numCodons++;
+					if (isCodon(codon[1])){ // make sure we don't have an intra-codon gap
+						char aa_ref = translate(trimGaps(codon[0]));
+						char aa_ass = translate(trimGaps(codon[1]));
+						if (aa_ref != aa_ass){
+							numBadCodons++;
+							if (aa_ass == '*' && aa_ref != '*'){ // check for nonsense mutation
+								bcds.addPrmtrStop(numCodons, aa_ref);
+							} else {
+								bcds.addAASubstitution(numCodons, aa_ref, aa_ass);
+							}
+						} else {
+							lastNoGap = numCodons;	
+						}
+						if (inGap) { // ending the stretch of gap
+							inGap = false;
+							int[] tmp = {lastNoGap, numCodons-1};
+							bcds.addGapSegment(tmp);
+						}
+					} else { // bad codon
+						numBadCodons++;
+						if (inGap) {
+							if (assGapCount % 3 == 0) { // still in frame
+								
+							} else { // not a gap. just a stretch of broken frame
+								
+							}
+						} else {
+							inGap = true;
+							lastNoGap = numCodons;
+							
+						}
+					}
+					lastInFrame = numCodons;
+				} else {  // we will enter here if the reference codon is a blank
+					if (codon[0].length == 3) {
+						if (isCodon(codon[1])){
+							char aa = translate(codon[1]);
+							if (aa == '*')
+								bcds.addInsertionStop(assNACount/3);
+						}
+					}
+				}
+			} else { // out of frame, so this codon won't be correct
+				
+				if (isCodon(codon[0])){
+					numCodons++;
+					numBadCodons++;
+				}else {
+					System.out.flush();
+				}
+				// AJT0403: check for stop codon due to frameshift here
+				
+			}
+			frameShift += assGapCount - refGapCount;
+			
+			if (inFrame) {
+				if (frameShift % 3 != 0) {
+					inFrame = false;
+					lastInFrame = numCodons;
+					// starting a stretch of broken frame
+				}
+			} else {
+				if (frameShift % 3 == 0 || cdnI == codons.length - 1) {
+					inFrame = true;
+					int[] tmp = {lastInFrame+1 , numCodons};
+					bcds.addBFSegment(tmp);
+					// ending a stretch of broken frame
+				}
+			}
+	
+			
+		}
+		bcds.setAASubRate(((double)numBadCodons)/((double)numCodons));
+	}
+	
+	private void computeSubstitutions(LiteWeightFeature feat, char[] refSeq, char[] assSeq){
+		if (refSeq.length != assSeq.length){
+			throw new IllegalArgumentException("Sequences must be the same length: ref length = "
+										+ refSeq.length + " assembly length = " + assSeq.length); 
+		}
+		try {
+			char[] tmp = RNATools.translate(
+					  	 DNATools.toRNA(
+					  	 DNATools.createDNA(new String(refSeq))))
+					  	 .toString()
+					  	 .toCharArray();
+			refSeq = tmp;
+		} catch (IllegalSymbolException e) {
+			System.err.println(e.getMessage());
+			System.err.println("Bad Symbol in the reference sequence: \n >>" + new String(assSeq)+"<<");
+			e.printStackTrace();
+		} catch (IllegalAlphabetException e) {
+			e.printStackTrace();
+		}
+		try {
+			char[] tmp = RNATools.translate(
+					 DNATools.toRNA(
+					 DNATools.createDNA(new String(assSeq))))
+					 .toString()
+					 .toCharArray();
+			assSeq = tmp;
+		} catch (IllegalSymbolException e) {
+			System.err.println(e.getMessage());
+			System.err.println("Bad Symbol in the assembly sequence: \n >>" + new String(assSeq)+"<<");
+			e.printStackTrace();
+		} catch (IllegalAlphabetException e) {
+			e.printStackTrace();
+		}
+		
+		for (int i = 0; i < assSeq.length; i++){
+			if (assSeq[i] != refSeq[i]) {
+				if (assSeq[i] == '*') {
+					if (brokenCDS.containsKey(feat)){
+						brokenCDS.get(feat).addPrmtrStop(i+1, refSeq[i]);
+					} else {
+						BrokenCDS tmp = new BrokenCDS(feat);
+						tmp.addPrmtrStop(i+1, refSeq[i]);
+						brokenCDS.put(feat, tmp);
+					}
+				} else {
+					if (brokenCDS.containsKey(feat)){
+						brokenCDS.get(feat).addNASubstitution(i+1, refSeq[i], assSeq[i]);
+					} else {
+						BrokenCDS tmp = new BrokenCDS(feat);
+						tmp.addNASubstitution(i+1, refSeq[i], assSeq[i]);
+						brokenCDS.put(feat, tmp);
+					}
+				}
+			}                           
+		}
+	}
+
+	/**
+	 * Returns true if we have 3 valid bases in this byte array, false otherwise
+	 * @param ar
+	 * @return
+	 */
+	public static boolean isCodon(byte[] ar) {
+		int numBases = 0;
+		for (byte b: ar)
+			if (isValidNucAcid(b)) numBases++;
+		return numBases == 3;
+	}
+	
+	public static boolean hasGap(byte[] ar){
+		for (int i = 0; i < ar.length; i++){
+			if (ar[i] == '-') 
+				return true;
+		}
+		return false;
+	}
+	
+	public static int numGaps(byte[] ar){
+		int ret = 0;
+		for (byte b: ar)
+			if (b == '-') ret++;
+		return ret;
+	}
+	
+	public static byte[] trimGaps(byte[] ar){
+		int numBases = ar.length - numGaps(ar);
+		byte[] ret = new byte[numBases];
+		int newBaseI = 0;
+		for (int i = 0; i < ar.length; i++){
+			if (ar[i] != '-')
+				ret[newBaseI++] = ar[i];
+		}
+		return ret;
+	}
+	
+	//public byte[] subArray(byte[] in, )
+	
+	/**
+	 * <br>
+	 * Returns an array of 2D byte arrays.
+	 * The first index of one of these 2D byte arrays will 
+	 * contain the reference codon, and the 
+	 * second index will contain the assembly 
+	 * codon.
+	 *</br>
+	 *
+	 * 
+	 * <br>
+	 * Reference arrays always length [1,3] and 
+	 * always have either 0 or 3 nucleic acid codes.
+	 * </br>
+	 * <br>
+	 * Assembly arrays always length [1,3] and 
+	 * can have any number of nucleic acid codes.
+	 * </br>
+	 * 
+	 */
+	public static byte[][][] splitOnRefCodons(byte[][] ar){
+		byte[] ref = ar[0];
+		byte[] ass = ar[1];
+		// first, make sure we have a valid coding sequence
+		int numBases = 0;
+		for (int i = 0; i < ref.length; i++){
+			if (isValidNucAcid(ref[i])) numBases++;
+		}
+		if (numBases % 3 != 0) {
+			throw 
+				new IllegalArgumentException("Number of references bases - "+
+											numBases+" - not divisible by 3");
+		}
+		
+		Vector<byte[][]> vect = new Vector<byte[][]>();
+		int codonStart = 0;
+		int baseI = 0;
+		int codonPos = 0; // should only take on values 0,1,2,3
+		int prevCodonEnd = 0; // assume the first base in the sequence is a valid nuc. acid 
+		boolean lookForNewCodonStart = false;
+		int nGaps = 0;
+		while (baseI < ref.length) {
+			if (isValidNucAcid(ref[baseI])) {
+				if (lookForNewCodonStart) {
+					if (prevCodonEnd != baseI-1){
+						/*  cut out the gap: everything between 
+						     prevCodonEnd (exc.) and baseI (exc.)  */
+						byte[][] tmp = new byte[2][baseI-prevCodonEnd-1];
+						System.arraycopy(ref, prevCodonEnd+1, tmp[0], 0, tmp[0].length);
+						System.arraycopy(ass, prevCodonEnd+1, tmp[1], 0, tmp[1].length);
+						vect.add(tmp);
+					}
+					codonStart = baseI;
+					lookForNewCodonStart = false;
+				}
+				codonPos++;
+			} else {
+				if (ref[baseI] == '-') {
+					nGaps++;
+				}
+				
+				if (nGaps == 3) {
+					nGaps = 0;
+					byte[][] tmp = new byte[2][3];
+					System.arraycopy(ref, baseI-2, tmp[0], 0, 3);
+					System.arraycopy(ass, baseI-2, tmp[1], 0, 3);
+					vect.add(tmp);
+					prevCodonEnd = baseI;
+				}
+				
+				if (lookForNewCodonStart)
+					codonStart++;
+				if (baseI == 0)
+					throw new IllegalArgumentException(ref[baseI]+
+							" : Sequence must start with a valid nucleic acid code.");
+			}
+			if (codonPos == 3) {
+				/*  cut out everything between 
+				    prevCodonEnd and baseI   */
+				byte[][] tmp = new byte[2][baseI-codonStart+1];
+				System.arraycopy(ref, codonStart, tmp[0], 0, tmp[0].length);
+				System.arraycopy(ass, codonStart, tmp[1], 0, tmp[1].length);
+				vect.add(tmp);
+				codonPos = 0;
+				lookForNewCodonStart = true;
+				prevCodonEnd = baseI;
+			}
+			baseI++;
+		}
+		return vect.toArray(new byte[vect.size()][][]);
+	}
+	
+	public static char translate(byte[] codon){
+		if (hasGap(codon)) {
+			codon = trimGaps(codon);
+		}
+		try {
+			return 	RNATools.translate(
+					DNATools.toRNA(
+					DNATools.createDNA(
+					new String(codon))))
+					.seqString().charAt(0);
+		
+		} catch (IndexOutOfBoundsException e) {
+			e.printStackTrace();
+		} catch (IllegalAlphabetException e) {
+			e.printStackTrace();
+		} catch (IllegalSymbolException e) {
+			e.printStackTrace();
+		}
+		return '-';
+	}
+	
+	
+	
+	private static String getBadChars(byte[] ar){
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < ar.length; i++){
+			if (!isValidChar(ar[i])){
+				sb.append((i+1)+":"+new Character((char)ar[i]) + " ");
+			}
+		}
+		return sb.toString();
+	}
+
+	public static  boolean isValidChar(byte b){
+			switch(b){
+			case 'a': return true;
+			case 'A': return true;
+			case 'c': return true;
+			case 'C': return true;
+			case 't': return true;
+			case 'T': return true;
+			case 'g': return true;
+			case 'G': return true;
+			case 'k': return true;
+			case 'K': return true;
+			case 'm': return true;
+			case 'M': return true;
+			case 'r': return true;
+			case 'R': return true;
+			case 'y': return true;
+			case 'Y': return true;
+			case 's': return true;
+			case 'S': return true;
+			case 'w': return true;
+			case 'W': return true;
+			case 'b': return true;
+			case 'B': return true;
+			case 'v': return true;
+			case 'V': return true;
+			case 'h': return true;
+			case 'H': return true;
+			case 'd': return true;
+			case 'D': return true;
+			case 'x': return true;
+			case 'X': return true;
+			case 'n': return true;
+			case 'N': return true;
+			case '-': return true;
+				default : return false;
+			}
+	}
+
+	public static  boolean isValidNucAcid(byte b){
+		switch(b){
+		case 'a': return true;
+		case 'A': return true;
+		case 'c': return true;
+		case 'C': return true;
+		case 't': return true;
+		case 'T': return true;
+		case 'g': return true;
+		case 'G': return true;
+		case 'k': return true;
+		case 'K': return true;
+		case 'm': return true;
+		case 'M': return true;
+		case 'r': return true;
+		case 'R': return true;
+		case 'y': return true;
+		case 'Y': return true;
+		case 's': return true;
+		case 'S': return true;
+		case 'w': return true;
+		case 'W': return true;
+		case 'b': return true;
+		case 'B': return true;
+		case 'v': return true;
+		case 'V': return true;
+		case 'h': return true;
+		case 'H': return true;
+		case 'd': return true;
+		case 'D': return true;
+		case 'x': return true;
+		case 'X': return true;
+		case 'n': return true;
+		case 'N': return true;
+			default : return false;
+		}
+	}
+}
diff --git a/src/org/gel/mauve/analysis/Gap.java b/src/org/gel/mauve/analysis/Gap.java
new file mode 100644
index 0000000..d3bccbc
--- /dev/null
+++ b/src/org/gel/mauve/analysis/Gap.java
@@ -0,0 +1,240 @@
+package org.gel.mauve.analysis;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.biojava.bio.seq.FeatureHolder;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.XmfaViewerModel;
+
+public class Gap implements Comparable<Gap>{
+
+	
+	private int genSrcIdx;
+	
+	private int lcbId;
+	
+	private int lcbCol;
+	
+	private long position;
+	
+	private int posInCtg;
+	
+	private Chromosome chrom;
+	
+	private long length;
+	
+	private XmfaViewerModel model;
+	
+	private long[] pos;
+	
+	private Chromosome[] chromosomes;
+	
+	
+	/**
+	 * Creates a gap with the given arguments.
+	 * 
+	 * @param genomeSrcIdx source index of Genome this Gap belongs to
+	 * @param lcb the ID of the LCB that this Gap belongs to
+	 * @param pos the position that this Gap starts at
+	 * @param len the length of the Gap
+	 * @param model the XmfaViewerModel holding the alignment that this Gap was determined under
+	 */
+	public Gap(int genomeSrcIdx, int lcb, long pos, long len, XmfaViewerModel model){
+		this.genSrcIdx = genomeSrcIdx;
+		this.position = pos;
+		this.length = len;
+		this.lcbId = lcb;
+		this.model = model;
+		Genome[] genomes = model.getGenomes().toArray(new Genome[model.getGenomes().size()]);
+		Genome g = model.getGenomeBySourceIndex(genSrcIdx);
+		this.chrom = g.getChromosomeAt(position);
+		if (this.chrom == null) System.err.println("Null Chromosome");
+		this.posInCtg = (int) (position - this.chrom.getStart()+1);
+		this.lcbCol = (int) model.getLCBAndColumn(g, position)[1];
+		this.pos = new long[model.getGenomes().size()];
+		long[] ar = model.getLCBAndColumn(genomeSrcIdx, pos);
+		boolean[] gap = new boolean[model.getGenomes().size()];
+		model.getColumnCoordinates((int)ar[0], ar[1], this.pos, gap);
+		chromosomes = new Chromosome[genomes.length];
+		for (int i = 0; i < genomes.length; i++){
+			chromosomes[i] = genomes[i].getChromosomeAt(this.pos[i]);
+		}
+	}
+	
+	private Gap(Gap gap){
+		this.genSrcIdx = gap.genSrcIdx;
+		this.position = gap.position;
+		this.length = gap.length;
+		this.lcbId = gap.lcbId;
+		this.model = gap.model;
+		this.chrom = gap.chrom;
+		this.posInCtg = gap.posInCtg;
+		this.lcbCol = gap.lcbCol;
+		this.pos = new long[gap.pos.length];
+		System.arraycopy(gap.pos, 0, this.pos, 0, this.pos.length);
+		this.chromosomes = new Chromosome[gap.chromosomes.length];
+		System.arraycopy(gap.chromosomes, 0, this.chromosomes, 0, this.chromosomes.length);
+	}
+	
+	
+	/**
+	 * Genome  Contig   Position_in_Contig   GenomeWide_Position   Length
+	 * 
+	 * @return a tab-delimited String with the information ordered as given above
+	 */
+	public String toString(){
+		return this.toString("sequence_"+Integer.toString(genSrcIdx));
+	}
+	
+	
+	
+	/**
+	 * Genome  Contig   Position_in_Contig   GenomeWide_Position   Length  seqi_pos seqi_ctg seqi_posInCtg
+	 * 
+	 * Replaces genome id with the given name
+	 * 
+	 * @param genomeName the name to put in place of the genome source index 
+	 * 
+	 * @return a tab-delimited String with the information ordered as given above
+	 */
+	public String toString(String genomeName){
+		StringBuilder sb = new StringBuilder();
+		sb.append(genomeName +"\t"+ chrom.getName() + "\t"+
+				Integer.toString(posInCtg)+"\t" + 
+				Long.toString(position) +"\t"+ Long.toString(length));
+		for (int i = 0; i < pos.length; i++){
+			int ctg_pos = (int)(pos[i] - chromosomes[i].getStart()+1); 
+			sb.append("\t"+pos[i]+"\t"+chromosomes[i].getName()+"\t"+ctg_pos);
+		}
+		return sb.toString();
+	}
+	
+	public int getGenomeSrcIdx(){
+		return genSrcIdx;
+	}
+	
+	public long getPosition(){
+		return position;
+	}
+	
+	public long getLength(){
+		return length;
+	}
+	
+	public int getLCB(){
+		return lcbId;
+	}
+	
+	public FeatureHolder getFeatures(int genSrcIdx){
+		Genome genome = model.getGenomeBySourceIndex(genSrcIdx);
+		long[] starts = new long[model.getGenomes().size()];
+		boolean[] gapS = new boolean[model.getGenomes().size()];
+		long[] ends = new long[model.getGenomes().size()];
+		boolean[] gapE = new boolean[model.getGenomes().size()];
+		model.getColumnCoordinates(lcbId, lcbCol, starts, gapS);
+		model.getColumnCoordinates(lcbId, lcbCol+1, ends, gapE);
+		long left = Math.min(starts[genSrcIdx],ends[genSrcIdx]);
+		long right = Math.max(starts[genSrcIdx],ends[genSrcIdx]);
+		boolean rev = starts[genSrcIdx] > ends[genSrcIdx];
+		return genome.getAnnotationsAt(left, right, rev);
+	}
+	
+	
+	/**
+	 * Returns the contig that this gap lies on
+	 * 
+	 * @param model
+	 * @return
+	 */
+	public Chromosome getContig(){
+		return chrom;
+	}
+	
+	/**
+	 * Returns the position of this gap in the contig
+	 * in which it lies.
+	 * 
+	 * @param model the <code>XmfaViewerModel</code> this Gap came from
+	 * 
+	 * @return a non-concatenated genome coordinate
+	 */
+	public int getPositionInContig(){
+		return posInCtg;
+	}
+	
+	public int compareTo(Gap g){
+		if (this.genSrcIdx == g.genSrcIdx){
+			if (this.position == g.position){
+				if (this.length == g.length){
+					return 0;
+				} else {
+					return this.length < g.length ? -1 : 1;
+				}
+			} else {
+				return this.position < g.position ? -1 : 1;
+			}
+		} else {
+			return this.genSrcIdx < g.genSrcIdx ? -1 : 1;
+		}
+	}
+	
+
+	public static Gap mergeGaps(Gap a, Gap b){
+		if (a.genSrcIdx != b.genSrcIdx)
+			throw new IllegalArgumentException("Can't merge gaps that aren't from same genome");
+		if (a.position != b.position)
+			throw new IllegalArgumentException("Can't merge gaps that don't start at the same position");
+		
+		Gap ret = new Gap(a);
+		ret.length += b.length;
+		return ret;
+		
+	}
+	
+	public static Comparator<Gap> getAlnmtPosComparator(){
+		return new Comparator<Gap>(){
+			public int compare(Gap a, Gap b){
+				if (a.genSrcIdx == b.genSrcIdx){
+					if (a.position == b.position){
+						return (int) (a.length - b.length);
+					} else {
+						return (int) (a.position - b.position);
+					}
+				} else {
+					return a.genSrcIdx-b.genSrcIdx;
+				}
+			}
+		};
+	}
+	/**
+	 * <br>Returns a negative number, zero, or a positive number 
+	 * if this Gap comes before, lies within, or comes after the
+	 * given feature, respectively. </br>
+	 * <br>
+	 * If the feature passed in is not a feature in the genome that this
+	 * gap pertains to, the relative position of this gap to the feature
+	 * is determined based on the position in the genome, which the feature 
+	 * belongs to, that is homologous to the position that this gap starts at.
+	 * </br>
+	 * <br>
+	 * Strand is neglected here, as a gap affects both strands.
+	 * </br>
+	 * @param feat the feature to compare this Gap to
+	 * 
+	 * @return a negative number, zero, or a positive number
+	 */
+	public int relativePos(LiteWeightFeature feat){
+		int pos = (int) Math.abs(this.pos[feat.getGenSrcIdx()]);
+		if (pos < feat.getLeft())
+			return -1;
+		else if (pos <= feat.getRight())
+			return 0;
+		else
+			return 1;
+	}
+	
+}
diff --git a/src/org/gel/mauve/analysis/IdentityMatrix.java b/src/org/gel/mauve/analysis/IdentityMatrix.java
new file mode 100644
index 0000000..c789ccd
--- /dev/null
+++ b/src/org/gel/mauve/analysis/IdentityMatrix.java
@@ -0,0 +1,104 @@
+package org.gel.mauve.analysis;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.gel.mauve.XMFAAlignment;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.backbone.Backbone;
+import org.gel.mauve.backbone.BackboneList;
+
+public class IdentityMatrix {
+
+	public static void export( XmfaViewerModel model, XMFAAlignment xmfa, BufferedWriter output ) throws IOException
+	{
+		int seq_count = model.getSequenceCount();
+		
+		for(int seqI=0; seqI<seq_count; seqI++){
+			for(int seqJ=0; seqJ<seq_count; seqJ++){
+				if(seqJ <= seqI){
+					output.write("\t");
+					continue;
+				}
+				int[][] dist_mat = SnpExporter.countSubstitutions(model, seqI, seqJ);
+				
+				long shared_len = 0;
+				BackboneList bb_list = model.getBackboneList();
+				Backbone[] bb_array = bb_list.getBackboneArray();
+				for(int i=0; i<bb_array.length; i++){
+					if(!bb_array[i].getSeqs()[seqI] || !bb_array[i].getSeqs()[seqJ]) continue;
+					shared_len += bb_array[i].getLength();
+				}
+				
+				long total_diffs = 0;
+				for(int i=0; i<4; i++){
+					for(int j=0; j<4; j++){
+						total_diffs += dist_mat[i][j]; 
+					}
+				}
+		
+				float dist_01 = (float)total_diffs / (float)shared_len;
+				String dist_string = "";
+				if(seqJ > 0) dist_string += "\t";
+				dist_string += dist_01;
+				output.write(dist_string);
+			}
+			output.write("\n");
+		}
+		output.close();
+	}
+
+	public static void main(String[] args) {
+		Options opts = new Options();
+		opts.addOption( OptionBuilder
+			.withArgName("alignment file")
+			.hasArg()
+			.withDescription("A required parameter specifying an XMFA format file generated by progressiveMauve")
+			.isRequired()
+			.create('f')
+			);
+
+		opts.addOption( OptionBuilder
+				.withArgName("output file name")
+				.hasArg()
+				.withDescription("The name of the output file for the identity matrix")
+				.isRequired()
+				.create('o')
+				);
+
+		CommandLineParser parser = new GnuParser();
+		CommandLine line=null;
+		try{
+			line = parser.parse( opts, args );
+		}catch(org.apache.commons.cli.ParseException pe){
+			HelpFormatter formatter = new HelpFormatter();
+			formatter.printHelp( 
+					"SnpExporter -f <XMFA alignment input> -o <SNP output>", opts);
+			
+			throw new RuntimeException("There was an error parsing your command-line options.  Please check them and try again.");
+		}
+		String alnmtFilePath = line.getOptionValue('f');
+		String outputFilePath = line.getOptionValue('o');
+		
+		try {
+			System.out.print("Loading alignment file...");
+			XmfaViewerModel model = new XmfaViewerModel(new File(alnmtFilePath), null);
+			System.out.println("Exporting SNPs...");
+			model.setReference(model.getGenomeBySourceIndex(0));
+			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath));
+			export(model, model.getXmfa(), bw);
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.exit(-1);
+		}
+		
+	}
+}
diff --git a/src/org/gel/mauve/analysis/LiteWeightFeature.java b/src/org/gel/mauve/analysis/LiteWeightFeature.java
new file mode 100644
index 0000000..9511a36
--- /dev/null
+++ b/src/org/gel/mauve/analysis/LiteWeightFeature.java
@@ -0,0 +1,130 @@
+package org.gel.mauve.analysis;
+
+import java.util.Comparator;
+
+public class LiteWeightFeature implements Comparable<LiteWeightFeature>
+{
+	
+	private int genSrcIdx;
+	private int left;
+	private int right;
+	private int strand;
+	private String locus;
+	private String type;
+	
+	private String ID;
+	
+	public LiteWeightFeature(int genSrcIdx, int l, int r, int s, String ltag, String featType){
+		this.genSrcIdx = genSrcIdx;
+		left = l; 
+		right = r; 
+		strand = s; 
+		locus = ltag; 
+		type = featType;
+		if (locus == null){
+			ID = featType+(s == -1 ? "-c-" : "-f-") + Integer.toString(l)+"-"+Integer.toString(r);
+		} else 
+			ID = locus;
+			
+	}
+	
+	public int getLength(){
+		return right - left + 1;
+	}
+	
+	public int hashCode(){
+		return ID.hashCode();
+	}
+	
+	public String getUniqueID(){
+		return ID;
+	}
+	
+	public String getLocus(){
+		return locus;
+	}
+	
+	public int getLeft(){
+		return left;
+	}
+	
+	public int getRight(){
+		return right;
+	}
+	
+	public boolean isReverse(){
+		return strand < 0;
+	}
+	
+	public int getStrand(){
+		return strand;
+	}
+	
+	public int getGenSrcIdx(){
+		return genSrcIdx;
+	}
+	
+	public String getType(){
+		return type;
+	}
+	
+	public int compareTo(LiteWeightFeature o)
+	{
+		if(left < o.left)
+			return -1;
+		else if(left > o.left)
+			return 1;
+		if(right < o.right)
+			return -1;
+		else if(right > o.right)
+			return 1;
+		if(strand < o.strand)
+			return -1;
+		else if(strand > o.strand)
+			return 1;
+		return 0;
+	}
+	
+	public String getID(){
+		return this.ID;
+	}
+	
+	public static Comparator<LiteWeightFeature> getLoopingComparator(){
+		return new Comparator<LiteWeightFeature>(){
+			public int compare(LiteWeightFeature o1, LiteWeightFeature o2){
+				// if on same strand
+				if (o1.strand == o2.strand){
+					if (o1.strand > 0){ // on forward strand
+						if (o1.left < o2.left) 
+							return -1;
+						else if (o1.left > o2.left)
+							return 1; 
+						if (o1.right < o2.right)
+							return -1;
+						else if (o1.right > o2.right)
+							return 1;
+						else
+							return 0;
+					} else { // on complementary strand
+						if (o1.right > o2.right)
+							return -1;
+						else if (o1.right < o2.right)
+							return 1;
+						if (o1.left > o2.left)
+							return -1;
+						else if (o1.left < o2.left)
+							return 1;
+						else
+							return 0;
+					}
+				} else if (o1.strand > 0){ // keep the forward strand stuff first
+					return -1;
+				} else {
+					return 1;
+				}
+			}
+		};
+	}
+	
+	
+}
diff --git a/src/org/gel/mauve/analysis/OneToOneOrthologExporter.java b/src/org/gel/mauve/analysis/OneToOneOrthologExporter.java
new file mode 100644
index 0000000..e4ebecc
--- /dev/null
+++ b/src/org/gel/mauve/analysis/OneToOneOrthologExporter.java
@@ -0,0 +1,1122 @@
+package org.gel.mauve.analysis;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.RenderingHints;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriter;
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
+import javax.imageio.stream.ImageOutputStream;
+import javax.management.RuntimeErrorException;
+import javax.swing.AbstractButton;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFormattedTextField;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.event.UndoableEditListener;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.PosixParser;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.symbol.Location;
+import org.biojava.bio.symbol.LocationTools;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.GenomeBuilder;
+import org.gel.mauve.LCB;
+import org.gel.mauve.SimilarityIndex;
+import org.gel.mauve.XMFAAlignment;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.backbone.Backbone;
+import org.gel.mauve.backbone.BackboneList;
+import org.gel.mauve.gui.RearrangementPanel;
+
+/*
+class LiteWeightFeature implements Comparable
+{
+	public LiteWeightFeature( int l, int r, int s, String ltag){ left = l; right = r; strand = s; locus = ltag; };
+	public int compareTo(Object o)
+	{
+		LiteWeightFeature c = (LiteWeightFeature)o;
+		if(left < c.left)
+			return -1;
+		else if(left > c.left)
+			return 1;
+		if(right < c.right)
+			return -1;
+		else if(right > c.right)
+			return 1;
+		if(strand < c.strand)
+			return -1;
+		else if(strand > c.strand)
+			return 1;
+		return 0;
+	}
+	int left;
+	int right;
+	int strand;
+	String locus;
+};
+*/
+public class OneToOneOrthologExporter {
+
+	/**
+	 * Takes an Iterator over features and trims it down to an 
+	 * array of LiteWeightFeatures that contains only those features
+	 * specified by <code>featureType</code>
+	 * 
+	 * @param genSrcIdx
+	 * @param i
+	 * @param featureType
+	 * @return
+	 */
+	public static LiteWeightFeature[] getFeaturesByType(int genSrcIdx, Iterator i, String featureType)
+	{
+		Vector cds = new Vector();
+		while(i.hasNext())
+		{
+			StrandedFeature f = (StrandedFeature)i.next();
+			if(f.getType().equalsIgnoreCase(GenomeBuilder.MAUVE_AGGREGATE))
+			{
+				LiteWeightFeature[] d = getFeaturesByType(genSrcIdx, f.features(), featureType);	// this is an aggregate, so recurse
+				for(int dI = 0; dI < d.length; dI++)
+					cds.add(d[dI]);
+				continue;
+			}
+			if(!f.getType().equalsIgnoreCase(featureType))
+				continue;
+			int l = f.getLocation().getMin();
+			int r = f.getLocation().getMax();
+			Object ltag = null;
+			try{
+				ltag = f.getAnnotation().getProperty("locus_tag");
+			}catch(Exception e){
+				try{
+					ltag = f.getAnnotation().getProperty("gene");
+				}catch(Exception e2){}				
+			}
+			if(ltag==null) ltag = new String();
+			if(l > r)
+			{
+				int t = l;
+				l = r;
+				r = t;
+			}
+			cds.add(new LiteWeightFeature(genSrcIdx, l, r, f.getStrand() == StrandedFeature.POSITIVE ? 1 : -1, ltag.toString(),featureType));
+		}
+		Collections.sort(cds);
+		// remove dupes
+		for(int j = 1; j < cds.size(); j++)
+		{
+			LiteWeightFeature cj = (LiteWeightFeature)cds.elementAt(j);
+			LiteWeightFeature cjm1 = (LiteWeightFeature)cds.elementAt(j-1);
+			if(cj.compareTo(cjm1) == 0 && cj.getLocus().equalsIgnoreCase(cjm1.getLocus()))
+			{
+				cds.remove(j);
+				j--;
+			}
+		}
+		LiteWeightFeature[] c = new LiteWeightFeature[cds.size()];
+		return (c = (LiteWeightFeature[])cds.toArray(c));
+	}
+	private static class Quad {
+		public Quad(long[] l, long[] r)
+		{ 
+			left = new long[l.length];
+			right = new long[r.length];
+			System.arraycopy(l, 0, left, 0, l.length);
+			System.arraycopy(r, 0, right, 0, r.length);
+			for(int i = 0; i < l.length; i++)
+			{
+				left[i] = left[i] < 0 ? -left[i] : left[i];
+				right[i] = right[i] < 0 ? -right[i] : right[i];
+				if(left[i] > right[i])
+				{
+					long t = right[i];
+					right[i] = left[i];
+					left[i] = t;
+				}
+			}
+		}
+		long[] left;
+		long[] right;
+	}
+
+	private static Quad[] getBackboneSegs(XmfaViewerModel model, Genome g_i, LiteWeightFeature cds)
+	{
+		XMFAAlignment xmfa = model.getXmfa();
+		// extract the alignment of the region in question
+		BackboneList bbl = model.getBackboneList();
+		int cur = cds.getLeft();
+		int max = cds.getRight();
+		boolean[] gap = new boolean[model.getSequenceCount()];
+		long[] tmp = new long[model.getSequenceCount()];
+		Vector bbsegs = new Vector();
+		LCB[] lcbs = model.getFullLcbList();
+		while(cur <= max)
+		{
+			Backbone bcur = bbl.getBackbone(g_i, cur);
+			if(bcur == null)
+			{	// check whether we've fallen outside backbone
+				bcur = bbl.getNextBackbone(g_i, cur);
+				if(bcur == null)
+					cur = max + 1;
+				else
+					cur = (int)(bcur.getLeftEnd(g_i));
+				continue;
+			}
+			Quad q = new Quad(bcur.left, bcur.right);
+			if(q.left[g_i.getSourceIndex()] < cds.getLeft())
+			{
+				long[] leftPos = xmfa.getLCBAndColumn(g_i, cds.getLeft());
+				if(leftPos == null)
+					System.err.println("Null leftpos!");
+				xmfa.getColumnCoordinates(model, (int)leftPos[0], leftPos[1], tmp, gap);
+				// check that it's still in-range
+				for(int ii = 0; ii < gap.length; ii++)
+				{
+					if(lcbs[bcur.getLcbIndex()].reverse[g_i.getSourceIndex()] == lcbs[bcur.getLcbIndex()].reverse[ii])
+						q.left[ii] = tmp[ii];
+					else
+						q.right[ii] = tmp[ii];
+					if(q.left[ii] < Math.abs(bcur.left[ii]) || q.left[ii] > Math.abs(bcur.right[ii]))
+					{
+						q.left[ii] = 0;
+						q.right[ii] = 0;
+					}
+				}
+			}
+			if(q.right[g_i.getSourceIndex()] > cds.getRight())
+			{
+				long[] rightPos = xmfa.getLCBAndColumn(g_i, cds.getRight());
+				if(rightPos == null)
+					System.err.println("Null rightpos!");
+				xmfa.getColumnCoordinates(model, (int)rightPos[0], rightPos[1], tmp, gap);
+				// check that it's still in-range
+				for(int ii = 0; ii < gap.length; ii++)
+				{
+					if(lcbs[bcur.getLcbIndex()].reverse[g_i.getSourceIndex()] == lcbs[bcur.getLcbIndex()].reverse[ii])
+						q.right[ii] = tmp[ii];
+					else
+						q.left[ii] = tmp[ii];
+					if(q.right[ii] < Math.abs(bcur.left[ii]) || q.right[ii] > Math.abs(bcur.right[ii]) || q.left[ii] == 0)
+					{
+						q.left[ii] = 0;
+						q.right[ii] = 0;
+					}
+				}
+			}
+			bbsegs.add(q);
+			cur = (int)(q.right[g_i.getSourceIndex()]) + 1;
+		}
+		Quad[] bbs = new Quad[bbsegs.size()];
+		bbs = (Quad[])bbsegs.toArray(bbs);
+		return bbs;
+	}
+
+	static class CdsOverlap
+	{
+		int length_i;
+		int length_j;
+		int left_i;
+		int right_i;
+	}
+	private static void addOverlappingCDS(XmfaViewerModel model, LiteWeightFeature[] cds, Quad bb, HashMap hm, int gI, int gJ)
+	{
+		// this is really a awful search, need a stabbing query instead!
+		for(int bs = 0; bs < cds.length; bs++)
+		{
+			if(!(bb.left[gJ] < cds[bs].getRight() && bb.right[gJ] > cds[bs].getLeft()))
+				continue;	// no overlap
+			// this one overlaps.  compute pct identity
+			long lefto = bb.left[gJ] > cds[bs].getLeft() ? bb.left[gJ] : cds[bs].getLeft();
+			long righto = bb.right[gJ] < cds[bs].getRight() ? bb.right[gJ] : cds[bs].getRight();
+			// get alignment columns
+			long[] lblob = model.getXmfa().getLCBAndColumn(model.getGenomeBySourceIndex(gJ), lefto);
+			long[] rblob = model.getXmfa().getLCBAndColumn(model.getGenomeBySourceIndex(gJ), righto);
+			long[] loffsets = new long[model.getSequenceCount()];
+			boolean[] lgaps = new boolean[model.getSequenceCount()];
+			long[] roffsets = new long[model.getSequenceCount()];
+			boolean[] rgaps = new boolean[model.getSequenceCount()];
+			model.getXmfa().getColumnCoordinates(model, (int)lblob[0], lblob[1], loffsets, lgaps);
+			model.getXmfa().getColumnCoordinates(model, (int)rblob[0], rblob[1], roffsets, rgaps);
+			for(int i = 0; i < loffsets.length; i++)
+			{
+				if(loffsets[i] > roffsets[i])
+				{
+					long tmp = loffsets[i];
+					loffsets[i] = roffsets[i];
+					roffsets[i] = tmp;
+				}
+			}
+			CdsOverlap co = new CdsOverlap();
+			co.left_i = (int)(loffsets[gI] > bb.left[gI] ? loffsets[gI] : bb.left[gI]);	// deal with a rare off-by-one the bad way.
+			co.right_i = (int)(roffsets[gI] < bb.right[gI] ? roffsets[gI] : bb.right[gI]);
+			if(co.left_i > co.right_i)
+				continue;	// weird.  no overlap.
+			co.length_i = (int)(roffsets[gI] - loffsets[gI]);
+			co.length_j = (int)(roffsets[gJ] - loffsets[gJ]);
+			Object v = hm.get(new Integer(bs));
+			if(v == null)
+				v = new Vector();
+			((Vector)v).add(co);
+			hm.put(new Integer(bs), v);
+		}
+	}
+
+	static public class OrthologExportParameters
+	{
+		// find all annotated orthologs that meet the following criteria:
+		 /**< minimum length of alignment as a fraction of feature */
+		public float min_conserved_length = 0.7f; 
+		/**< max length of alignment coverage of feature */
+		public float max_conserved_length = 1.0f;  
+		/**< min nucleotide identity in aligned region */
+		public float min_nucleotide_id = 0.6f;	
+		/**< Max nucleotide identity over aligned region */
+		public float max_nucleotide_id = 1.0f;	
+		/**< Include aligned regions that are not annotated with features (not yet implemented) */
+		public boolean predictUnannotated = false;	
+		/**< Type of features for which to find orthologs, e.g. CDS, tRNA, misc_RNA etc. */
+		public String featureType = "CDS";	
+		/**< If non-null, specifies a file where an XMFA alignment of features should be stored */
+		public File alignmentOutputFile = null;	
+	}
+	
+	public static void export( XmfaViewerModel model, BufferedWriter output, OrthologExportParameters oep ) throws IOException
+	{
+		float min_conserved_length = oep.min_conserved_length;
+		float max_conserved_length = oep.max_conserved_length;  
+		float min_nucleotide_id = oep.min_nucleotide_id;
+		float max_nucleotide_id = oep.max_nucleotide_id;
+
+		// for each annotated CDS in each genome, identify candidate 
+		// ortholog CDSes in other genomes
+		Vector allCds = new Vector();
+		for(int gI = 0; gI < model.getSequenceCount(); gI++)
+		{
+			Genome g = model.getGenomeBySourceIndex(gI);
+            Location loc = LocationTools.makeLocation(1, (int)g.getLength());
+            LiteWeightFeature[] cdsi = new LiteWeightFeature[0];
+            if(g.getAnnotationSequence() != null && loc != null )
+            {
+				FeatureHolder fh = g.getAnnotationSequence().filter(new FeatureFilter.OverlapsLocation(loc));
+				cdsi = getFeaturesByType(gI, fh.features(), oep.featureType);
+				Arrays.sort(cdsi);
+            }
+			allCds.add(cdsi);
+		}
+
+		// orthoPairs will contain a pairwise mapping of orthologs among each pair of genomes
+		// keys are CDS indexes and values are vectors of CDS indices
+		HashMap[][] orthoPairs = new HashMap[model.getSequenceCount()][model.getSequenceCount()];
+		for(int gI = 0; gI < model.getSequenceCount(); gI++)
+			for(int gJ = 0; gJ < model.getSequenceCount(); gJ++)
+				orthoPairs[gI][gJ] = new HashMap();
+		for(int gI = 0; gI < model.getSequenceCount(); gI++)
+		{
+			Genome g_i = model.getGenomeBySourceIndex(gI);
+			LiteWeightFeature[] cdsi = (LiteWeightFeature[])allCds.elementAt(gI);
+			for(int cI = 0; cI < cdsi.length; cI++)
+			{
+				// extract the alignment of the region in question
+				Quad[] bbs;
+				bbs = getBackboneSegs(model, g_i, cdsi[cI]);
+				byte[][] aln = model.getXmfa().getRange(g_i, (long)cdsi[cI].getLeft(), (long)cdsi[cI].getRight());
+				// for each of the other genomes, find the CDS that overlap the backbone segs and assess orthology
+				for(int gJ = gI+1; gJ < model.getSequenceCount(); gJ++)
+				{
+					// for each of the bb segs, find overlapping cds
+					HashMap hm = new HashMap();
+					LiteWeightFeature[] cdsj = (LiteWeightFeature[])allCds.elementAt(gJ);
+					for( int bbI = 0; bbI < bbs.length; bbI++)
+						addOverlappingCDS(model, cdsj, bbs[bbI], hm, gI, gJ);
+
+					// now for each overlapping CDS that meets the coverage threshold, 
+					// compute the percent identity
+					Iterator ki = hm.keySet().iterator();
+					while(ki.hasNext())
+					{
+						Integer cdsid = (Integer)ki.next();
+						Vector v = (Vector)hm.get(cdsid);
+						int cov_i = 0;
+						int cov_j = 0;
+						int left_min = -1;
+						int right_max = -1;
+						Iterator viter = v.iterator();
+						while(viter.hasNext())
+						{
+							
+							CdsOverlap co = (CdsOverlap)viter.next();
+							cov_i += co.length_i;
+							cov_j += co.length_j;
+							if(co.left_i < left_min || left_min == -1)
+								left_min = co.left_i;
+							if(co.right_i > right_max || right_max == -1)
+								right_max = co.right_i;
+						}
+						int cons_i = cdsi[cI].getRight() - cdsi[cI].getLeft();
+						if(cov_i < min_conserved_length * cons_i ||
+								cov_i > max_conserved_length * cons_i)
+							continue;	// not enough covered
+						int cons_j = cdsj[cdsid.intValue()].getRight() - cdsj[cdsid.intValue()].getLeft();
+						if(cov_j < min_conserved_length * cons_j ||
+								cov_j > max_conserved_length * cons_j)
+							continue;	// not enough covered
+						
+						// now compute percent id
+						int cur = cdsi[cI].getLeft();
+						int col = 0;
+						byte[] aln_gI = aln[gI];
+						byte[] aln_gJ = aln[gJ];
+						while(cur < left_min)
+						{
+							if(col == aln_gI.length)
+							{
+								System.err.println("bombers");
+							}
+							if(aln_gI[col] != '-' && aln_gI[col] != '\n' && aln_gI[col] != '\r')
+								cur++;
+							col++;
+						}
+						int id = 0;
+						int tot = 0;
+						while(cur <= right_max)
+						{
+							if(col == aln_gI.length)
+							{
+								System.err.println("bombers");
+							}
+							if(aln_gI[col] != '-' && aln_gI[col] != '\n' && aln_gI[col] != '\r')
+							{
+								cur++;
+								if(col < aln_gJ.length && aln_gJ[col] != '-' && aln_gJ[col] != '\n' && aln_gJ[col] != '\r')
+									tot++;
+								if(col < aln_gJ.length && 
+										SimilarityIndex.char_map[(char)aln_gI[col]] == SimilarityIndex.char_map[(char)aln_gJ[col]])
+									id++;	// Use of char_map should fix upper/lowercase issues
+							}
+							col++;
+						}
+						if(id >= min_nucleotide_id * tot && id <= max_nucleotide_id * tot)
+						{
+							Integer kkk = new Integer(cI);
+							Vector vvv = (Vector)orthoPairs[gI][gJ].get(kkk);
+							if(vvv == null) vvv = new Vector();
+							vvv.add(cdsid);
+							orthoPairs[gI][gJ].put(kkk, vvv);
+							vvv = (Vector)orthoPairs[gJ][gI].get(cdsid);
+							if(vvv == null) vvv = new Vector();
+							vvv.add(kkk);
+							orthoPairs[gJ][gI].put(cdsid, vvv);
+							if(cI > cdsi.length || cdsid.intValue() > cdsj.length)
+								System.err.println("bug!!");
+						}
+					}
+				}
+			}
+		}
+
+		// now that all pairwise orthology relationships have been identified,
+		// compute transitive homology
+		Vector orthologs = new Vector();
+		for(int gI = 0; gI < model.getSequenceCount(); gI++)
+		{
+			for(int gJ = 0; gJ < model.getSequenceCount(); gJ++)
+			{
+				if(gI == gJ)
+					continue;
+				while(orthoPairs[gI][gJ].size() > 0)
+				{
+					HashSet[] orthos = new HashSet[model.getSequenceCount()];
+					for(int i = 0; i < orthos.length; i++)
+						orthos[i] = new HashSet();
+					Integer key = (Integer)orthoPairs[gI][gJ].keySet().iterator().next();
+					Vector val = (Vector)orthoPairs[gI][gJ].get(key);
+					orthos[gI].add(key);
+					orthos[gJ].addAll(val);
+					orthoPairs[gI][gJ].remove(key);
+					Stack s = new Stack();
+					s.push(new Integer(gI));
+					s.push(new Integer(gJ));
+					while(!s.isEmpty())
+					{
+						// perform a depth-first-search of the ortho network for transitive orthologs
+						int cur = ((Integer)s.pop()).intValue();
+						// collect orthos for each of the other sequences
+						for(int i = 0; i < orthos.length; i++)
+						{
+							Iterator siter = orthos[cur].iterator();
+							while(siter.hasNext())
+							{
+								Object skey = siter.next();
+								Object v2 = orthoPairs[cur][i].get(skey);
+								if(v2 == null)
+									continue;
+								Vector vv = (Vector)v2;
+								orthoPairs[cur][i].remove(skey);
+								LiteWeightFeature[] cdsi = (LiteWeightFeature[])allCds.elementAt(cur);
+								LiteWeightFeature[] cdsj = (LiteWeightFeature[])allCds.elementAt(i);
+								if(((Integer)skey).intValue() > cdsi.length )
+									System.err.println("bug!!");
+								for(int x = 0; x < vv.size(); x++)
+									if(((Integer)vv.elementAt(x)).intValue() > cdsj.length )
+										System.err.println("bug!!");
+								int psize = orthos[i].size();
+								orthos[i].addAll(vv);
+								if( orthos[i].size() > psize )
+									s.push(new Integer(i)); // found new orthologs, so continue transitive search
+							}
+						}
+					}
+					orthologs.add(orthos);
+				}
+			}
+		}
+				
+		// write out the ortholog table
+		StringBuilder sb = new StringBuilder();
+		for(int oI = 0; oI < orthologs.size(); oI++)
+		{
+			HashSet[] ortho = (HashSet[])orthologs.elementAt(oI);			
+			boolean first = true;
+			for(int sI = 0; sI < ortho.length; sI++)
+			{
+				Iterator iter = ortho[sI].iterator();
+				while(iter.hasNext())
+				{
+					if(!first)	sb.append("\t");
+					if(first)	first = !first;
+					int osi = ((Integer)iter.next()).intValue();
+					LiteWeightFeature[] cdsi = (LiteWeightFeature[])allCds.elementAt(sI);
+					if(osi > cdsi.length )
+						System.err.println("bug!!");
+					sb.append(sI);
+					sb.append(":");
+					sb.append(cdsi[osi].getLocus());
+					sb.append(":");
+					sb.append(cdsi[osi].getLeft());
+					sb.append("-");
+					sb.append(cdsi[osi].getRight());
+				}
+			}
+			sb.append("\n");
+		}
+		output.write(sb.toString());
+		output.flush();
+
+		// make a list of CDS that have no orthologs, since
+		// we will want to write these out too
+		Vector found = new Vector();
+		for(int sI = 0; sI < model.getSequenceCount(); sI++)
+		{
+			LiteWeightFeature[] cdsi = (LiteWeightFeature[])allCds.elementAt(sI);
+			found.add( new boolean[cdsi.length] );
+		}
+	
+		for(int oI = 0; oI < orthologs.size(); oI++)
+		{
+			HashSet[] ortho = (HashSet[])orthologs.elementAt(oI);			
+			for(int sI = 0; sI < ortho.length; sI++)
+			{
+				Iterator iter = ortho[sI].iterator();
+				while(iter.hasNext())
+				{
+					int osi = ((Integer)iter.next()).intValue();
+					boolean[] f = (boolean[])found.elementAt(sI);
+					f[osi] = true;
+				}
+			}
+		}
+		// now write out the singletons (not found)
+		sb = new StringBuilder();
+		for(int sI = 0; sI < model.getSequenceCount(); sI++)
+		{
+			boolean[] f = (boolean[])found.elementAt(sI);
+			LiteWeightFeature[] cdsi = (LiteWeightFeature[])allCds.elementAt(sI);
+			for(int fI = 0; fI < f.length; fI++)
+			{
+				if(f[fI])
+					continue;	// this one has orthologs, skip it
+				sb.append(sI);
+				sb.append(":");
+				sb.append(cdsi[fI].getLocus());
+				sb.append(":");
+				sb.append(cdsi[fI].getLeft());
+				sb.append("-");
+				sb.append(cdsi[fI].getRight());
+				sb.append("\n");
+			}
+		}
+		output.write(sb.toString());
+		output.flush();
+		
+		// if requested to write a file of alignments then do that now
+		if(oep.alignmentOutputFile!=null){
+			BufferedWriter xmfaout = new BufferedWriter(new FileWriter(oep.alignmentOutputFile));
+			for(int oI = 0; oI < orthologs.size(); oI++)
+			{
+				HashSet[] ortho = (HashSet[])orthologs.elementAt(oI);			
+				// for each sequence, construct the maximal coordinate range then select
+				// the maximal alignment column range among all sequences?
+				long maxalnRange = -1;
+				int maxalnSeq = -1;
+				long maxalnLeft = -1;
+				long maxalnRight = -1;
+				long[] seqlefts = new long[ortho.length];
+				long[] seqrights = new long[ortho.length];
+				for(int sI = 0; sI < ortho.length; sI++)
+				{
+					long mincoord = 0;
+					long maxcoord = 0;
+					Iterator iter = ortho[sI].iterator();
+					while(iter.hasNext())
+					{
+						int osi = ((Integer)iter.next()).intValue();
+						LiteWeightFeature[] cdsi = (LiteWeightFeature[])allCds.elementAt(sI);
+						if(mincoord==0||mincoord>cdsi[osi].getLeft())	mincoord=cdsi[osi].getLeft();
+						if(maxcoord==0||maxcoord<cdsi[osi].getRight())	maxcoord=cdsi[osi].getRight();
+					}
+					seqlefts[sI]=mincoord;
+					seqrights[sI]=maxcoord;
+					Object[] aln = model.getXmfa().getRange(model.getGenomeBySourceIndex(sI), mincoord, maxcoord);
+					if(((byte[])aln[sI]).length > maxalnRange)
+					{
+						maxalnSeq=sI;
+						maxalnRange=((byte[])aln[sI]).length;
+						maxalnLeft = mincoord;
+						maxalnRight = maxcoord;
+					}
+				}
+				Object[] aln = model.getXmfa().getRange(model.getGenomeBySourceIndex(maxalnSeq), maxalnLeft, maxalnRight);
+				StringBuilder xmfaEntry = new StringBuilder();
+				for(int sI = 0; sI < ortho.length; sI++)
+				{
+					xmfaEntry.append(">");
+					xmfaEntry.append(sI);
+					Iterator iter = ortho[sI].iterator();
+					boolean first = true;
+					while(iter.hasNext())
+					{
+						if(first)
+						{
+							xmfaEntry.append(":");
+							xmfaEntry.append(seqlefts[sI]);
+							xmfaEntry.append("-");
+							xmfaEntry.append(seqrights[sI]);
+							xmfaEntry.append(":");
+							first = false;
+						}
+						else
+							xmfaEntry.append(",");
+						int osi = ((Integer)iter.next()).intValue();
+						LiteWeightFeature[] cdsi = (LiteWeightFeature[])allCds.elementAt(sI);
+						xmfaEntry.append(cdsi[osi].getLocus());
+						xmfaEntry.append(" ");
+						xmfaEntry.append(!cdsi[osi].isReverse() ? "+" : "-");
+					}
+					xmfaEntry.append("\n");
+					xmfaEntry.append(format80(new String((byte[])aln[sI])));
+				}
+				xmfaEntry.append("=\n");
+				xmfaout.write(xmfaEntry.toString());
+			}
+			xmfaout.flush();
+		}
+	}
+	
+	/* reformats a string to be 80 column width */
+	private static String format80(String sequence)
+	{
+		StringBuilder sb = new StringBuilder();
+		sequence = sequence.replaceAll("[\\r\\n]", "");
+		for(int i=0; i<sequence.length(); i+=80)
+		{
+			int j = i+80 < sequence.length() ? i+80 : sequence.length();
+			sb.append(sequence.substring(i,j));
+			sb.append("\n");
+		}
+		return sb.toString();
+	}
+	
+	public static void main(String[] args){
+		OrthologExportParameters oep = new OrthologExportParameters();
+		Options opts = new Options();
+		opts.addOption( OptionBuilder
+			.withArgName("alignment file")
+			.hasArg()
+			.withDescription("A required parameter specifying an XMFA format file generated by progressiveMauve")
+			.isRequired()
+			.create('f')
+			);
+
+		opts.addOption( OptionBuilder
+				.withArgName("minimum nucleotide identity")
+				.hasArg()
+				.withDescription("Set the minimum nucleotide identity for homologs, ranges between [0,1], default " + oep.min_nucleotide_id)
+				.create('n')
+				);
+
+		opts.addOption( OptionBuilder
+				.withArgName("maximum nucleotide identity")
+				.hasArg()
+				.withDescription("Set the maximum nucleotide identity for homologs, ranges between [0,1], default " + oep.max_nucleotide_id)
+				.create('N')
+				);
+		
+		opts.addOption( OptionBuilder
+				.withArgName("minimum conserved length")
+				.hasArg()
+				.withDescription("Set the minimum fraction of the feature that must be conserved to consider it a homolog, ranges in [0.5,1], default " + oep.min_conserved_length)
+				.create('l')
+				);
+
+		opts.addOption( OptionBuilder
+				.withArgName("maximum conserved length")
+				.hasArg()
+				.withDescription("Set the maximum fraction of the feature that must be conserved to consider it a homolog, ranges in [0.5,1], default " + oep.max_conserved_length)
+				.create('L')
+				);
+
+		opts.addOption( OptionBuilder
+				.withArgName("feature type")
+				.hasArg()
+				.withDescription("Set the type of feature to use, possibilities include CDS, rRNA, tRNA, misc_RNA, default " + oep.featureType)
+				.create('t')
+				);
+
+/*		opts.addOption( OptionBuilder
+				.withArgName("predict unannotated")
+				.withDescription("Set this to include unannotated regions that meet the criteria for homology")
+				.create('p')
+				);
+*/
+		opts.addOption( OptionBuilder
+				.withArgName("output alignments")
+				.withDescription("Should alignments of each region be generated?")
+				.create('a')
+				);
+		
+		opts.addOption( OptionBuilder
+				.withArgName("output file name")
+				.hasArg()
+				.withDescription("The name of the output file for the homologs")
+				.isRequired()
+				.create('o')
+				);
+
+		CommandLineParser parser = new GnuParser();
+		CommandLine line=null;
+		try{
+			line = parser.parse( opts, args );
+		}catch(org.apache.commons.cli.ParseException pe){
+			HelpFormatter formatter = new HelpFormatter();
+			formatter.printHelp( 
+					"OneToOneOrthologExporter -f <XMFA alignment input> -o <ortholog output> [other options]", 
+					"The parameters -f and -o are required.  Other parameters include:",
+					opts,
+					"\nExample:\nOneToOneOrthologExporter -f my_aln.xmfa -o my_orthologs -n 0.6 -N 0.8 -a\n \n ");
+			
+			throw new RuntimeException("There was an error parsing your command-line options.  Please check them and try again.");
+		}
+		
+		String alignment_xmfa = line.getOptionValue('f');
+		String output_file = line.getOptionValue('o');
+		if(line.hasOption('n')){
+			oep.min_nucleotide_id = Float.parseFloat(line.getOptionValue('n'));
+		}
+		if(line.hasOption('N')){
+			oep.max_nucleotide_id = Float.parseFloat(line.getOptionValue('N'));
+		}
+		if(line.hasOption('l')){
+			oep.min_conserved_length = Float.parseFloat(line.getOptionValue('l'));
+		}
+		if(line.hasOption('L')){
+			oep.max_conserved_length = Float.parseFloat(line.getOptionValue('L'));
+		}
+		if(line.hasOption('t')){
+			oep.featureType = line.getOptionValue('t');
+		}
+		if(line.hasOption('p')){
+			oep.predictUnannotated = true;
+		}else
+			oep.predictUnannotated = false;
+		if(line.hasOption('a')){
+			oep.alignmentOutputFile = new File(output_file + ".alignments");
+		}
+		
+		File xmfa_file = new File(alignment_xmfa);
+		BaseViewerModel bvm = null;
+		try{
+			bvm = org.gel.mauve.ModelBuilder.buildModel(xmfa_file, null);
+		}catch(Exception mfe){
+			mfe.printStackTrace();
+			throw new RuntimeException("Error reading input alignment file.");
+		}
+		if(!(bvm instanceof XmfaViewerModel)){
+			throw new RuntimeException("Error, alignment file must be in XMFA format");
+		}
+
+		BufferedWriter bw = null;
+		try{
+			bw = new BufferedWriter( new FileWriter(output_file));
+		}catch(Exception e){
+			throw new RuntimeException("Error opening output file " + output_file + " for writing.");
+		}
+		try{
+			export((XmfaViewerModel)bvm, bw, oep);
+		}catch(Exception e){
+			e.printStackTrace();
+			throw new RuntimeException("Error creating ortholog output.");			
+		}
+	}
+
+	static public class ExportFrame extends JFrame
+	{
+	    private final static DecimalFormat FORMAT = new DecimalFormat("###");
+	    
+	    private RearrangementPanel rrpanel;
+	    private JTextField outputFile = new JTextField();
+	    private JFileChooser fc = new JFileChooser();
+	    private JComboBox featureTypeSelector = new JComboBox();
+
+	    private JFormattedTextField minIdentityBox = new JFormattedTextField(FORMAT);
+	    private JFormattedTextField maxIdentityBox = new JFormattedTextField(FORMAT);
+	    private JFormattedTextField minCoverageBox = new JFormattedTextField(FORMAT);
+	    private JFormattedTextField maxCoverageBox = new JFormattedTextField(FORMAT);
+	    private JCheckBox writeAlignmentFileBox = new JCheckBox();
+	    private JCheckBox visibleGenomesBox = new JCheckBox();
+
+	    private OrthologExportParameters oep = new OrthologExportParameters();
+	    
+	    XmfaViewerModel model;
+
+	    public ExportFrame(XmfaViewerModel model)
+	    {
+	    	this.model = model;
+	        setSize(400,225);
+	        
+	        getContentPane().setLayout(new GridBagLayout());
+	        GridBagConstraints c = new GridBagConstraints();
+
+	        setTitle("Mauve Ortholog Export");
+
+	        c.insets = new Insets(2,2,2,2);
+
+	        // Format label.
+	        c.gridx = 0;
+	        c.gridy = 0;
+	        c.gridwidth = 1;
+	        c.anchor = GridBagConstraints.EAST;
+	        c.fill = GridBagConstraints.NONE;
+	        getContentPane().add(new JLabel("Features:"), c);
+	        
+	        // Format selector.
+	        c.gridx = 1;
+	        c.gridy = 0;
+	        c.gridwidth = 2;
+	        c.anchor = GridBagConstraints.WEST;
+	        c.fill = GridBagConstraints.HORIZONTAL;
+	        featureTypeSelector.addItem("CDS");
+	        featureTypeSelector.addItem("gene");
+	        featureTypeSelector.addItem("rRNA");
+	        featureTypeSelector.addItem("misc_RNA");
+	        featureTypeSelector.addItem("tRNA");
+	        getContentPane().add(featureTypeSelector, c);
+
+	        // Percent identity label.
+	        c.gridx = 0;
+	        c.gridy = 1;
+	        c.gridwidth = 1;
+	        c.weighty = 1;
+	        c.anchor = GridBagConstraints.EAST;
+	        c.fill = GridBagConstraints.NONE;
+	        getContentPane().add(new JLabel("Identity:"), c);
+
+	        // Image size boxes.
+	        JPanel scalePanel = new JPanel();
+	        scalePanel.setLayout(new GridBagLayout());
+	        GridBagConstraints c2 = new GridBagConstraints();
+	        
+	        // Min ID
+	        c2.gridx = 0;
+	        c2.gridy = 0;
+	        c2.anchor = GridBagConstraints.EAST;
+	        scalePanel.add(new JLabel("Min:"), c2);
+	        
+	        // Min ID box
+	        c2.gridx = 1;
+	        c2.insets = new Insets(0,0,0,4);
+	        c2.anchor = GridBagConstraints.WEST;
+	        scalePanel.add(minIdentityBox,c2);
+	        minIdentityBox.setValue(new Long(Math.round((oep.min_nucleotide_id*100.0))));
+	        minIdentityBox.setColumns(3);
+
+	        // Max ID label
+	        c2.gridx = 2;
+	        c2.anchor = GridBagConstraints.EAST;
+	        c2.insets = new Insets(0,0,0,0);
+	        scalePanel.add(new JLabel("Max:"),c2);
+
+	        // Max ID box
+	        c2.gridx = 3;
+	        c2.anchor = GridBagConstraints.WEST;
+	        scalePanel.add(maxIdentityBox,c2);
+	        maxIdentityBox.setValue(new Long(Math.round((oep.max_nucleotide_id*100.0))));
+	        maxIdentityBox.setColumns(3);
+
+	        JPanel scalePanel2 = new JPanel();
+	        scalePanel2.setLayout(new GridBagLayout());
+	        c2 = new GridBagConstraints();
+
+	        // Min Coverage
+	        c2.gridx = 0;
+	        c2.gridy = 0;
+	        c2.anchor = GridBagConstraints.EAST;
+	        scalePanel2.add(new JLabel("Min:"), c2);
+	        
+	        // Min Coverage box
+	        c2.gridx = 1;
+	        c2.insets = new Insets(0,0,0,4);
+	        c2.anchor = GridBagConstraints.WEST;
+	        scalePanel2.add(minCoverageBox,c2);
+	        minCoverageBox.setValue(new Long(Math.round((oep.min_conserved_length*100.0))));
+	        minCoverageBox.setColumns(3);
+
+	        // Max Coverage label
+	        c2.gridx = 2;
+	        c2.anchor = GridBagConstraints.EAST;
+	        c2.insets = new Insets(0,0,0,0);
+	        scalePanel2.add(new JLabel("Max:"),c2);
+
+	        // Max Coverage box
+	        c2.gridx = 3;
+	        c2.anchor = GridBagConstraints.WEST;
+	        scalePanel2.add(maxCoverageBox,c2);
+	        maxCoverageBox.setValue(new Long(Math.round((oep.max_conserved_length*100.0))));
+	        maxCoverageBox.setColumns(3);
+
+	        // Adding scale panel.
+	        c.gridx = 1;
+	        c.gridy = 1;
+	        c.gridwidth = 2;
+	        c.anchor = GridBagConstraints.WEST;
+	        getContentPane().add(scalePanel, c);
+
+	        
+	        // Adding coverage label.
+	        c.gridx = 0;
+	        c.gridy = 2;
+	        c.gridwidth = 1;
+	        c.weighty = 1;
+	        c.anchor = GridBagConstraints.EAST;
+	        getContentPane().add(new JLabel("Coverage:"), c);
+
+	        // Adding scale panel 2
+	        c.gridx = 1;
+	        c.gridy = 2;
+	        c.gridwidth = 2;
+	        c.anchor = GridBagConstraints.WEST;
+	        getContentPane().add(scalePanel2, c);
+
+
+	        // checkboxes.
+	        c.gridx = 0;
+	        c.gridy = 3;
+	        c.gridwidth = 3;
+	        c.weighty = 0;
+	        c.anchor = GridBagConstraints.WEST;
+	        c.fill = GridBagConstraints.NONE;
+
+		    writeAlignmentFileBox.setText("Create ortholog alignment file");
+		    writeAlignmentFileBox.setSelected(true);
+	        getContentPane().add(writeAlignmentFileBox, c);
+
+/*	        c.gridy = 4;
+	        visibleGenomesBox.setText("Visible genomes only");
+	        visibleGenomesBox.setToolTipText("Export orthologs only for non-collapsed genomes");
+	        visibleGenomesBox.setSelected(false);
+	        getContentPane().add(visibleGenomesBox, c);	        
+*/        
+	        // File label.
+	        c.gridx = 0;
+	        c.gridy = 5;
+	        c.gridwidth = 1;
+	        c.fill = GridBagConstraints.NONE;
+	        c.anchor = GridBagConstraints.SOUTHEAST;
+	        c.weighty = 0;
+	        getContentPane().add(new JLabel("Output file:"), c);
+	        
+	        // File text box
+	        c.gridx = 1;
+	        c.gridy = 5;
+	        c.gridwidth = 1;
+	        c.weighty = 1;
+	        c.fill = GridBagConstraints.HORIZONTAL;
+	        c.anchor = GridBagConstraints.SOUTHWEST;
+	        c.weightx = 1;
+	        getContentPane().add(outputFile, c);
+	        
+	        // File browse button.
+	        JButton fileButton = new JButton("Browse...");
+	        fileButton.addActionListener(new ActionListener()
+	                {
+
+	                    public void actionPerformed(ActionEvent e)
+	                    {
+	                        int ret = fc.showDialog(ExportFrame.this, "Select");
+	                        if (ret == JFileChooser.APPROVE_OPTION)
+	                        {
+	                            File f = fc.getSelectedFile();
+	                            outputFile.setText(f.getAbsolutePath());
+	                        }
+	                    }
+	                }
+	        );
+	        c.gridx = 2;
+	        c.gridy = 5;
+	        c.gridwidth = 1;
+	        c.fill = GridBagConstraints.NONE;
+	        c.anchor = GridBagConstraints.SOUTHWEST;
+	        c.weightx = 0;
+	        getContentPane().add(fileButton, c);
+	        
+	        // Export button.
+	        JPanel buttonPanel = new JPanel();
+	        
+	        JButton exportButton = new JButton("Export");
+	        exportButton.addActionListener(new ActionListener()
+	                {
+	        
+	                    public void actionPerformed(ActionEvent e)
+	                    {
+	                        doExport();
+	                    }
+	            
+	                }
+	        );
+	        
+	        buttonPanel.add(exportButton);
+	        
+	        JButton cancelButton = new JButton("Cancel");
+	        cancelButton.addActionListener(new ActionListener()
+	                {
+	        
+	                    public void actionPerformed(ActionEvent e)
+	                    {
+	                        setVisible(false);
+	                    }
+	                }
+	        );
+	        
+	        buttonPanel.add(cancelButton);
+
+	        c.gridx = 0;
+	        c.gridy = 6;
+	        c.gridwidth = 2;
+	        c.weighty = 0;
+	        c.fill = GridBagConstraints.NONE;
+	        c.anchor = GridBagConstraints.CENTER;
+
+	        JLabel orthoLab = new JLabel("<html>Note: ortholog export may<br>be very time-consuming");
+	        orthoLab.setForeground(Color.gray);
+	        getContentPane().add(orthoLab, c);
+	        
+	        c.gridx = 2;
+	        c.gridy = 6;
+	        c.gridwidth = 2;
+	        c.weighty = 0;
+	        c.fill = GridBagConstraints.NONE;
+	        c.anchor = GridBagConstraints.SOUTHEAST;
+
+	        getContentPane().add(buttonPanel, c);
+	        this.setVisible(true);
+	    }
+	    
+	    private void doExport()
+	    {
+	    	File f = new File(outputFile.getText());
+	        if (f.exists())
+	        {
+	            int result = JOptionPane.showConfirmDialog(this, "The file " + outputFile.getText() + " already exists.  Overwrite?", "File exists", JOptionPane.YES_NO_OPTION);
+	            if (result == JOptionPane.NO_OPTION)
+	            {
+	                return;
+	            }
+	        }
+	        oep.featureType = (String)featureTypeSelector.getSelectedItem();
+	        oep.min_nucleotide_id = Float.parseFloat(this.minIdentityBox.getText()) / 100f;
+	        oep.max_nucleotide_id = Float.parseFloat(this.maxIdentityBox.getText()) / 100f;
+	        oep.min_conserved_length = Float.parseFloat(this.minCoverageBox.getText()) / 100f;
+	        oep.max_conserved_length = Float.parseFloat(this.maxCoverageBox.getText()) / 100f;
+	        String errorMessage = null;
+	        if(oep.min_nucleotide_id>=oep.max_nucleotide_id)
+	        	errorMessage = "Min. pairwise nucleotide identity must be less than max.";
+	        if(oep.min_conserved_length>=oep.max_conserved_length)
+	        	errorMessage = "Min. pairwise alignment coverage must be less than max.";
+	        if(oep.min_nucleotide_id < 0 || oep.max_nucleotide_id > 1.0)
+	        	errorMessage = "Pairwise nucleotide identity must be in the range of 0 to 100.";
+	        if(oep.min_conserved_length <= 0 || oep.max_conserved_length > 1.0)
+	        	errorMessage = "Pairwise alignment coverage must be in the range of 1 to 100.";
+	        if(oep.min_conserved_length <= 0.5 || oep.max_conserved_length > 1.0)
+	        {
+	        	JOptionPane.showMessageDialog(this, "<html>Setting the minimum conserved length below 51% can yield undesirable<br/> long chains of homolog groups when annotated features overlap.<br/><b>Proceed with caution!", "Warning!",JOptionPane.WARNING_MESSAGE);
+	        }
+	        if(errorMessage != null)
+	        {
+	        	JOptionPane.showMessageDialog(this, errorMessage, "Error",JOptionPane.ERROR_MESSAGE);
+	        	return;
+	        }
+	        try{
+		        if(this.writeAlignmentFileBox.isSelected())
+		        	oep.alignmentOutputFile = new File(f.getCanonicalPath() + ".alignments");
+	        	BufferedWriter bw = new BufferedWriter( new FileWriter(f));
+		        OneToOneOrthologExporter.export(model, bw, oep);
+		        setVisible(false);
+	        }catch(IOException ioe){
+	        	ioe.printStackTrace();
+	        }
+	    }
+	}
+}
diff --git a/src/org/gel/mauve/analysis/PermutationExporter.java b/src/org/gel/mauve/analysis/PermutationExporter.java
new file mode 100644
index 0000000..543b6ea
--- /dev/null
+++ b/src/org/gel/mauve/analysis/PermutationExporter.java
@@ -0,0 +1,696 @@
+package org.gel.mauve.analysis;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.LCBlist;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.XmfaViewerModel;
+	
+public class PermutationExporter {
+
+	public static final char LCB_BND = 'l';
+	public static final char CTG_BND = 'c';
+	
+	private static Map<String,Integer> sharedBoundaryCounts = new HashMap<String, Integer>();
+	
+	public static LCB[] getSplitLCBs(XmfaViewerModel model){
+		Genome[] genomes = model.getGenomes().toArray(new Genome[model.getGenomes().size()]);
+		LCB[] lcbList = projectLcbList(model, model.getVisibleLcbList(),genomes);
+		lcbList = splitLcbList(model, lcbList, genomes);
+		return lcbList;
+	}
+	
+	/**
+	 * Computes a refined set of LCBs, returning an array containing only those
+	 * LCBs present in all of <code>genomes</code>. The return set of LCBs may contain
+	 * more LCBs than present in the alignment, as LCBs are split based on contig boundaries
+	 * 
+	 * 
+	 * @param model a model containing an alignment 
+	 * @param lcbList an array of LCBs
+	 * @param genomes the genomes to work with
+	 * @return an array of <code>LCB</code>s 
+	 */
+	@SuppressWarnings("unchecked")
+	public static LCB[] projectLcbList(XmfaViewerModel model, LCB[] lcbList, Genome[] genomes)
+	{
+		if (!sharedBoundaryCounts.containsKey(model.getSrc().getAbsolutePath()))
+			sharedBoundaryCounts.put(model.getSrc().getAbsolutePath(), 0);
+		//System.err.println(model.getSrc().getAbsolutePath());
+		// make a list of undesired genomes
+		Genome[] others = new Genome[model.getSequenceCount()-genomes.length];
+		int k=0;
+		// iterate over all genomes
+		for(int i=0; i<model.getSequenceCount(); i++)
+		{
+			int j=0;
+			for(; j<genomes.length; j++)
+				if(model.getGenomes().elementAt(i) == genomes[j])
+					break;
+			if(j==genomes.length)
+				others[k++]=(Genome)model.getGenomes().elementAt(i);
+		}
+		
+		// genomes = the genomes to export 
+		// others =  the genomes not to export
+
+		Vector projlcbs = new Vector();
+		
+		for(int i=0; i<lcbList.length; i++)
+		{
+			LCB lcb = lcbList[i];
+			// check to see if all the genomes we're interested are in this LCB
+			int g=0;
+			for(; g<genomes.length; g++)
+				if(lcb.getLeftEnd(genomes[g])==0)
+					break;
+			if(g<genomes.length)
+				continue;
+			// create a copy of the current LCB
+			LCB newlcb = new LCB(lcb);
+			// remove the genomes we're not interested in from this LCB
+			for(int o=0; o<others.length; o++){
+				newlcb.setLeftEnd(others[o], 0);
+				newlcb.setRightEnd(others[o], 0);
+				newlcb.setReverse(others[o], false);
+			}
+			
+			projlcbs.add(newlcb);
+		}
+		
+		LCB[] plist = new LCB[projlcbs.size()];
+		projlcbs.toArray(plist);
+		// now that we've got all the LCBs we need, compute adjacencies
+		LCBlist.computeLCBAdjacencies(plist, model);
+		return plist;
+	}
+	
+	public static LCB[] splitLcbList(XmfaViewerModel model, LCB[] lcbList, Genome[] genomes){
+		HashMap<Integer, Set<Long>> break_map = new HashMap<Integer, Set<Long>>();
+		// for each LCB, accumulate the breakpoint column coordinates across all genomes for the LCB
+		for (int g = 0; g < genomes.length; g++){
+			List chrom = genomes[g].getChromosomes();
+			Iterator it = chrom.iterator();				
+			while(it.hasNext()){
+				Chromosome chr = (Chromosome) it.next();
+				long[] chrbnds = new long[2];
+				chrbnds[0] = chr.getStart();
+				chrbnds[1] = chr.getEnd();
+				for(long chrb:chrbnds){
+					long[] splitBnd = model.getLCBAndColumn(genomes[g], chrb);
+					Integer inty = new Integer((int)(splitBnd[0]));
+					if(break_map.get(inty) == null){
+						break_map.put(inty, new TreeSet<Long>());
+					}
+					break_map.get(inty).add(splitBnd[1]);
+				}
+			}
+		}
+		
+		// now break up each LCB on the identified columns
+		Vector<LCB> projlcbs = new Vector<LCB>();
+		for(LCB lcb: lcbList){
+			Set<Long> breakset = break_map.get(new Integer(lcb.id));
+			if(breakset == null) continue;
+			LCB lcbCopy = new LCB(lcb);
+			long prev = 0;
+			for(Long bp: breakset){
+				LCB lcbCrop = cropLeft(model, genomes, lcbCopy, bp.longValue() - prev);
+				projlcbs.add(lcbCrop);
+				prev = bp.longValue();
+			}
+			projlcbs.add(lcbCopy);
+		}
+		
+		LCB[] plist = new LCB[projlcbs.size()];
+		projlcbs.toArray(plist);
+		// now that we've got all the LCBs we need, compute adjacencies
+		LCBlist.computeLCBAdjacencies(plist, model);
+		return plist;		
+	}
+		
+	/**
+	 * Returns the number of shared boundaries between contigs/chromosomes and LCB 
+	 * @param model XmfaViewerModel to get this info for
+	 * @return the number of shared boundaries between contigs/chromosomes and LCBs if 
+	 * 			this number has been calculated already, -1 otherwise
+	 */
+	public static int getSharedBoundaryCount(XmfaViewerModel model){
+		Genome[] genomes = new Genome[model.getGenomes().size()];
+		model.getGenomes().toArray(genomes);
+		LCB[] lcbs = model.getFullLcbList();
+		return getSharedBoundaryCount(model, lcbs, genomes);		
+	}
+	
+	public static int getSharedBoundaryCount(XmfaViewerModel model, LCB[] lcbs, Genome[] genomes){
+		int ret = 0;
+		for (Genome g: genomes){
+			Vector<Long> bndryPtList = new Vector<Long>();
+			loadChromosomeBoundaries(bndryPtList, g);
+			loadLcbBoundaries(bndryPtList, g, lcbs);
+			ret += sortAndCount(bndryPtList);
+		}
+		return ret;
+	}
+	
+	private static void loadChromosomeBoundaries(Vector<Long> bndryPts, Genome g){
+		Chromosome chr = null;
+		Iterator<Chromosome> it = g.getChromosomes().iterator();
+		while(it.hasNext()){
+			chr = it.next();
+			bndryPts.add(chr.getStart());
+			bndryPts.add(chr.getEnd());
+		}
+	}
+	
+	private static void loadLcbBoundaries(Vector<Long> bndryPts, Genome g, LCB[] lcbs){
+		for (LCB lcb: lcbs){
+			bndryPts.add(lcb.getLeftEnd(g));
+			bndryPts.add(lcb.getRightEnd(g));
+		}
+	}
+	
+	private static int sortAndCount(Vector<Long> list){
+		Long[] ar = new Long[list.size()];
+		list.toArray(ar);
+		Arrays.sort(ar);
+		int ret = 0;
+//		int[] dif = new int[ar.length-1];
+		for (int i = 1; i < ar.length; i++){
+			if (ar[i] == ar[i-1])
+				ret++;
+//			dif[i-1] = (int) (ar[i] - ar[i-1]);
+		}
+		return ret;
+	}
+	
+	/** 
+	 * Trims off everything to the left of and including col from lcb
+	 * and returns the removed segment as an LCB object.
+	 * 
+	 * col must be less than or equal to LCB length
+	 * 
+	 * @param model the overlying XmfaViewerModel
+	 * @param genomes the genomes of interest
+	 * @param lcb the LCB to be trimmed
+	 * @param col the alignment column to trim up to
+	 * @return the LCB trimmed off 
+	 */
+	/* FIXME - off by one error - read FIXME statement below. 
+	 * Turn on error messages below and run Permutation Exporter or DCJ Analysis for example */
+	public static LCB cropLeft (XmfaViewerModel model, Genome[] genomes, LCB lcb, long col){	
+
+		long lcbLen = model.getXmfa().getLcbLength(lcb.id);
+		if (col > lcbLen)
+			throw new IllegalArgumentException("col == " + col + " : greater than LCB length (length == " + lcbLen+")");
+		long[] start_coords = new long[model.getSequenceCount()];
+		long[] end_coords = new long[model.getSequenceCount()];
+		boolean[] gapStart = new boolean[model.getSequenceCount()];
+		boolean[] gapEnd = new boolean[model.getSequenceCount()];
+		
+		// Create a copy of the LCB we're trimming. Then shift the copy's 
+		// right coordinates left to the split boundary and the original's 
+		// left coordinates right to the split boundary.
+		
+		LCB newLCB = new LCB(lcb);
+			// Now we need to set new end coordinates for   
+			// newLCB and new start coordinates for lcb
+		// Get end coordinates for newLCB...
+		model.getColumnCoordinates(lcb.id, col, end_coords, gapEnd);
+		// Get start coordinates for lcb...
+		if (col < lcbLen)
+			model.getColumnCoordinates(lcb.id, col+1, start_coords, gapStart);
+		else { // col == lcbLen
+			return lcb;
+		}
+		
+		for (int j = 0; j < genomes.length; j++){ 
+			int srcIdx = genomes[j].getSourceIndex();
+			Genome g = genomes[j];
+			
+			if (lcb.getReverse(genomes[j])){
+			
+			//	if (!gapEnd[srcIdx])
+					newLCB.setLeftEnd(genomes[j], end_coords[srcIdx]);
+			//	if (!gapStart[srcIdx])
+					lcb.setRightEnd(genomes[j], start_coords[srcIdx]);
+			} else {
+				
+			//	if (!gapEnd[srcIdx])
+					newLCB.setRightEnd(genomes[j], end_coords[srcIdx]);
+			//	if (!gapStart[srcIdx])
+					lcb.setLeftEnd(genomes[j], start_coords[srcIdx]);
+			}
+			System.err.flush();
+		}
+		return newLCB;
+		
+	}
+	
+	/**
+	 * Computes the signed permutations for each genome in <code>genomes</code>. 
+	 * Returns an array of <code>Vector</code>s (one per genome) of
+	 * <code>Vector</code>s (one per chromosome) of <code>String</code>s (one per block). 
+	 * 
+	 * @param model the model containing the alignment
+	 * @param genomes the genomes to compute permutations for
+	 * @return an array of <code>Vector</code>s of <code>Vector</code>s of <code>String</code>s
+	 */
+	//@SuppressWarnings("unchecked")
+	//@SuppressWarnings("unchecked")
+	public static Vector[] computeSignedPermutation(XmfaViewerModel model, Genome[] genomes, boolean splitOnCtgBnds)
+	{
+		int seq_count = genomes.length;
+		LCB[] lcbList = null;
+		if (splitOnCtgBnds){
+			lcbList = model.getSplitLcbList();
+		} else {
+			lcbList = model.getVisibleLcbList();
+		}
+		// Filter out LCBs that don't pertain to our set of genomes.
+		// LCB splitting by contig boundaries has already been done for us, 
+		// so no need to split again. 
+		lcbList = projectLcbList(model, lcbList, genomes);
+		Vector[] signed_perms = new Vector[seq_count];
+		
+
+		for (int seqI = 0; seqI < seq_count; seqI++)
+			signed_perms[seqI] = new Vector();
+
+		// First, construct a matrix of chromosome lengths.
+		// which means we have to compute the highest number of chromosomes
+		int max_chr_count = 0;
+		for (int seqI = 0; seqI < seq_count; seqI++) {
+			int cur_count = genomes[seqI].getChromosomes ().size ();
+			max_chr_count = cur_count > max_chr_count ? cur_count
+					: max_chr_count;
+		}
+		
+		long [][] chr_end = new long [seq_count] [max_chr_count];
+		for (int seqI = 0; seqI < seq_count; seqI++) {
+			List chromo = genomes[seqI].getChromosomes (); 
+			// get all chromosomes, and store their lengths
+			for (int chrI = 0; chrI < chromo.size (); chrI++) {
+				chr_end[seqI][chrI] = ((Chromosome) chromo.get (chrI)).getEnd();
+			}
+		}
+		
+	
+		for (int seqI = 0; seqI < seq_count; seqI++) {  
+			Genome g = genomes[seqI];				// for each genome
+			//List chromo = g.getChromosomes ();
+			int leftmost_lcb = 0;         // 
+			for (; leftmost_lcb < lcbList.length; leftmost_lcb++)
+				if (lcbList[leftmost_lcb].getLeftAdjacency (g) == LCBlist.ENDPOINT)  // if  
+					break;
+			int adjI = leftmost_lcb;
+			int cur_chromosome = 0;
+			signed_perms[seqI].add(new Vector());	// initialize a new chromosome
+			Vector cur_chromo = (Vector)signed_perms[seqI].lastElement();
+		//	boolean stop = false;
+			while (adjI != LCBlist.ENDPOINT && adjI != LCBlist.REMOVED && adjI < lcbList.length) {
+				long l = lcbList[adjI].getLeftEnd (g);
+				long r = lcbList[adjI].getRightEnd(g);
+				while (chr_end[seqI][cur_chromosome] < l) {
+					// add new vector for a new chromosome
+					signed_perms[seqI].add(new Vector());
+					cur_chromo = (Vector)signed_perms[seqI].lastElement();
+					cur_chromosome++;
+				} 
+				if (lcbList[adjI].getReverse (g)) {
+					cur_chromo.add(new Integer(-(adjI + 1)));
+				}else{
+					cur_chromo.add(new Integer(adjI + 1));
+				}
+				
+				adjI = lcbList[adjI].getRightAdjacency (g);
+			}
+			
+		}
+		return signed_perms;
+	}
+	
+	/**
+	 * 
+	 * @param model
+	 * @param output
+	 * @param genomes
+	 * @throws IOException
+	 */
+	public static void export( XmfaViewerModel model, BufferedWriter output, Genome[] genomes ) throws IOException
+	{
+		String[] perms = getPermStrings(model,genomes, false);
+		
+		for (int i = 0; i < perms.length; i++){
+			output.write(perms[i]+"\n");
+			
+		}
+		output.flush();
+	}
+	
+	
+	/**
+	 * Returns an array of permutations corresponding to the <code>Genome</code>s passed in.
+	 * The permutation returned will apply to only those LCBs that are present in <code>genomes</code>
+	 * 
+	 * @param model XmfaViewerModel holding alignment
+	 * @param genomes the genomes of interest.
+	 * @param splitOnCtgBnds split blocks up by contig/chromosome boundaries if true
+	 * @return an array of permutations, one for each element in <code>genomes</code>
+	 */
+	@SuppressWarnings("unchecked")
+	public static String[] getPermStrings(XmfaViewerModel model, Genome[] genomes, boolean splitOnCtgBnds) 
+	{
+		// perms = an array of vectors of vectors. one array element per genome.
+		Vector[] perms = computeSignedPermutation(model, genomes, splitOnCtgBnds); 
+		StringBuilder sb = new StringBuilder();
+		
+		
+		for(int i = 0; i < perms.length; i++)
+		{
+			// perms[i] = a vector of vectors. one vector per contig 
+			for(int j = 0; j < perms[i].size(); j++)
+			{
+				// cur = a vector of "block" elements
+				Vector cur = (Vector)perms[i].elementAt(j);
+				for(int k = 0; k < cur.size(); k++)
+				{
+					if(k>0)
+						sb.append(",");
+					sb.append(cur.elementAt(k).toString());
+				}
+				if(cur.size()>0){
+					if (genomes[i].isCircular(j))
+						sb.append(MauveConstants.CIRCULAR_CHAR);
+					sb.append("$ ");
+				}
+			}
+			sb.append("\n");
+		}
+		return sb.toString().split("\n");
+	}
+	/**
+	 * Returns an array of permutations for all genomes in the alignment
+	 * 
+	 *
+	 * @param model the alignment to get permutations for
+	 * @return an array of permutations, indexed by genome source index
+	 */
+	public static String[] getPermStrings(XmfaViewerModel model, boolean splitOnCtgBnds){
+		Vector<Genome> v = model.getGenomes();
+		return getPermStrings(model, v.toArray(new Genome[v.size()]), splitOnCtgBnds);
+	}
+
+/*	public static String[] getPermStrings(XmfaViewerModel model, boolean splitOnCtgBnds){
+		Vector<Genome> v = model.getGenomes();
+		return getPermStrings(model, v.toArray(new Genome[v.size()]), splitOnCtgBnds);
+	}*/
+	
+	public static interface Boundary extends Comparable<Boundary> {
+		public long getPos();
+		
+		public static class Sequence implements Boundary {
+			final long pos;
+			final int g;
+			public Sequence (long pos){ this.pos = pos; g = -1; }
+			public Sequence (long pos, int g){ this.pos = pos; this.g = g; }
+			public int compareTo(Boundary o) {
+				if (this.pos < o.getPos()){
+					return -1;
+				} else if (this.pos > o.getPos()){
+					return 1;
+				} else return 0;
+			}
+			public long getPos() {return pos;}
+		}
+		
+		public static class Block implements Boundary {
+			final long pos;
+			final int lcb;
+			public Block (long pos, int lcb){ this.pos = pos; this.lcb = lcb;}
+			public int compareTo(Boundary o) {
+				if (this.pos < o.getPos()){
+					return -1;
+				} else if (this.pos > o.getPos()){
+					return 1;
+				} else return 0;
+			}
+			public long getPos() {return pos;}
+			public int getId() {return lcb;}
+		}
+	}
+	
+	public static int countInterBlockBounds(XmfaViewerModel model, Genome g){
+		int ret = 0;
+		Vector<Boundary> bnds = new Vector<Boundary>(); 
+		Iterator<Chromosome> it = g.getChromosomes().iterator();
+		it.next();
+		while(it.hasNext()) {
+			Chromosome tmp = it.next();
+			bnds.add(new Boundary.Sequence(tmp.getStart()));
+			//bnds.add(new Boundary.Sequence(tmp.getEnd()));
+		}
+		LCB[] lcbs = model.getFullLcbList();
+		for (LCB lcb: lcbs){
+			bnds.add(new Boundary.Block(lcb.getLeftEnd(g),lcb.id));
+			bnds.add(new Boundary.Block(lcb.getRightEnd(g),lcb.id));
+		}
+		Boundary[] ar = new Boundary[bnds.size()];
+		bnds.toArray(ar);
+		Arrays.sort(ar);
+	//	if (ar[0] instanceof Boundary.Sequence) ret++;
+		for (int i = 1; i < ar.length-1; i++){
+			Boundary l = ar[i-1];
+			Boundary m = ar[i];
+			Boundary r = ar[i+1];
+			if (l instanceof Boundary.Block && 
+				m instanceof Boundary.Sequence && 
+				r instanceof Boundary.Block) {
+				Boundary.Block bl = (Boundary.Block) l;
+				Boundary.Block br = (Boundary.Block) r;
+				if (bl.lcb != br.lcb) ret++;
+			}/* else if (l instanceof Boundary.Sequence && m instanceof Boundary.Sequence && r instanceof Boundary.Block){
+				System.out.print("");
+			} else if (r instanceof Boundary.Sequence && m instanceof Boundary.Sequence && l instanceof Boundary.Block){
+				System.out.print("");
+			}*/
+		}
+	//	if (ar[ar.length-1] instanceof Boundary.Sequence) ret++;
+		return ret;
+	}
+	
+	public static int countInterBlockBounds(XmfaViewerModel model){
+		int ret = 0;
+		Iterator<Genome> it = model.getGenomes().iterator();
+		while(it.hasNext()){
+			ret += countInterBlockBounds(model, it.next());
+		}
+		return ret;
+	}
+	
+	public static int countSharedContigBounds(XmfaViewerModel model, Genome[] genomes){
+		for (int gI = 0; gI < genomes.length; gI++){
+			for (int gJ = gI+1; gJ < genomes.length; gJ++){
+				
+			}
+		}
+		return 0;
+	}
+	
+	public static class ExportFrame extends JFrame
+	{	    
+	    private JTextField outputFile = new JTextField();
+	    private JFileChooser fc = new JFileChooser();
+	    private JComboBox formatSelector = new JComboBox();
+	    XmfaViewerModel model;
+	    
+	    public ExportFrame(XmfaViewerModel model)
+	    {
+	    	this.model = model;
+	        setSize(300,150);
+	        
+	        getContentPane().setLayout(new GridBagLayout());
+	        GridBagConstraints c = new GridBagConstraints();
+
+	        setTitle("Export Genome Arrangement");
+
+	        fc.setDialogTitle("Export permutation file to...");
+
+	        c.insets = new Insets(2,2,2,2);
+
+	        // Format label.
+	        c.gridx = 0;
+	        c.gridy = 0;
+	        c.gridwidth = 1;
+	        c.anchor = GridBagConstraints.EAST;
+	        c.fill = GridBagConstraints.NONE;
+	        getContentPane().add(new JLabel("Genomes:"), c);
+	        
+	        // Format selector.
+	        c.gridx = 1;
+	        c.gridy = 0;
+	        c.gridwidth = 2;
+	        c.anchor = GridBagConstraints.WEST;
+	        c.fill = GridBagConstraints.HORIZONTAL;
+	        formatSelector.addItem("Visible");
+	        formatSelector.addItem("All");
+	        getContentPane().add(formatSelector, c);
+	        
+	        // File label.
+	        c.gridx = 0;
+	        c.gridy = 6;
+	        c.gridwidth = 1;
+	        c.fill = GridBagConstraints.NONE;
+	        c.anchor = GridBagConstraints.SOUTHEAST;
+	        c.weighty = 0;
+	        getContentPane().add(new JLabel("Output file:"), c);
+	        
+	        // File text box
+	        c.gridx = 1;
+	        c.gridy = 6;
+	        c.gridwidth = 1;
+	        c.weighty = 1;
+	        c.fill = GridBagConstraints.HORIZONTAL;
+	        c.anchor = GridBagConstraints.SOUTHWEST;
+	        c.weightx = 1;
+	        getContentPane().add(outputFile, c);
+	        
+	        // File browse button.
+	        JButton fileButton = new JButton("Browse...");
+	        fileButton.addActionListener(new ActionListener()
+	                {
+
+	                    public void actionPerformed(ActionEvent e)
+	                    {
+	                        int ret = fc.showDialog(ExportFrame.this, "Select");
+	                        if (ret == JFileChooser.APPROVE_OPTION)
+	                        {
+	                            File f = fc.getSelectedFile();
+	                            outputFile.setText(f.getAbsolutePath());
+	                        }
+	                    }
+	                }
+	        );
+	        c.gridx = 2;
+	        c.gridy = 6;
+	        c.gridwidth = 1;
+	        c.fill = GridBagConstraints.NONE;
+	        c.anchor = GridBagConstraints.SOUTHWEST;
+	        c.weightx = 0;
+	        getContentPane().add(fileButton, c);
+	        
+	        // Export button.
+	        JPanel buttonPanel = new JPanel();
+	        
+	        JButton exportButton = new JButton("Export");
+	        exportButton.addActionListener(new ActionListener()
+	                {
+	        
+	                    public void actionPerformed(ActionEvent e)
+	                    {
+	                        doExport();
+	                    }
+	            
+	                }
+	        );
+	        
+	        buttonPanel.add(exportButton);
+	        
+	        JButton cancelButton = new JButton("Cancel");
+	        cancelButton.addActionListener(new ActionListener()
+	                {
+	        
+	                    public void actionPerformed(ActionEvent e)
+	                    {
+	                        setVisible(false);
+	                    }
+	                }
+	        );
+	        
+	        buttonPanel.add(cancelButton);
+	        
+	        c.gridx = 0;
+	        c.gridy = 7;
+	        c.gridwidth = 3;
+	        c.weighty = 0;
+	        c.fill = GridBagConstraints.NONE;
+	        c.anchor = GridBagConstraints.SOUTHEAST;
+
+	        getContentPane().add(buttonPanel, c);
+	        this.setVisible(true);
+	    }
+	    
+	    private void doExport()
+	    {
+	        String format = (String) formatSelector.getSelectedItem();
+	    	File f = new File(outputFile.getText());
+	        if (f.exists())
+	        {
+	            int result = JOptionPane.showConfirmDialog(this, "The file " + f + " already exists.  Overwrite?", "File exists", JOptionPane.YES_NO_OPTION);
+	            if (result == JOptionPane.NO_OPTION)
+	            {
+	                return;
+	            }
+	        }
+	        
+	        // create a list of desired genomes in viewing index order
+	        boolean all = format.equalsIgnoreCase("All");
+        	Vector<Genome> gg = new Vector<Genome>();
+        	for(int i=0; i<model.getSequenceCount(); i++)
+        	{
+        		if(all || model.getGenomeByViewingIndex(i).getVisible())
+        			gg.add(model.getGenomeByViewingIndex(i));
+        	}
+        	Genome[] genomes = new Genome[gg.size()];
+        	genomes = gg.toArray(genomes);
+
+        	try{
+	        	BufferedWriter bw = new BufferedWriter(new FileWriter(f));
+		        PermutationExporter.export(model, bw, genomes);
+    			bw.flush();
+    			bw.close();
+	        }catch(IOException ioe)
+	        {
+	        	ioe.printStackTrace();
+	        	System.err.println("Sorry, the selected file " + f + " could not be written\n");
+	        }
+	        setVisible(false);
+	    }
+	}
+
+	public int compareTo(Boundary arg0) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+}
diff --git a/src/org/gel/mauve/analysis/SNP.java b/src/org/gel/mauve/analysis/SNP.java
new file mode 100644
index 0000000..0d30be2
--- /dev/null
+++ b/src/org/gel/mauve/analysis/SNP.java
@@ -0,0 +1,331 @@
+package org.gel.mauve.analysis;
+
+import java.util.Comparator;
+
+import org.biojava.bio.seq.FeatureHolder;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.XmfaViewerModel;
+
+public class SNP {
+
+	private char[] pattern;
+	
+	/**
+	 * positions in each genome of the SNP. Negative values indicate
+	 * reverse strand.
+	 */
+	private long[] pos;
+	
+	private boolean[] present;
+	
+	private Chromosome[] chrom;
+	
+	private int[] posInCtg;
+	
+	private XmfaViewerModel model;
+	
+	/**
+	 * Creates a <code>SNP</code> object with <code>numTaxa</code> taxa
+	 * 
+	 * @param numTaxa the number of taxa at this SNP
+	 */
+	public SNP(XmfaViewerModel model){
+		int numTaxa = model.getSequenceCount();
+		present = new boolean[numTaxa];
+		pattern = new char[numTaxa];
+		pos = new long[numTaxa];
+		for (int i = 0; i < numTaxa; i++){
+			pattern[i] = '-';
+			pos[i] = 0;
+		}
+		this.model = model;
+		chrom = new Chromosome[numTaxa];
+		posInCtg = new int[numTaxa];
+	}
+	
+	/**
+	 * Creates a <code>SNP</code> object with the given patterns and
+	 * genome positions. 
+	 * 
+	 * @param pat the pattern at this SNP
+	 * @param pos the positions in each of the genomes
+	 */
+	private SNP(char[] pat, long[] pos, XmfaViewerModel model){
+		this.pattern = pat;
+		this.pos = pos;
+		this.model = model;
+	}
+	
+	/**
+	 * <p>
+	 * Returns a tab-delimited description of the SNP
+	 * </p><p>
+	 * SNP_pattern seq0-Contig seq0-Position_in_Contig seq0-GenomeWide_Position...
+	 * </p><p>
+	 * Example:<br/>
+	 * 		AC	contig_0087 -1658 -1235 Chromosome 1090 1090</p>
+	 * 
+	 * @return a String representation of this SNP
+	 * 		
+	 */
+	public String toString(){
+		StringBuilder sb = new StringBuilder();
+		sb.append(pattern);
+		for (int i = 0; i < pos.length; i++){
+			if (chrom[i] != null)
+				sb.append("\t"+chrom[i].getName()
+					  +"\t"+posInCtg[i]+"\t"+pos[i]);
+			else 
+				sb.append("\tnull\t"+posInCtg[i]+"\t"+pos[i]);
+		}
+		return sb.toString();
+	}
+	
+	/**
+	 * Add the base for the specified genome.
+	 * 
+	 * @param genSrcIdx the source index of the genome to be added
+	 * @param base the base to be added for genome <code>genSrcIdx</code>
+	 * @param position the position where this base is located in genome <code>genSrcIdx</code>
+	 * 
+	 * <br></br>
+	 * <br>
+	 * NOTE: we should probably have a static SNPBuilder kind of class
+	 * to avoid this function getting called where it's not supposed to be.
+	 *  </br>
+	 */
+	public void addTaxa(int genSrcIdx, char base, long position){
+		if (genSrcIdx >= pattern.length)
+			throw new IllegalArgumentException(genSrcIdx+" : source index out of bounds");
+		present[genSrcIdx] = true;
+		pattern[genSrcIdx] = base;
+		pos[genSrcIdx] = position;
+		if (position == 0) {
+			posInCtg[genSrcIdx] = 0;
+		} else {
+			chrom[genSrcIdx] =
+				model.getGenomeBySourceIndex(genSrcIdx).getChromosomeAt(position);
+			posInCtg[genSrcIdx] = (int) (position - chrom[genSrcIdx].getStart() + 1);
+		}
+		
+	}
+	
+	/**
+	 * Returns the character for genome <code>genSrcIdx</code>
+	 * 
+	 * @param genSrcIdx the source index of the genome in query
+	 * @return the character for genome <code>genSrcIdx</code> at this SNP
+	 */
+	public char getChar(int genSrcIdx){
+		if (genSrcIdx >= pattern.length)
+			throw new IllegalArgumentException(genSrcIdx+" : source index out of bounds");
+		else
+			return pattern[genSrcIdx];
+	}
+	
+	/**
+	 * Returns the contig this SNP lies on in the given genome
+	 * 
+	 * @param genSrcIdx the source index of the genome of interest
+	 * @return the contig in genome <code>genSrcIdx</code> where this SNP lies
+	 */
+	public Chromosome getContig(int genSrcIdx){
+		return model.getGenomeBySourceIndex(genSrcIdx).
+							getChromosomeAt(pos[genSrcIdx]);
+	}
+	
+	/**
+	 * Returns the position in the respective contig of this SNP
+	 * for the given genome.
+	 * 
+	 * @param genSrcIdx the source index of the genome of interest
+	 * @return the position in the contig of genome <code>genSrcIdx</code> where this SNP lies
+	 */
+	public int getPositionInContig(int genSrcIdx){
+		return (int) (pos[genSrcIdx]-getContig(genSrcIdx).getStart() + 1);
+	}
+	
+	/**
+	 * Returns the location of this SNP in genome <code>genomeSrcIdx</code>
+	 * 
+	 * @param genomeSrcIdx the source index of the genome in query 
+	 * @return the  character for genome <code>genomeSrcIdx</code> at this SNP
+	 */
+	public long getPos(int genomeSrcIdx){
+		if (genomeSrcIdx >= pattern.length)
+			throw new IllegalArgumentException(genomeSrcIdx+" : source index out of bounds");
+		else
+			return pos[genomeSrcIdx];
+	}
+	
+	/**
+	 * Returns the features in the given genome that overlap with this SNP
+	 * 
+	 * @param genSrcIdx the source index of the genome of interest
+	 * @return the features overlapping this SNP in genome <code>genSrcIdx</code>	 
+	 */
+	public FeatureHolder getFeatures(int genSrcIdx){
+		long pos = this.pos[genSrcIdx];
+		return model.getGenomeBySourceIndex(genSrcIdx).
+							getAnnotationsAt(pos, pos, pos<0?true:false);
+		
+	}
+	
+	/**
+	 * Returns true if any genomes have a gap in this SNP.
+	 * 
+	 * @return false if all genomes have a base present here, true otherwise
+	 */
+	public boolean hasGap(){
+		for (int i = 0; i < pattern.length; i++){
+			if (pattern[i] == '-')
+				return true;
+		}
+		return false;
+	}
+	
+	/**
+	 * Returns true if the specified genome has a gap in this SNP
+	 * 
+	 * @param idx the genome source index 
+	 * @return true if genome <code>idx</code> has a gap here, false otherwise
+	 */
+	public boolean hasGap(int idx){
+		return pattern[idx] == '-';
+	}
+	
+	/**
+	 * Returns true of any of the bases in the alignment
+	 * column corresponding to this SNP are ambiguity codes.
+	 * 
+	 * @return
+	 */
+	public boolean hasAmbiguities(){
+		for (int i = 0; i < pattern.length; i++){
+			char c = pattern[i];
+			switch(c){
+			case 'k': return true;
+			case 'K': return true;
+			case 'm': return true;
+			case 'M': return true;
+			case 'r': return true;
+			case 'R': return true;
+			case 'y': return true;
+			case 'Y': return true;
+			case 's': return true;
+			case 'S': return true;
+			case 'w': return true;
+			case 'W': return true;
+			case 'b': return true;
+			case 'B': return true;
+			case 'v': return true;
+			case 'V': return true;
+			case 'h': return true;
+			case 'H': return true;
+			case 'd': return true;
+			case 'D': return true;
+			case 'x': return true;
+			case 'X': return true;
+			case 'n': return true;
+			case 'N': return true;
+			case '-': return true;
+				default :
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * Returns a negative number, zero, or a positive number 
+	 * if this SNP comes before, lies within, or comes after the
+	 * given feature, respectively.
+	 * 
+	 * @param feat the feature to compare this SNP to
+	 * 
+	 * @return a negative number, zero, or a positive number
+	 */
+	public int relativePos(LiteWeightFeature feat){
+		int snpPos = (int) Math.abs(this.pos[feat.getGenSrcIdx()]);
+		if (snpPos == 0)
+			return 1;
+		boolean snpFwd = this.pos[feat.getGenSrcIdx()] > 0;
+		boolean featFwd = feat.getStrand() > 0;
+		if (featFwd == snpFwd){ // if on same strand
+			if (featFwd) {
+				if (snpPos < feat.getLeft())
+					return -1;
+				else if (snpPos <= feat.getRight())
+					return 0;
+				else 
+					return 1;
+			} else {
+				if (snpPos > feat.getRight())
+					return -1;
+				else if (snpPos >= feat.getLeft())
+					return 0;
+				else
+					return 1;
+			}
+		} else {
+			if (featFwd)
+				return -1;
+			else
+				return 1;
+		}
+	}
+	
+	/**
+	 * Returns true if genome x and genome y share the same
+	 * base in this SNP.
+	 */
+	public boolean areEqual(Genome x, Genome y){
+		return pattern[x.getSourceIndex()] == pattern[y.getSourceIndex()];
+	}
+	/**
+	 * Returns true if genome x and genome y share the same
+	 * base in this SNP.
+	 * @param x genome source index
+	 * @param y genome source index
+	 */
+	public boolean areEqual(int x, int y){
+		return pattern[x] == pattern[y];
+	}
+	
+	public static Comparator<SNP> getLoopingComparator(final int genSrcIdx){
+		return new Comparator<SNP>(){
+			private int idx = genSrcIdx;
+			public int compare(SNP o1, SNP o2) {
+				if (o1.pos[idx] == 0 && o2.pos[idx] == 0) {
+					return 0;
+				} else {
+					if ((o1.pos[idx] >= 0) == (o2.pos[idx] >= 0)){ // on same strand
+						if (o1.pos[idx] > 0) { // on forward strand
+							if (o1.pos[idx] > o2.pos[idx]) {
+								return 1;
+							} else if (o1.pos[idx] < o2.pos[idx]){
+								return -1;
+							} else {
+								return 0;
+							}
+						} else { // on complement strand
+							if (o1.pos[idx] > o2.pos[idx]) {
+								return -1;
+							} else if (o1.pos[idx] < o2.pos[idx]) {
+								return 1;
+							} else {
+								return 0;
+							}
+						}
+					} else { // on diff. strands
+						if (o1.pos[idx] < 0) // put comp strand stuff last
+							return 1;
+						else
+							return -1;
+					}
+				}
+			}
+		};
+	}
+	
+}
diff --git a/src/org/gel/mauve/analysis/Segment.java b/src/org/gel/mauve/analysis/Segment.java
new file mode 100644
index 0000000..a4f9624
--- /dev/null
+++ b/src/org/gel/mauve/analysis/Segment.java
@@ -0,0 +1,169 @@
+package org.gel.mauve.analysis;
+
+import java.io.Serializable;
+
+import java.util.Comparator;
+
+import org.gel.mauve.Match;
+
+public class Segment implements Serializable {
+	static final long serialVersionUID = 1;
+	
+	/**
+	 *  The start coordinate of this match in each sequence
+	 */
+	public long [] left;
+
+	/**
+	 *  The ends of this match in each sequence
+	 */
+	public long [] right;
+	
+	/**
+	 * The direction of each match. false is forward, true is reverse
+	 */
+	public boolean [] reverse;
+
+	public Segment [] prevs;
+
+	public Segment [] nexts;
+	
+	public String typed_id;
+
+	public static final Segment END = new Segment (0, false);
+
+	public static final String MULTIPLICITY_STRING = "multiplicity";
+
+	// represents which sequences this segment is part of
+	protected long mult_type;
+
+	public Segment (long [] l, long [] r, boolean [] c) {
+		left = l;
+		right = r;
+		reverse = c;
+	}
+
+	public Segment (int count, boolean links) {
+		left = new long [count];
+		right = new long [count];
+		reverse = new boolean [count];
+		if (links) {
+			prevs = new Segment [count];
+			nexts = new Segment [count];
+		}
+	}
+
+	public Segment (int count) {
+		this (count, false);
+	}
+	
+	public Segment () {
+	}
+
+	/**
+	 * compute a number indicating which genomes the local alignment is defined
+	 * in. This is a binary number where a 1 indicates that the match is defined
+	 * and a 0 indicates it is undefined. For example, a local alignment shared
+	 * by the first and the fourth (out of five) genomes would be represented as
+	 * 10010.
+	 */
+	public long multiplicityType () {
+		if (mult_type == 0) {
+			// determine the match's multiplicity type.
+			for (int seqI = 0; seqI < left.length; seqI++) {
+				mult_type <<= 1;
+				if (left[seqI] != Match.NO_MATCH)
+					mult_type |= 1;
+			}
+		}
+		return mult_type;
+	}
+
+	public void append (Segment add, boolean remove) {
+		for (int i = 0; i < left.length; i++) {
+			if (prevs[i] == add)
+				left[i] = add.left[i];
+			else if (nexts[i] == add)
+				right[i] = add.right[i];
+			else if (left[i] != Match.NO_MATCH) {
+				System.out.println ("Tried to add in inconsistent order: "
+						+ add + " " + this);
+			}
+			if (remove)
+				add.remove (i);
+		}
+	}
+
+	public void remove (int seq_index) {
+		if (prevs[seq_index] != null)
+			prevs[seq_index].nexts[seq_index] = nexts[seq_index];
+		nexts[seq_index].prevs[seq_index] = prevs[seq_index];
+	}
+
+	/** format the ungapped local alignment coordinates into a string */
+	public String toString () {
+		String rval = new String ();
+		for (int seqI = 0; seqI < left.length; seqI++) {
+			rval += "<";
+			if (left[seqI] != Match.NO_MATCH) {
+				if (reverse != null && reverse[seqI])
+					rval += "-";
+				rval += left[seqI];
+				rval += ",";
+				rval += right[seqI];
+			}
+			rval += "> ";
+		}
+		return rval;
+	}
+
+	public long [] getSegmentLengths () {
+		long [] sizes = new long [left.length];
+		for (int i = 0; i < sizes.length; i++)
+			sizes[i] = getSegmentLength (i);
+		return sizes;
+	}
+
+	public long getSegmentLength (int sequence) {
+		if (left[sequence] == 0)
+			return 0;
+		else
+			return right[sequence] - left[sequence] + 1;
+	}
+
+	public double getAvgSegmentLength () {
+		long [] sizes = getSegmentLengths ();
+		long total = 0;
+		int present_in = 0;
+		for (int i = 0; i < left.length; i++) {
+			if (sizes[i] != 0) {
+				total += sizes[i];
+				present_in++;
+			}
+		}
+		return ((double) total) / present_in;
+	}
+	
+	public static Comparator<Segment> getGenPositionComparator(int genSrcIdx){
+		return new GenomePositionComparator(genSrcIdx);
+	}
+	
+	private static class GenomePositionComparator implements Comparator<Segment> {
+		private int src;
+		
+		public GenomePositionComparator(int genSrcIdx){
+			src = genSrcIdx;
+		}
+		
+		public int compare(Segment a, Segment b){
+			long leftA = Math.abs(a.reverse[src]? a.right[src]:a.left[src]);
+			long leftB = Math.abs(b.reverse[src]? b.right[src]:b.left[src]);
+			return (int) (leftA - leftB);
+		}
+	}
+	
+	
+
+
+}
+
diff --git a/src/org/gel/mauve/analysis/SegmentComparator.java b/src/org/gel/mauve/analysis/SegmentComparator.java
new file mode 100644
index 0000000..3c16443
--- /dev/null
+++ b/src/org/gel/mauve/analysis/SegmentComparator.java
@@ -0,0 +1,65 @@
+package org.gel.mauve.analysis;
+
+import java.util.Comparator;
+
+public class SegmentComparator implements Comparator {
+
+	public int index;
+
+	public boolean multiple;
+
+	public static final int BY_MULTIPLICITY = -1;
+
+	public SegmentComparator (int index, boolean mult) {
+		this.index = index;
+		multiple = mult;
+	}
+
+	public SegmentComparator (int index) {
+		this (index, false);
+	}
+
+	public int compare (Object o1, Object o2) {
+		if (o1 == null)
+			return -1;
+		else if (o2 == null)
+			return 1;
+		Segment seg = null;
+		long one;
+		long two;
+		int ret = 0;
+		int i = index;
+		do {
+			long rval = 0;
+			if (i == BY_MULTIPLICITY) {
+				rval = ((Segment) o1).multiplicityType () - ((Segment) o2).multiplicityType ();
+				if (multiple)
+					i++;
+			}
+			if (rval == 0 && i > BY_MULTIPLICITY) {
+				if (o1 instanceof Segment) {
+					seg = (Segment) o1;
+					one = seg.left [i];
+				}
+				else
+					one = ((Long) o1).longValue ();
+				if (o2 instanceof Segment) {
+					seg = (Segment) o2;
+					two = seg.left [i];
+				}
+				else
+					two = ((Long) o2).longValue ();
+				rval = one - two;
+			}
+			if (rval < 0)
+				ret = -1;
+			else
+				ret = rval == 0 ? 0 : 1;
+			i++;
+			if (seg != null && i == seg.left.length)
+				i = 0;
+		} while (multiple && ret == 0 && i != index);
+		return ret;
+	}
+
+}
diff --git a/src/org/gel/mauve/analysis/SnpExporter.java b/src/org/gel/mauve/analysis/SnpExporter.java
new file mode 100644
index 0000000..2f65c7d
--- /dev/null
+++ b/src/org/gel/mauve/analysis/SnpExporter.java
@@ -0,0 +1,502 @@
+package org.gel.mauve.analysis;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.XMFAAlignment;
+import org.gel.mauve.XmfaViewerModel;
+
+public class SnpExporter {
+	public static char[] revtab = initRevTab();
+	static public char[] initRevTab(){
+			revtab = new char[255];
+			for(byte i=0; i<127; i++){
+					revtab[i]=(char)i;
+			}
+	        revtab['a'] = 't';
+	        revtab['A'] = 'T';
+	        revtab['t'] = 'a';
+	        revtab['T'] = 'A';
+	        revtab['c'] = 'g';
+	        revtab['C'] = 'G';
+	        revtab['g'] = 'c';
+	        revtab['G'] = 'C';
+	        revtab['r'] = 'y';
+	        revtab['R'] = 'Y';
+	        revtab['y'] = 'r';
+	        revtab['Y'] = 'R';
+	        revtab['k'] = 'm';
+	        revtab['K'] = 'M';
+	        revtab['m'] = 'k';
+	        revtab['M'] = 'K';
+	        revtab['s']='s';
+	        revtab['S']='S';
+	        revtab['w']='w';
+	        revtab['W']='W';
+	        revtab['b'] = 'v';
+	        revtab['B'] = 'V';
+	        revtab['v'] = 'b';
+	        revtab['V'] = 'B';
+	        revtab['d'] = 'h';
+	        revtab['D'] = 'H';
+	        revtab['h'] = 'd';
+	        revtab['H'] = 'D';
+	        revtab['n']='n';
+	        revtab['N']='N';
+	        revtab['x']='x';
+	        revtab['-']='-';
+	        return revtab;
+	}
+	static public char revLookup(byte b)
+	{
+		return revtab[b];
+	}
+	static char[] lowertab = initLowerTab();
+	static public char[] initLowerTab(){
+			lowertab= new char[255];
+			lowertab['a'] = 'a';
+			lowertab['A'] = 'a';
+			lowertab['t'] = 't';
+			lowertab['T'] = 't';
+			lowertab['c'] = 'c';
+			lowertab['C'] = 'c';
+			lowertab['g'] = 'g';
+			lowertab['G'] = 'g';
+			lowertab['r'] = 'r';
+			lowertab['R'] = 'r';
+			lowertab['y'] = 'y';
+			lowertab['Y'] = 'y';
+			lowertab['k'] = 'k';
+			lowertab['K'] = 'k';
+			lowertab['m'] = 'm';
+			lowertab['M'] = 'm';
+			lowertab['s']='s';
+			lowertab['S']='s';
+			lowertab['w']='w';
+			lowertab['W']='w';
+			lowertab['b'] = 'b';
+			lowertab['B'] = 'b';
+			lowertab['v'] = 'v';
+			lowertab['V'] = 'v';
+			lowertab['d'] = 'd';
+			lowertab['D'] = 'd';
+			lowertab['h'] = 'h';
+			lowertab['H'] = 'h';
+			lowertab['n']='n';
+			lowertab['N']='n';
+			lowertab['x']='x';
+			lowertab['-']='-';
+	        return lowertab;
+	}
+	static public char lowerLookup(byte b)
+	{
+		return lowertab[b];
+	}
+	public static void export( XmfaViewerModel model, XMFAAlignment xmfa, BufferedWriter output ) throws IOException
+	{
+		int iv_count = (int)model.getLcbCount();
+		int seq_count = model.getSequenceCount();
+
+		// write the header
+		// then extract and write out all snps
+		output.write("SNP pattern");
+		for( int seqI = 0; seqI < seq_count; seqI++ )
+		{	
+			String seq = "sequence_"+Integer.valueOf(seqI+1).toString()+"_";
+			output.write("\t"+seq+"Contig\t"+seq+"PosInContg\t"+seq+"GenWidePos");
+			output.write(Integer.valueOf(seqI+1).toString());
+		}
+		output.write("\n");
+		
+		SNP[] snps = getSNPs(model, xmfa);
+		for (int i = 0; i < snps.length; i++){
+			output.write(snps[i].toString()+"\n");
+		}
+	}
+	
+	public static void exportGaps(XmfaViewerModel model, XMFAAlignment xmfa, BufferedWriter output) throws IOException {
+		// Genome  Contig   Position_in_Contig   GenomeWide_Position   Length
+		Gap[][] gaps = getGaps(model);
+		output.write("Genome\tContig\tPosition_in_Contig\tGenomeWide_Position\tLength\n");
+		for (Gap[] ar: gaps)
+			for (Gap g: ar)
+				output.write(g.toString()+"\n");
+	}
+	
+	public static SNP[] getSNPs(XmfaViewerModel model){
+		return getSNPs(model, model.getXmfa());
+	}
+	
+	public static SNP[] getSNPs( XmfaViewerModel model, XMFAAlignment xmfa){
+		return getSNPs(model,xmfa,xmfa.getSourceLcbList());
+	}
+	
+	public static SNP[] getSNPs( XmfaViewerModel model, XMFAAlignment xmfa, LCB[] lcbs){
+	//	int iv_count = (int)model.getLcbCount();
+		int seq_count = model.getSequenceCount();
+		Vector<SNP> snps = new Vector<SNP>();
+		
+		for(int i = 0;i  < lcbs.length; i++ )
+		{
+			int ivI = lcbs[i].id;
+			int iv_length = (int)xmfa.getLcbLength(ivI);
+			byte[][] bytebuf = new byte[seq_count][iv_length];
+			for( int seqI = 0; seqI < seq_count; seqI++ )
+			{
+				byte[] tmp = xmfa.readRawSequence (ivI, seqI, 0, iv_length);
+				tmp = XMFAAlignment.filterNewlines(tmp);
+				System.arraycopy(tmp, 0, bytebuf[seqI], 0, iv_length);
+			}
+			int cc = 0;
+			for(int colI = 0; colI < iv_length; colI++)
+			{
+				// first check whether the column contains a polymorphic site
+				char b = 0;			
+				boolean poly = false;
+				for( int seqI = 0; seqI < seq_count; seqI++ )
+				{
+					byte c = bytebuf[seqI][colI];
+					if( c == '-' )
+						continue;
+					if( b == 0 )
+						b = lowerLookup(c);
+					if( b != lowerLookup(c) )
+						poly = true;
+				}
+				if( b != 0)
+					cc++;	// don't count all-gap columns
+				if(!poly)
+					continue;
+				
+				// if so, then write out the polymorphic site...
+				// first determine which seq is reference
+				long [] seq_offsets = new long[seq_count];
+				boolean [] gap = new boolean[seq_count];
+				xmfa.getColumnCoordinates (model, ivI, cc-1, seq_offsets, gap);
+				Genome g = model.getReference();
+				int refseq = g.getSourceIndex();
+				int lcbi = model.getLCBIndex(model.getGenomeBySourceIndex(refseq), seq_offsets[refseq]);
+				// don't use refseq if it's not aligned at the polymorphic site
+				while(lcbi >= xmfa.getSourceLcbList().length){
+					refseq = (refseq + 1) % model.getSequenceCount();
+					lcbi = model.getLCBIndex(model.getGenomeBySourceIndex(refseq), seq_offsets[refseq]);
+				}
+				boolean rev = xmfa.getSourceLcbList()[lcbi].getReverse(model.getReference());
+				SNP tmp = new SNP(model);
+				StringBuilder sb = new StringBuilder();
+				for( int seqI = 0; seqI < seq_count; seqI++ )
+				{
+					char c = '-';
+					if(rev)
+						c = revLookup(bytebuf[seqI][colI]);
+					else
+						c = (char)bytebuf[seqI][colI];
+
+					long pos = 0;
+					if(!gap[seqI])
+						pos = seq_offsets[seqI];
+					tmp.addTaxa(seqI, c, pos);
+				}
+				snps.add(tmp);
+			}
+		}
+		return snps.toArray(new SNP[snps.size()]);
+	}
+	
+	public static SNP[] getLocalSNPs( XmfaViewerModel model, XMFAAlignment xmfa, LCB lcb){
+		LCB[] tmp = new LCB[1];
+		tmp[0] = lcb;
+		return getSNPs(model,xmfa,tmp);
+	}
+	
+	private static int countGapLen(byte[] seq, int gapStart){
+		int len = gapStart;
+		while(seq[len] == '-')
+			len++;
+		return len-gapStart;
+	}
+	
+	public static Chromosome[][] getUniqueChromosomes(XmfaViewerModel model){
+		Chromosome[][] ret = new Chromosome[model.getGenomes().size()][];
+		for (int i = 0; i < ret.length; i++){
+			ret[i] = getUniqueChromosomes(model,model.getGenomeBySourceIndex(i));
+		}
+		return ret;
+	}
+	
+	public static Chromosome[] getUniqueChromosomes(XmfaViewerModel model, Genome g){
+		return getUniqueChromosomes(model, model.getXmfa(), model.getGenomes().toArray(new Genome[model.getGenomes().size()]),g);
+	}
+	
+	public static Chromosome[] getUniqueChromosomes(XmfaViewerModel model, XMFAAlignment xmfa, Genome[] set, Genome g){
+		if (g.getSourceIndex()==1) {
+			System.out.println(g.getDisplayName());
+		}
+		Pattern p = Pattern.compile("Scaffold5.12[56][0-9]");
+		List<Chromosome> chroms = g.getChromosomes();
+		Iterator<Chromosome> it = chroms.iterator();
+		Vector<Chromosome> ret = new Vector<Chromosome>();
+		while(it.hasNext()){
+			Chromosome curr = it.next();
+		//	if (p.matcher(curr.getName()).matches()){
+		//		System.out.println(curr.getName());
+		//	} 
+			boolean unique = true;
+			long i = curr.getStart();
+			long[] coords = new long[model.getGenomes().size()];
+			boolean[] gap = new boolean[model.getGenomes().size()];
+			for (; i <= curr.getEnd() && unique; i++){
+				long[] loc = model.getLCBAndColumn(g, i);
+				int lcbId = (int)loc[0];
+				model.getColumnCoordinates(lcbId, loc[1], coords, gap);
+				unique = unique && isUnique(gap,set,g.getSourceIndex());
+			}
+			if (gap[1])
+				System.out.print("");
+			if (unique) 
+				ret.add(curr);
+		}
+		return ret.toArray(new Chromosome[ret.size()]);
+	}
+	
+	private static boolean isUnique(boolean[] gap, Genome[] set, int g){
+		if (gap[g]) 
+			return false;
+		boolean allGap = true;
+		for (int i = 0; i < set.length && allGap; i++){
+			if (set[i].getSourceIndex() == g) 
+				continue;
+			allGap = allGap && gap[set[i].getSourceIndex()];
+		}
+		return allGap;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public static Gap[][] getGaps(XmfaViewerModel model){
+		XMFAAlignment xmfa = model.getXmfa();
+		int iv_count = (int)model.getLcbCount();
+		int seq_count = model.getSequenceCount();
+
+		Vector[] gaps = new Vector[seq_count];
+		 for (int i = 0; i < gaps.length; i++)
+			gaps[i] = new Vector();
+		
+		LCB[] lcbs = xmfa.getSourceLcbList();
+		LCB[] tmpLCBs = new LCB[lcbs.length];
+		System.arraycopy(lcbs, 0, tmpLCBs, 0, lcbs.length);
+		
+		
+		Genome[] genomes = (Genome[]) model.getGenomes().toArray(new Genome[seq_count]); 
+		
+		for (int genI = 0; genI < genomes.length; genI++){
+			Genome g = genomes[genI];
+
+			// First we need to get intra-LCB gaps
+			for (int lcbI = 0; lcbI < lcbs.length; lcbI++){
+				
+				int lcbLen = (int) xmfa.getLcbLength(lcbI);
+				byte[] tmp = xmfa.readRawSequence(lcbI, genI, 0, lcbLen);
+				tmp = XMFAAlignment.filterNewlines(tmp);
+				long[] seq_offset = new long[genomes.length];
+				boolean[] gap = new boolean[genomes.length];
+				
+				int colI = 0;
+				while (colI < tmp.length){
+					if (tmp[colI]=='-'){
+						long start = colI;
+						// this will move colI to the position immediately following the gap
+						while(tmp[colI]=='-'){
+							if (colI == tmp.length-1) {
+								colI++;
+								break;
+							} else 
+								colI++;
+						}
+						long end = colI-1;
+						long len = end - start + 1;
+						if (lcbs[lcbI].getReverse(g))
+							xmfa.getColumnCoordinates(model, lcbI, end+1, seq_offset, gap);
+						else
+							xmfa.getColumnCoordinates(model, lcbI, start-1, seq_offset, gap);
+						
+						gaps[genI].add(new Gap(genI,lcbI,seq_offset[genI],len, model));	
+					} else	
+						colI++;
+				}	
+			}
+			
+			// Now we need to get inter-LCB gaps
+			
+			// sort based on left-end positions
+			Arrays.sort(tmpLCBs, Segment.getGenPositionComparator(genI));
+			
+			for (int lcbI = 0; lcbI < tmpLCBs.length-1; lcbI++){
+				LCB lcb1 = tmpLCBs[lcbI];
+				LCB lcb2 = tmpLCBs[lcbI+1];
+				long dist = lcb2.getLeftEnd(genomes[genI])-lcb1.getRightEnd(genomes[genI])-1;
+				if (dist > 0){ // there's gaps in other genomes
+					
+					for (int genJ = 0; genJ < genomes.length; genJ++){
+						if (genJ == genI) 
+							continue; 
+						else {
+							gaps[genJ].add(new Gap(genJ, -1,
+									lcb1.getRightEnd(genomes[genJ]), dist, model));
+						}
+					}
+				}
+			}
+			
+			
+			
+		}
+		
+		Gap[][] ret = new Gap[gaps.length][];
+		for (int i = 0; i < ret.length; i++){
+			mergeAdjacentGaps(gaps[i]);
+			ret[i] = (Gap[]) gaps[i].toArray(new Gap[gaps[i].size()]);
+		}
+		return ret;
+	}
+	
+	/**
+	 * Merges gaps that begin at the same position.
+	 * If two Gaps begin at the same position,
+	 * they are merged to create a new Gap object,
+	 * removed, and replaced with the resulting new Gap.
+	 * 
+	 * @param v list of gaps to merge (if necessary)
+	 */
+	private static void mergeAdjacentGaps(Vector<Gap> v){
+		// sort Gaps by their position in alignment
+		Collections.sort(v, Gap.getAlnmtPosComparator());
+		
+		int gapI = 0;
+		while (gapI < v.size()-1){
+			if (v.get(gapI).getPosition() == v.get(gapI+1).getPosition()){
+		//		System.err.print("\nMerging gap of size " + v.get(gapI).getLength() +
+		//				" with a gap of size " + v.get(gapI+1).getLength() + ". ");
+				
+				Gap merge = Gap.mergeGaps(v.get(gapI), v.get(gapI+1));
+		//		System.err.println("Created gap of size " + merge.getLength());
+				v.remove(gapI);
+				v.remove(gapI);
+				v.insertElementAt(merge, gapI);
+			} else {
+				gapI++;
+			}
+		}
+	}
+	
+	/**
+	 * Returns a 4x4 matrix of counts of substitution types between 
+	 * genome <code>src_i</code> and <code>src_j</code>
+	 * 
+	 * <code>
+	 *      A  C  T  G
+	 *    A -
+	 *    C    -
+	 *    T       -
+	 *    G          -
+	 * </code>
+	 * 
+	 * @param model <code>XmfaViewerModel</code> holding alignment data
+	 * @param src_i source index of a genome 
+	 * @param src_j source index of a genome 
+	 * @return a 4x4 matrix of substitution counts
+	 */
+	public static int[][] countSubstitutions(XmfaViewerModel model, int src_i, int src_j){
+		int[][] subs = new int[4][4];
+		
+		SNP[] snps = getSNPs(model, model.getXmfa(), model.getXmfa().getSourceLcbList());
+		
+		for (int k = 0; k < snps.length; k++){ 
+			char c_i = snps[k].getChar(src_i);
+			char c_j = snps[k].getChar(src_j);
+			int b_i = getBaseIdx(c_i);
+			int b_j = getBaseIdx(c_j);
+			if (b_i != b_j && b_i > 0 && b_j > 0)
+				subs[b_i][b_j]++;
+		}
+		return subs;
+	}
+	
+	private static int getBaseIdx(char c){
+		switch(c){
+		  case 'a': return 0; 
+		  case 'A': return 0;
+		  case 'c': return 1;
+		  case 'C': return 1;
+		  case 't': return 2;
+		  case 'T': return 2;
+		  case 'g': return 3;
+	 	  case 'G': return 3;
+		  default: return -1;
+		}
+	}
+	
+	public static void main(String[] args) {
+		Options opts = new Options();
+		opts.addOption( OptionBuilder
+			.withArgName("alignment file")
+			.hasArg()
+			.withDescription("A required parameter specifying an XMFA format file generated by progressiveMauve")
+			.isRequired()
+			.create('f')
+			);
+
+		opts.addOption( OptionBuilder
+				.withArgName("output file name")
+				.hasArg()
+				.withDescription("The name of the output file for the homologs")
+				.isRequired()
+				.create('o')
+				);
+
+		CommandLineParser parser = new GnuParser();
+		CommandLine line=null;
+		try{
+			line = parser.parse( opts, args );
+		}catch(org.apache.commons.cli.ParseException pe){
+			HelpFormatter formatter = new HelpFormatter();
+			formatter.printHelp( 
+					"SnpExporter -f <XMFA alignment input> -o <SNP output>", opts);
+			
+			throw new RuntimeException("There was an error parsing your command-line options.  Please check them and try again.");
+		}
+		String alnmtFilePath = line.getOptionValue('f');
+		String outputFilePath = line.getOptionValue('o');
+		
+		try {
+			System.out.print("Loading alignment file...");
+			XmfaViewerModel model = new XmfaViewerModel(new File(alnmtFilePath), null);
+			System.out.println("Exporting SNPs...");
+			model.setReference(model.getGenomeBySourceIndex(0));
+			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath));
+			export(model, model.getXmfa(), bw);
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.exit(-1);
+		}
+		
+	}
+
+}
diff --git a/src/org/gel/mauve/analysis/TreeAnalysis.java b/src/org/gel/mauve/analysis/TreeAnalysis.java
new file mode 100644
index 0000000..3f82682
--- /dev/null
+++ b/src/org/gel/mauve/analysis/TreeAnalysis.java
@@ -0,0 +1,58 @@
+package org.gel.mauve.analysis;
+
+import java.util.Vector;
+
+
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.XMFAAlignment;
+import org.gel.mauve.XmfaViewerModel;
+/*
+import pal.distance.DistanceMatrix;
+import pal.misc.SimpleIdGroup;
+*/
+public class TreeAnalysis {
+	
+	
+	/*
+	
+	public static DistanceMatrix getLocalDistMat(XmfaViewerModel model, XMFAAlignment xmfa, LCB lcb){
+		long[] starts = lcb.starts;
+		int taxaCount = 0;
+		Vector<Genome> vNames = new Vector<Genome>();
+		for (int i = 0; i < starts.length; i++){
+			if (starts[i]>0){
+				String name = model.getGenomeBySourceIndex(i).getDisplayName();
+				int lastIdx = name.lastIndexOf(".");
+				name = name.substring(name.lastIndexOf("/")+1, 
+						(lastIdx>=0?lastIdx:name.length()));
+				vNames.add(model.getGenomeBySourceIndex(i));
+				taxaCount++;
+			}
+			
+		}
+		Genome[] genomes = vNames.toArray(new Genome[taxaCount]);
+		double[][] dist = new double[taxaCount][taxaCount];
+		SNP[] snps = SnpExporter.getLocalSNPs(model, xmfa, lcb);
+		for (int snpI = 0; snpI < snps.length; snpI++){
+			SNP tmp = snps[snpI];
+			if (!tmp.hasAmbiguities()){
+				for (int genI=0;genI<genomes.length;genI++){
+					for (int genJ=0;genJ<genI;genJ++){
+						dist[genI][genJ]+=tmp.areEqual(genomes[genI], genomes[genJ])?0:1;
+					}
+				}
+				
+			}
+		}
+		String[] names = new String[genomes.length];
+		for (int i = 0; i < genomes.length; i++)
+			names[i] = genomes[i].getDisplayName();
+		return new DistanceMatrix(dist, new SimpleIdGroup(names));
+	}
+	
+	
+	*/
+
+}
diff --git a/src/org/gel/mauve/assembly/AssemblyScorer.java b/src/org/gel/mauve/assembly/AssemblyScorer.java
new file mode 100644
index 0000000..4eb2aad
--- /dev/null
+++ b/src/org/gel/mauve/assembly/AssemblyScorer.java
@@ -0,0 +1,810 @@
+package org.gel.mauve.assembly;
+
+import java.io.File;
+
+
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+import java.util.List;
+
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.analysis.BrokenCDS;
+import org.gel.mauve.analysis.CDSErrorExporter;
+import org.gel.mauve.analysis.Gap;
+import org.gel.mauve.analysis.PermutationExporter;
+import org.gel.mauve.analysis.SNP;
+import org.gel.mauve.analysis.SnpExporter;
+import org.gel.mauve.contigs.ContigOrderer;
+import org.gel.mauve.dcjx.Adjacency;
+import org.gel.mauve.dcjx.Block;
+import org.gel.mauve.dcjx.DCJ;
+import org.gel.mauve.gui.AlignmentProcessListener;
+
+
+public class AssemblyScorer implements AlignmentProcessListener {
+
+	private ContigOrderer co;
+	private File alnmtFile;
+	private File outputDir;
+	private boolean batch;
+	private String basename;
+	private XmfaViewerModel model; 
+	private int[][] subs;
+	private SNP[] snps;
+	private Gap[] refGaps;
+	private Gap[] assGaps; 
+	private Chromosome[] extraCtgs;
+	private Chromosome[] missingChroms;
+	private BrokenCDS[] brokenCDS;
+	private CDSErrorExporter cdsEE;
+	private DCJ dcj;
+	private boolean getBrokenCDS = true;
+	
+	private int numSharedBnds;
+	
+	private int numInterLcbBnds;
+	
+	/** extra adjacencies */
+	private Vector<Adjacency> typeI;
+	
+	/** missing adjacencies */
+	private Vector<Adjacency> typeII;
+	private Vector<Chromosome> invCtgs;
+	private Map<Chromosome,Integer> misAsm;
+	private int numMisAssemblies;
+	
+	private int miscalled;
+	private int uncalled;
+
+	/** contig stats */
+	private long maxContigLength = -1;
+	private long contigN50 = -1;
+	private long contigN90 = -1;
+	private long minContigLength = -1;
+
+	private static int A = 0;
+	private static int C = 1;
+	private static int T = 2;
+	private static int G = 3;
+	
+
+	public AssemblyScorer(XmfaViewerModel model){
+		this.model = model;
+		loadInfo();
+	}
+	
+	public AssemblyScorer(File alnmtFile, File outDir) {
+		this.alnmtFile = alnmtFile;
+		this.outputDir = outDir;
+		basename = alnmtFile.getName();
+		batch = false;
+	}
+	
+	public AssemblyScorer(File alnmtFile, File outDir, String basename) {
+		this(alnmtFile,outDir);
+		this.basename = basename;
+	}
+	
+	public AssemblyScorer(ContigOrderer co, File outDir) {
+		this.co = co;
+		this.outputDir = outDir;
+		batch = false;
+	}
+	
+	public AssemblyScorer(ContigOrderer co, File outDir, String basename) {
+		this(co,outDir);
+		this.basename = basename;
+	}
+	
+	public void completeAlignment(int retcode){
+		if (retcode == 0) {
+			if (co != null){
+				alnmtFile = co.getAlignmentFile();
+				if (basename == null) {
+					basename = alnmtFile.getName();
+				}
+			}
+			try {
+				this.model = new XmfaViewerModel(alnmtFile,null);
+			} catch (IOException e) {
+				System.err.println("Couldn't load alignment file " 
+									+ alnmtFile.getAbsolutePath());
+				e.printStackTrace();
+			}
+			if (model != null) {
+				loadInfo();
+				printInfo(this, outputDir, basename, batch);
+			}
+			
+		} else { 
+			System.err.println("Alignment failed with error code "+ retcode);
+		}
+			
+	}
+	
+	private void computeAdjacencyErrors(){
+		Adjacency[] ref = dcj.getAdjacencyGraph().getGenomeA();
+		Adjacency[] ass = dcj.getAdjacencyGraph().getGenomeB();
+		Comparator<Adjacency> comp = new Comparator<Adjacency>(){
+			public int compare(Adjacency o1, Adjacency o2) {
+				String s1 = new String(min(o1.getFirstBlockEnd(),o1.getSecondBlockEnd()));
+				s1 = s1 + max(o1.getFirstBlockEnd(),o1.getSecondBlockEnd());
+				String s2 = new String(min(o2.getFirstBlockEnd(),o2.getSecondBlockEnd()));
+				s2 = s2 + max(o2.getFirstBlockEnd(),o2.getSecondBlockEnd());
+				return s1.compareTo(s2);
+			}
+			private String min(String s1, String s2){
+				if (s1.compareTo(s2) > 0)
+					return s2;
+				else 
+					return s1;
+			}
+			private String max(String s1, String s2){
+				if (s1.compareTo(s2) > 0)
+					return s1;
+				else 
+					return s2;
+			}
+		};
+		// false positives : adjacencies in assembly that aren't in the reference
+		typeI = new Vector<Adjacency>();
+		// false negatives : adjacencies in reference that aren't in the assembly
+		typeII = new Vector<Adjacency>();
+		
+		TreeSet<Adjacency> refSet = new TreeSet<Adjacency>(comp);
+		for (Adjacency a: ref) {
+			refSet.add(a);
+		}
+		
+	//	System.err.println("\nnum assembly adjacencies: " + ass.length);
+		Vector<Adjacency> intersection = new Vector<Adjacency>(); 
+		for (Adjacency a: ass){
+			if (!refSet.contains(a)) 
+				typeI.add(a);
+			else
+				intersection.add(a);
+		}
+
+	//	System.err.println("num typeI errors: " + typeI.size());
+		
+		TreeSet<Adjacency> assSet = new TreeSet<Adjacency>(comp);
+		for (Adjacency a: ass) { assSet.add(a);}
+		
+	//	System.err.println("num ref adjacencies: " + ref.length);
+		
+		for (Adjacency a: ref) {
+			if (!assSet.contains(a))
+				typeII.add(a);
+		}
+	//	System.err.println("num typeII errors: " + typeII.size());
+		
+	/*	Iterator<Adjacency> it = intersection.iterator();
+		while(it.hasNext()){
+			System.err.println(it.next().toString());
+		}*/
+	}
+	
+	/**
+	 * computes info. sorts gaps and snps
+	 */
+	private synchronized void loadInfo(){
+		model.setReference(model.getGenomeBySourceIndex(0));
+		
+		System.out.print("Counting shared bounds between contigs/chromosomes and LCBs...");
+		numSharedBnds = PermutationExporter.getSharedBoundaryCount(model);
+		System.out.print("done!\n");
+		
+		System.out.print("Counting interLCB contig/chromosome boundaries...");
+		numInterLcbBnds = PermutationExporter.countInterBlockBounds(model);
+		System.out.print("done!\n");
+		
+		System.out.print("Computing signed permutations...");
+		String[] perms = PermutationExporter.getPermStrings(model, true); 
+		System.out.print("done!\n");
+		
+		//System.out.println("Permutations: ");
+		//System.out.println(perms[0]);
+		//System.out.println(perms[1]);
+		
+		System.out.print("Performing DCJ rearrangement analysis...");
+		this.dcj = new DCJ(perms[0], perms[1]);
+		System.out.print("done!\n");
+		
+		System.out.print("Computing adjacency errors...");
+		computeAdjacencyErrors();
+		System.out.print("done!\n");
+		
+		System.out.print("Getting SNPs...");
+		this.snps = SnpExporter.getSNPs(model);
+		System.out.print("done!\n");
+		
+		System.out.print("Counting base substitutions...");
+		this.subs = AssemblyScorer.countSubstitutions(snps);
+		summarizeBaseCalls();
+		System.out.print("done!\n");
+		
+		System.out.print("Counting gaps...");
+		Gap[][] tmp = SnpExporter.getGaps(model);
+		System.out.print("done!\n");
+		
+		System.out.print("Counting extra contigs...");
+		Chromosome[][] unique = SnpExporter.getUniqueChromosomes(model);
+		System.out.print("done!\n");
+		
+		computeContigSizeStats();
+		
+		refGaps = tmp[0];
+		assGaps = tmp[1];
+		Arrays.sort(assGaps);
+		Arrays.sort(refGaps);
+		missingChroms = unique[0];
+		extraCtgs = unique[1];
+		System.out.flush();
+		if (getBrokenCDS){
+			computeBrokenCDS();
+		}
+	}
+	
+	private void computeContigSizeStats(){
+		List<Chromosome> chromos = model.getGenomeBySourceIndex(1).getChromosomes();
+		long[] sizer = new long[chromos.size()];
+		int i=0;
+		long sum = 0;
+		for(Chromosome c : chromos){
+			sizer[i++] = c.getLength();
+			sum += c.getLength();
+		}
+		
+		Arrays.sort(sizer);
+		minContigLength = sizer[0];
+		maxContigLength = sizer[sizer.length-1];
+		long cur = 0;
+		for(i=sizer.length-1; i>=0 && cur*2 < sum; i--){
+			cur += sizer[i];
+		}
+		contigN50 = sizer[i+1];
+		i = 0;
+		for(; i>0 && cur < sum*0.9d;){
+			cur += sizer[--i];
+		}
+		contigN90 = sizer[i];
+	}
+	
+	
+	public void summarizeBaseCalls(){
+		int subsum = 0;
+		for(int i=0; i<subs.length;i++){
+			for(int j=0; j<subs.length;j++)
+				subsum += subs[i][j];
+		}
+		this.miscalled = subsum;
+		this.uncalled = snps.length - subsum;
+	}
+
+	private void computeBrokenCDS(){
+		Iterator<Genome> it = model.getGenomes().iterator();
+		boolean haveAnnotations = true;
+		while(it.hasNext()){
+			haveAnnotations = haveAnnotations &&
+				(it.next().getAnnotationSequence() != null);
+		}
+		haveAnnotations = model.getGenomeBySourceIndex(0).getAnnotationSequence() != null;
+		if (haveAnnotations){
+			System.out.print("Getting broken CDS...");
+			System.out.flush();
+			this.cdsEE = new CDSErrorExporter(model, snps, assGaps, refGaps);
+			try {
+				brokenCDS = cdsEE.getBrokenCDS();
+				System.out.print("done!\n");
+			} catch (Exception e){
+				System.err.println("\n\nfailed to compute broken CDS. Reason given below");
+				System.err.println(e.getMessage());
+				e.printStackTrace();
+			} 
+		}
+	}
+	
+	public int getMiscalled() {
+		return miscalled;
+	}
+
+	public void setMiscalled(int miscalled) {
+		this.miscalled = miscalled;
+	}
+
+	public int getUncalled() {
+		return uncalled;
+	}
+
+	public void setUncalled(int uncalled) {
+		this.uncalled = uncalled;
+	}
+
+	public XmfaViewerModel getModel(){
+		return this.model;
+	}
+
+	public DCJ getDCJ(){
+		return dcj;
+	}
+	
+	public Gap[] getReferenceGaps(){
+		return refGaps;
+	}
+	
+	public Gap[] getAssemblyGaps(){
+		return assGaps;
+	}
+	
+	public Chromosome[] getExtraContigs(){
+		return extraCtgs;
+	}
+	
+	public Chromosome[] getMissingChromosomes(){
+		return missingChroms;
+	}
+	
+	public SNP[] getSNPs(){
+		return snps;
+	}
+	
+	public int[][] getSubs(){
+		return subs;
+	}
+	
+	public boolean hasBrokenCDS(){
+		if (brokenCDS == null) 
+			return false;
+		else {
+			return brokenCDS.length > 0;
+		}
+	}
+	
+	public BrokenCDS[] getBrokenCDS(){
+		return brokenCDS;
+	}
+	
+	public int numBrokenCDS(){
+		return cdsEE.numBrokenCDS();
+	}
+	
+	public int numCompleteCDS(){
+		return cdsEE.numCompleteCDS();
+	}
+	
+	public int getSCJdist(){
+		return dcj.scjDistance();
+	}
+	
+	public int getDCJdist(){
+		return dcj.dcjDistance();
+	}
+	
+	public int getBPdist(){
+		return dcj.bpDistance();
+	}
+	
+	public int numBlocks(){
+		return dcj.numBlocks();
+	}
+
+	public double typeIadjErr(){
+		return ((double) typeI.size()) / 
+			((double) dcj.getAdjacencyGraph()
+						 .getGenomeB().length);
+	}
+	
+	public double typeIIadjErr(){
+		return ((double) typeII.size()) /
+			((double) dcj.getAdjacencyGraph()
+						 .getGenomeA().length);
+	}
+	
+	public Map<Chromosome, Integer> getMisAssemblies(){
+		return misAsm;
+	}
+	
+	public Chromosome[] getInverted(){
+		return invCtgs.toArray(new Chromosome[invCtgs.size()]);
+	}
+	
+	public int numLCBs(){
+		return (int) model.getLcbCount();
+	}
+	
+	public int numContigs(){
+		return model.getGenomeBySourceIndex(1).getChromosomes().size();
+	}
+
+	public long numReplicons(){
+		return model.getGenomeBySourceIndex(0).getChromosomes().size();
+	}
+	
+	
+	public long numBasesAssembly(){
+		return model.getGenomeBySourceIndex(1).getLength();
+	}
+	
+	public long numBasesReference(){
+		return model.getGenomeBySourceIndex(0).getLength();
+	}
+	
+	public double percentMissedBases(){
+		double totalBases = model.getGenomeBySourceIndex(0).getLength();
+		double missedBases = 0;
+		for(int i = 0; i < assGaps.length; i++){
+			missedBases += assGaps[i].getLength();
+		}
+		return missedBases/totalBases;
+	}
+	
+	public long totalMissedBases(){
+		long missedBases = 0;
+		for(int i = 0; i < assGaps.length; i++){
+			missedBases += assGaps[i].getLength();
+		}
+		return missedBases;
+	}
+	
+	public long getMaxContigLength() {
+		return maxContigLength;
+	}
+
+	public long getContigN50() {
+		return contigN50;
+	}
+
+	public long getContigN90() {
+		return contigN90;
+	}
+
+	public long getMinContigLength() {
+		return minContigLength;
+	}
+
+	/**
+	 * 
+	 * @return numExtraBases/totalNumBases
+	 */
+	public double percentExtraBases(){
+		double totalBases = model.getGenomeBySourceIndex(1).getLength();
+		double extraBases = 0;
+		for (int i = 0; i < refGaps.length; i++){
+			extraBases += refGaps[i].getLength();
+		}
+		return extraBases/totalBases;
+	}
+	
+	public long totalExtraBases(){
+		long extraBases = 0;
+		for (int i = 0; i < refGaps.length; i++){
+			extraBases += refGaps[i].getLength();
+		}
+		return extraBases;
+	}
+	
+	public int getSharedBoundaryCount(){
+		return numSharedBnds;
+	}
+	
+	public int getInterLcbBoundaryCount(){
+		return numInterLcbBnds;
+	}
+	
+	/* 
+	 * calculate GC content of missing bases
+	 * in stretches up to 100nt.
+	 * Useful for determining if we're suffering GC bias
+	 */
+	public static void calculateMissingGC(AssemblyScorer asmScore, File outDir, String basename){
+		try{
+			System.out.println("Printing GC contents of gaps < 100nt!");
+			Gap[] gaps = asmScore.getAssemblyGaps();
+			java.io.BufferedWriter bw = new BufferedWriter(new java.io.FileWriter(new File( outDir, basename + "_missing_gc.txt" )));
+			java.io.BufferedWriter bw3 = new BufferedWriter(new java.io.FileWriter(new File( outDir, basename + "_background_gc_distribution.txt")));
+			Random randy = new Random();
+			for(int i=0; i<gaps.length; i++){
+				if(gaps[i].getLength()>100)
+					continue;
+				long glen = gaps[i].getLength();
+				glen = glen < 50 ? 50 : glen;
+				if(gaps[i].getPosition()+glen/2 >=
+					(int)asmScore.getModel().getGenomeBySourceIndex(gaps[i].getGenomeSrcIdx()).getLength())
+				{
+					glen = ((int)asmScore.getModel().getGenomeBySourceIndex(gaps[i].getGenomeSrcIdx()).getLength() - gaps[i].getPosition() - 3) / 2;
+				}
+				if(gaps[i].getPosition()-glen/2 < 1)
+				{
+					glen = gaps[i].getPosition() / 2;
+				}
+
+				long[] left = asmScore.getModel().getLCBAndColumn(gaps[i].getGenomeSrcIdx(), gaps[i].getPosition()-glen/2);
+				long[] right = asmScore.getModel().getLCBAndColumn(gaps[i].getGenomeSrcIdx(), gaps[i].getPosition()+glen/2);
+				if(left[0]!=right[0]){
+					// gap spans LCB.  too hard for this hack.
+					continue;
+				}
+				long ll = left[1] < right[1] ? left[1] : right[1];
+				byte[] rawseq = asmScore.getModel().getXmfa().readRawSequence((int)left[0], 0, ll, Math.abs(right[1]-left[1])+1);
+				double gc = countGC(rawseq);
+				if(!Double.isNaN(gc))
+				{
+					bw.write((new Double(gc)).toString());
+					bw.write("\n");
+				}
+				int upperbound = (int)asmScore.getModel().getGenomeBySourceIndex(gaps[i].getGenomeSrcIdx()).getLength() - (int)glen - 10000;
+				upperbound = upperbound < 0 ? 0 : upperbound;
+				int rpos = randy.nextInt(upperbound);
+	
+				// evil code copy!!
+				left = asmScore.getModel().getLCBAndColumn(gaps[i].getGenomeSrcIdx(), rpos-glen/2);
+				right = asmScore.getModel().getLCBAndColumn(gaps[i].getGenomeSrcIdx(), rpos+glen/2);
+				if(left[0]!=right[0]){
+					// gap spans LCB.  too hard for this hack.
+					continue;
+				}
+				ll = left[1] < right[1] ? left[1] : right[1];
+				rawseq = asmScore.getModel().getXmfa().readRawSequence((int)left[0], 0, ll, Math.abs(right[1]-left[1])+1);
+				gc = countGC(rawseq);
+				if(!Double.isNaN(gc))
+				{
+					bw3.write((new Double(gc)).toString());
+					bw3.write("\n");
+				}
+			}
+			bw.flush();
+			bw.close();
+			bw3.flush();
+			bw3.close();
+		}catch(IOException ioe){ioe.printStackTrace();};
+	}
+
+	static double countGC(byte[] rawseq){
+		double gc = 0;
+		double counts = 0;
+		for(int j=0; j<rawseq.length; j++){
+			if(rawseq[j]== 'G' || rawseq[j]== 'C' || 
+					rawseq[j]== 'g' || rawseq[j]== 'c')
+				gc++;
+			else if(rawseq[j]=='-' || rawseq[j]=='\n')
+				continue;
+			counts++;
+		}
+		return gc /= counts;
+	}
+
+	static String subsToString(AssemblyScorer assScore){
+		// A C T G
+		StringBuilder sb = new StringBuilder();
+		sb.append("\tA\tC\tT\tG\n");
+		char[] ar = {'A','C','T','G'};
+		int[][] subs = assScore.getSubs();
+		for (int i = 0; i < subs.length; i++){
+			sb.append(ar[i]);
+			for (int j = 0; j < subs.length; j++){
+				sb.append("\t"+(i==j?"-":subs[i][j]));
+			}
+			sb.append("\n");
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Returns a 4x4 matrix of counts of substitution types between 
+	 * genome <code>src_i</code> and <code>src_j</code>
+	 * 
+	 * <pre>
+	 * <code>
+	 *      A  C  T  G 
+	 *    A -          
+	 *    C    -       
+	 *    T       -    
+	 *    G          - 
+	 * </code>
+	 * </pre>
+	 * @param snps
+	 * @return a 4x4 matrix of substitution counts
+	 */
+	public static int[][] countSubstitutions(SNP[] snps){
+		int[][] subs = new int[4][4];
+		for (int k = 0; k < snps.length; k++){ 
+			char c_0 = snps[k].getChar(0);
+			char c_1 = snps[k].getChar(1);
+			
+			try {
+				if (c_0 != c_1)
+					subs[getBaseIdx(c_0)][getBaseIdx(c_1)]++;
+			} catch (IllegalArgumentException e){
+				//System.err.println("Skipping ambiguity: ref = " +c_0 +" assembly = " + c_1 );
+			}
+		}
+		return subs;
+	}
+
+	static int getBaseIdx(char c) throws IllegalArgumentException {
+		switch(c){
+		  case 'a': return AssemblyScorer.A; 
+		  case 'A': return AssemblyScorer.A;
+		  case 'c': return AssemblyScorer.C;
+		  case 'C': return AssemblyScorer.C;
+		  case 't': return AssemblyScorer.T;
+		  case 'T': return AssemblyScorer.T;
+		  case 'g': return AssemblyScorer.G;
+	 	  case 'G': return AssemblyScorer.G;
+		  default:{ throw new IllegalArgumentException("char " + c);}
+		}
+	}
+
+	public static final int MINIMUM_REPORTABLE_MISSING_LENGTH = 20;
+	public static void printMissingRegions(AssemblyScorer asmScore, File outDir, String basename)
+	{
+		try{
+			BufferedWriter bw = new BufferedWriter(new FileWriter(new File( outDir, basename + "_missing_regions.txt" )));
+			Gap[] gaps = asmScore.getReferenceGaps();
+			for(int gI=0; gI < gaps.length; gI++){
+				if(gaps[gI].getLength() < MINIMUM_REPORTABLE_MISSING_LENGTH)
+					continue;
+				byte[][] aln = asmScore.getModel().getSequenceRange(gaps[gI].getGenomeSrcIdx(), gaps[gI].getPosition(), gaps[gI].getPosition()+gaps[gI].getLength());
+				StringBuilder sb = new StringBuilder();
+				for(int i=0; i<aln[0].length; i++){
+					if(aln[0][i]!='-' && aln[0][i]!='\n' && aln[0][i]!='\r')
+						sb.append((char)aln[0][i]);
+				}
+				sb.append("\n");
+				bw.write(">at_contig_" + gaps[gI].getContig() + "_pos_" + gaps[gI].getPositionInContig() + "_assembly_has_" + gaps[gI].getLength() + "_extra_bases\n");
+				bw.write(sb.toString());
+			}
+			bw.flush();
+		}catch(IOException ioe){ioe.printStackTrace();};
+	}
+
+	public static void printInfo(AssemblyScorer sa, File outDir, String baseName, boolean batch){
+		PrintStream gapOut = null;
+		PrintStream miscallOut = null;
+		PrintStream uncallOut = null;
+		PrintStream sumOut = null;
+		PrintStream blockOut = null;
+		PrintStream chromOut = null;
+		if(!outDir.exists()){
+			outDir.mkdir();
+		}
+		try {
+			File gapFile = new File(outDir, baseName+"__gaps.txt");
+			gapFile.createNewFile();
+			gapOut = new PrintStream(gapFile);
+			File miscallFile = new File(outDir, baseName+"__miscalls.txt");
+			miscallFile.createNewFile();
+			miscallOut = new PrintStream(miscallFile);
+			File uncallFile = new File(outDir, baseName+"__uncalls.txt");
+			uncallFile.createNewFile();
+			uncallOut = new PrintStream(uncallFile);
+			File sumFile = new File(outDir, baseName+"__sum.txt");
+			sumFile.createNewFile();
+			sumOut = new PrintStream(sumFile);
+			File blockFile = new File(outDir,baseName+"__blocks.txt");
+			blockFile.createNewFile();
+			blockOut = new PrintStream(blockFile);
+			File chromFile = new File(outDir,"chromosomes.txt");
+			chromFile.createNewFile();
+			chromOut = new PrintStream(chromFile);
+		} catch (IOException e){
+			e.printStackTrace();
+			System.exit(-1);    
+		}
+		printInfo(sa,miscallOut,uncallOut,gapOut);
+		printBlockInfo(blockOut,sa.model);
+		if (batch){
+		    sumOut.print(ScoreAssembly.getSumText(sa, false, true));	
+		}else {
+		    sumOut.print(ScoreAssembly.getSumText(sa, true, true));
+		}
+		AssemblyScorer.calculateMissingGC(sa, outDir, baseName);
+		AssemblyScorer.printMissingRegions(sa, outDir, baseName);
+		chromOut.print(getReferenceChromosomes(sa));
+		
+		blockOut.close();
+		gapOut.close();
+		miscallOut.close();
+		uncallOut.close();
+		sumOut.close();
+		chromOut.close();
+	}
+
+	private static String getReferenceChromosomes(AssemblyScorer sa) {
+		Genome refGenome = sa.getModel().getGenomeBySourceIndex(0);
+		StringBuilder sb = new StringBuilder();
+		for(int cI=0; cI<refGenome.getChromosomes().size(); cI++){
+			Chromosome c = refGenome.getChromosomes().get(cI);
+			sb.append(c.getName());
+			sb.append("\t");
+			sb.append(c.getStart() + 1);
+			sb.append("\n");
+		}
+		return sb.toString();
+	}
+	
+	private static void printBlockInfo(PrintStream out, XmfaViewerModel model){
+		out.println("BlockId\tBlockLength\tRefLeft\tRefRight\tAsmLeft\tAsmRight");
+		LCB[] lcbList = model.getSplitLcbList();
+		int l = -1;
+		int r = -1;
+		Genome ref = model.getGenomeBySourceIndex(0);
+		Genome asm = model.getGenomeBySourceIndex(1);
+		for (LCB lcb: lcbList){
+			out.print(lcb.id+"\t"+lcb.getAvgSegmentLength());
+			if (lcb.getReverse(ref)){
+				out.print("\t"+-1*lcb.getLeftEnd(ref));
+				out.print("\t"+-1*lcb.getRightEnd(ref));
+			} else {
+				out.print("\t"+lcb.getLeftEnd(ref));
+				out.print("\t"+lcb.getRightEnd(ref));
+			}
+			if (lcb.getReverse(asm)){
+				out.print("\t"+-1*lcb.getLeftEnd(asm));
+				out.print("\t"+-1*lcb.getRightEnd(asm));
+			} else {
+				out.print("\t"+lcb.getLeftEnd(asm));
+				out.print("\t"+lcb.getRightEnd(asm));
+			}
+			out.println();
+		}
+	}
+	
+	/**
+	 * Prints the SNP and gap data from this AssemblyScorer object out to the 
+	 * respective <code>PrintStream</code>s. 
+	 * <br>
+	 * NOTE: <code>snpOut</code> and <code>gapOut</code> 
+	 * can take null values
+	 * </br>
+	 * @param sa the AssemblyScorer to print info for
+	 * @param snpOut stream to print SNP info to
+	 * @param gapOut stream to print gap info to
+	 */
+	public static void printInfo(AssemblyScorer sa,  PrintStream miscallOut, PrintStream uncallOut, PrintStream gapOut){
+		if (miscallOut!=null){
+			StringBuilder sb = new StringBuilder();
+			sb.append("SNP_Pattern\tRef_Contig\tRef_PosInContig\tRef_PosGenomeWide\tAssembly_Contig\tAssembly_PosInContig\tAssembly_PosGenomeWide\n");
+			StringBuilder sb2 = new StringBuilder();
+			sb2.append("SNP_Pattern\tRef_Contig\tRef_PosInContig\tRef_PosGenomeWide\tAssembly_Contig\tAssembly_PosInContig\tAssembly_PosGenomeWide\n");
+			for (int i = 0; i < sa.snps.length; i++)
+			{
+				if(sa.snps[i].hasAmbiguities())
+					sb.append(sa.snps[i].toString()+"\n");
+				else
+					sb2.append(sa.snps[i].toString()+"\n");
+			}
+			uncallOut.print(sb.toString());
+			uncallOut.flush();
+			miscallOut.print(sb2.toString());
+			miscallOut.flush();
+		}
+		if (gapOut!=null){
+			StringBuilder sb = new StringBuilder();
+			sb.append("Sequence\tContig\tPosition_in_Contig\tGenomeWide_Position\tLength\tGenome0_GlobalPos\tGenome0_contig\tGenome0_LocalPos\tGenome1_GlobalPos\tGenome1_contig\tGenome1_LocalPos\n");
+			for (int i = 0; i < sa.refGaps.length; i++)
+				sb.append(sa.refGaps[i].toString("reference")+"\n");
+			for (int i = 0; i < sa.assGaps.length; i++)
+				sb.append(sa.assGaps[i].toString("assembly")+"\n");			
+			gapOut.print(sb.toString());
+			gapOut.flush();
+		}
+		
+	}
+}
diff --git a/src/org/gel/mauve/assembly/ScoreAssembly.java b/src/org/gel/mauve/assembly/ScoreAssembly.java
new file mode 100644
index 0000000..f4f0fc4
--- /dev/null
+++ b/src/org/gel/mauve/assembly/ScoreAssembly.java
@@ -0,0 +1,427 @@
+package org.gel.mauve.assembly;
+
+import java.awt.Dimension;
+import java.io.File;
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.swing.JOptionPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.table.DefaultTableModel;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.biojava.bio.symbol.SymbolList;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.OptionsBuilder;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.analysis.BrokenCDS;
+import org.gel.mauve.analysis.Gap;
+import org.gel.mauve.analysis.SNP;
+import org.gel.mauve.contigs.ContigOrderer;
+import org.gel.mauve.gui.AlignFrame;
+import org.gel.mauve.gui.AlignWorker;
+import org.gel.mauve.gui.AnalysisDisplayWindow;
+import org.gel.mauve.gui.MauveFrame;
+
+public class ScoreAssembly  {
+	
+	private static final String[] DEFAULT_ARGS = {"--skip-refinement",
+													"--weight=200"};
+	private static String SUM_CMD = "Summary";
+	private static String SUM_DESC = "Summary of scoring assembly";
+	private static String SNP_CMD = "SNPs";
+	private static String SNP_DESC = "SNPs between reference and assembly";
+	private static String GAP_CMD = "Gaps";
+	private static String GAP_DESC = "Gaps in reference and assembly";
+	private static String CDS_CMD = "Broken CDS";
+	private static String CDS_DESC = "Broken CDS in assembly genome";
+	private static String INV_CMD = "Inverted Contigs";
+	private static String INV_DESC = "Incorrectly inverted contigs";
+	private static String MIS_CMD = "Mis-assembled Contigs";
+	private static String MIS_DESC = "Mis-assembled contigs with the total number of mis-assemblies in each";
+	
+	private static HashMap<String,ScoreAssembly> modelMap = new HashMap<String, ScoreAssembly>();
+	
+	private static HashMap<String,ScoreAssembly> running = new HashMap<String, ScoreAssembly>();
+
+	private static final String temp = "Running...";
+	
+	private static final String error = "Error computing DCJ distances! Please report bug to atritt at ucdavis.edu";
+	
+	private JTextArea sumTA;
+	
+	private XmfaViewerModel model;
+	
+	private int fWIDTH = 800;
+
+	private int fHEIGHT = 510;
+	
+	private AssemblyScorer asmScore;
+	
+	private AnalysisDisplayWindow win;
+		
+	private boolean finished;
+	
+	private boolean cancel;
+	
+	public ScoreAssembly(XmfaViewerModel model){
+		//init(model);
+		this.model = model;
+		this.finished = false;
+		initWithJTables(model);
+	}
+
+	private void initWithJTables(XmfaViewerModel model){
+		win = new AnalysisDisplayWindow("Score Assembly - "+model.getSrc().getName(), fWIDTH, fHEIGHT);
+		sumTA = win.addContentPanel(SUM_CMD, SUM_DESC, true);
+		sumTA.append(temp);
+		this.win.showWindow();
+		running.put(this.model.getSrc().getAbsolutePath(), this);
+		new Thread( new Runnable (){ 
+			public void run(){
+				try {
+				asmScore = new AssemblyScorer(ScoreAssembly.this.model);
+				ScoreAssembly.this.finish();
+				} catch (Exception e){
+					ScoreAssembly.this.cancel();
+					System.err.println("\nError\n");
+					e.printStackTrace();
+				}
+			}
+		}).start();
+	}
+	
+	public void cancel(){
+		cancel = true;
+	}
+	
+	private synchronized void finish(){
+		if (!cancel) {
+			running.remove(this.model.getSrc().getAbsolutePath());
+			modelMap.put(this.model.getSrc().getAbsolutePath(), this);
+			sumTA.replaceRange("", 0, temp.length());
+			sumTA.setText(getSumText(asmScore,true,false));
+			//addJTables();
+			addTables();
+			finished = true;
+			win.showWindow();
+			sumTA.setCaretPosition(0);
+		}
+	}
+	
+	private void addTables(){
+		SNP[] snps = asmScore.getSNPs();
+		Object[] snpHeader = {"SNP_Pattern","Ref_Contig","Ref_PosInContig",
+							"Ref_PosGenomeWide","Assembly_Contig",
+							"Assembly_PosInContig","Assembly_PosGenomeWide"};
+		
+		DefaultTableModel snpData = win.addContentTable(SNP_CMD, SNP_DESC, false);
+		win.showWindow();
+		snpData.setColumnIdentifiers(snpHeader);
+		for (int snpI = 0; snpI < snps.length; snpI++){
+			snpData.addRow(snps[snpI].toString().split("\t"));
+		}
+		
+		Gap[] refGaps = asmScore.getReferenceGaps();
+		Gap[] assGaps = asmScore.getAssemblyGaps();
+		int nGen = model.numGenomes();
+		Object[] gapHeader = new Object[5+(nGen)*3];
+		gapHeader[0] = "Sequence";
+		gapHeader[1] = "Contig";
+		gapHeader[2] = "Position_in_Contig";
+		gapHeader[3] = "GenomeWide_Position";
+		gapHeader[4] = "Length";
+		int idx = 5;
+		for (int j = 0; j < nGen; j++){
+			gapHeader[idx++] = "sequence_"+j+"_pos"; 
+			gapHeader[idx++] = "sequence_"+j+"_ctg";
+			gapHeader[idx++] = "sequence_"+j+"_posInCtg";
+		}
+		DefaultTableModel gapData = win.addContentTable(GAP_CMD, GAP_DESC, false);
+		gapData.setColumnIdentifiers(gapHeader);
+		for (int gapI = 0; gapI < refGaps.length; gapI++)
+			gapData.addRow(refGaps[gapI].toString().split("\t"));
+		for (int gapI = 0; gapI < assGaps.length; gapI++)
+			gapData.addRow(assGaps[gapI].toString().split("\t"));
+		if (asmScore.hasBrokenCDS()){
+			Object[] cdsHeader = 
+				{"CDS_ID","Peptide_Length",
+					"Perc_IncorrectBases", "Broken_Frame_Segments",
+					"Gap_Segments","Substituted_Positions","Substitutions",
+					"Stop_Codon_Positions","Original_Residue"};
+			DefaultTableModel cdsData = win.addContentTable(CDS_CMD, CDS_DESC, false);
+			cdsData.setColumnIdentifiers(cdsHeader);
+			BrokenCDS[] cds = asmScore.getBrokenCDS();
+			for (BrokenCDS bcds: cds)
+				cdsData.addRow(bcds.toString().split("\t"));
+		}
+	}
+	
+	public static String getSumText(AssemblyScorer assScore, boolean header, boolean singleLine){
+		NumberFormat nf = NumberFormat.getInstance();
+		nf.setMaximumFractionDigits(4);
+		StringBuilder sb = new StringBuilder();
+		if (singleLine){
+			if (header) {
+				sb.append("Name\tNumContigs\tNumRefReplicons\tNumAssemblyBases\tNumReferenceBases\tNumLCBs\t" +
+						"DCJ_Distance\tNumDCJBlocks\tNumSNPs\tNumMisCalled\tNumUnCalled\tNumGapsRef\tNumGapsAssembly\t" +
+						"TotalBasesMissed\tPercBasesMissed\tExtraBases\tPercExtraBases" + 
+						"\tMissingChromosomes\tExtraContigs\tNumSharedBoundaries\tNumInterLcbBoundaries"+
+						"\tBrokenCDS\tIntactCDS\tContigN50\tContigN90\tMinContigLength\tMaxContigLength"+
+						"\tAA\tAC\tAG\tAT\tCA\tCC\tCG\tCT\tGA\tGC\tGG\tGT\tTA\tTC\tTG\tTT\n");
+			}
+			sb.append(	assScore.getModel().getGenomeBySourceIndex(1).getDisplayName() + "\t" +
+						assScore.numContigs()+"\t"+assScore.numReplicons()+"\t"+
+						assScore.numBasesAssembly()+"\t"+assScore.numBasesReference()+"\t"+assScore.numLCBs()+"\t"+
+						assScore.getDCJdist()+"\t"+assScore.numBlocks()+"\t"+assScore.getSNPs().length+"\t"+
+						assScore.getMiscalled()+'\t'+assScore.getUncalled()+'\t'+
+						assScore.getReferenceGaps().length+"\t"+assScore.getAssemblyGaps().length+"\t"+
+					 	assScore.totalMissedBases()+"\t"+nf.format(assScore.percentMissedBases()*100)+"\t"+
+					 	assScore.totalExtraBases()+"\t"+nf.format(assScore.percentExtraBases()*100) +"\t"+
+					 	assScore.getMissingChromosomes().length+"\t"+assScore.getExtraContigs().length +"\t"+ 
+					 	assScore.getSharedBoundaryCount()+"\t"+assScore.getInterLcbBoundaryCount());
+			sb.append( "\t" + assScore.numBrokenCDS()+"\t"+assScore.numCompleteCDS());
+			sb.append( "\t" + assScore.getContigN50() + "\t" + assScore.getContigN90() + "\t" + assScore.getMinContigLength() + "\t" + assScore.getMaxContigLength());
+			int[][] subs = assScore.getSubs();
+			for(int i=0; i<subs.length; i++)
+			{
+				for(int j=0; j<subs[i].length; j++)
+				{
+					sb.append('\t');
+					sb.append(subs[i][j]);
+				}
+			}
+			sb.append("\n");
+		} else {
+			if (header) {
+				sb.append(
+					"Number of Contigs:\t"+assScore.numContigs()+"\n"+
+					"Number reference replicons:\t" + assScore.numReplicons()+"\n"+
+					"Number of assembly bases:\t" + assScore.numBasesAssembly()+"\n"+
+					"Number of reference bases:\t" + assScore.numBasesReference()+"\n"+
+					"Number of LCBs:\t" + assScore.numLCBs()+"\n"+
+					"Number of Blocks:\t"+assScore.numBlocks()+"\n"+
+					"Breakpoint Distance:\t"+assScore.getBPdist()+"\n"+
+					"DCJ Distance:\t"+assScore.getDCJdist()+"\n"+
+					"SCJ Distance:\t"+assScore.getSCJdist()+"\n"+
+					"Number of Complete Coding Sequences:\t"+assScore.numCompleteCDS()+"\n"+
+					"Number of Broken Coding Sequences:\t"+assScore.numBrokenCDS()+"\n"+					
+					"Number of SNPs:\t"+assScore.getSNPs().length+"\n"+
+					"Number of Gaps in Reference:\t"+assScore.getReferenceGaps().length+"\n"+
+					"Number of Gaps in Assembly:\t"+assScore.getAssemblyGaps().length+"\n" +
+					"Total bases missed in reference:\t" + assScore.totalMissedBases() +"\n"+
+					"Percent bases missed:\t" + nf.format(assScore.percentMissedBases()*100)+" %\n"+
+					"Total bases extra in assembly:\t" + assScore.totalExtraBases()+"\n" +
+					"Percent bases extra:\t" + nf.format(assScore.percentExtraBases()*100)+ " %\n"+
+					"Number of missing chromosomes:\t" + assScore.getMissingChromosomes().length +"\n"+
+					"Number of extra contigs:\t"+assScore.getExtraContigs().length +"\n"+
+					"Number of Shared Boundaries:\t"+assScore.getSharedBoundaryCount()+"\n"+
+					"Number of Inter-LCB Boundaries:\t"+assScore.getInterLcbBoundaryCount()+"\n"+
+					"Contig N50:\t"+assScore.getContigN50()+"\n"+
+					"Contig N90:\t"+assScore.getContigN90()+"\n"+
+					"Min contig length:\t"+assScore.getMinContigLength()+"\n"+
+					"Max contig length:\t"+assScore.getMaxContigLength()+"\n"+
+					"Substitutions (Ref on Y, Assembly on X):\n"+AssemblyScorer.subsToString(assScore)
+				);
+				
+				
+			} else { 
+				sb.append(
+						assScore.numContigs()+"\n"+
+						assScore.numLCBs()+"\n"+
+						assScore.getDCJdist()+"\n"+
+						 assScore.numBlocks()+"\n"+
+						 assScore.getSNPs().length+"\n"+
+						 assScore.getReferenceGaps().length+"\n"+
+						 assScore.getAssemblyGaps().length+"\n" +
+						 assScore.totalMissedBases() +"\n"+
+						 nf.format(assScore.percentMissedBases()*100)+"\n"+
+						 assScore.totalExtraBases()+"\n" +
+						 nf.format(assScore.percentExtraBases()*100)+ "\n"+
+						 assScore.getMissingChromosomes() +"\n"+
+						assScore.getExtraContigs()+"\n"+
+						"Number of Shared Boundaries:\t"+assScore.getSharedBoundaryCount()+"\n"+
+						"Number of Inter-LCB Boundaries:\t"+assScore.getInterLcbBoundaryCount()+"\n"+
+						 AssemblyScorer.subsToString(assScore));
+			}
+		}
+		return sb.toString();
+	}
+
+	public static void launchWindow(MauveFrame frame){
+		BaseViewerModel model = frame.getModel();
+		String key = model.getSrc().getAbsolutePath();
+		if (modelMap.containsKey(key)){
+		//	modelMap.get(key).win.showWindow();
+			startNewSA(frame);
+		} else if (model instanceof XmfaViewerModel) {
+			startNewSA(frame);			
+		} else {
+			System.err.println("Can't score assembly unless I have an" +
+							" XmfaViewerModel -- Please report this bug!");
+		}	
+	}
+	
+	private static void startNewSA(MauveFrame frame){
+		interactive(frame);
+	}
+
+	public static void interactive(MauveFrame frame){
+		XmfaViewerModel model = (XmfaViewerModel) frame.getModel();
+		String key = model.getSrc().getAbsolutePath();
+		if (running.containsKey(key)) {
+			int ret_val = JOptionPane.showConfirmDialog(frame, "I'm currently"+
+					"running the scoring pipeline for this assembly. Do you " +
+					"want me to restart it?");
+			if (ret_val == JOptionPane.YES_OPTION){
+				running.get(key).cancel();
+				running.remove(key).win.closeWindow();
+				startNewSA(frame);
+			} else if (ret_val == JOptionPane.NO_OPTION){
+				running.get(key).win.showWindow();
+			}
+		} else {
+				running.put(key, 
+						new ScoreAssembly(model));
+		}
+	}
+	
+	public static void main(String[] args){
+		CommandLine line = OptionsBuilder.getCmdLine(getOptions(), args);
+		if (args.length == 0 || line == null || line.hasOption("help")){
+			HelpFormatter formatter = new HelpFormatter();
+			formatter.printHelp(80,
+					"java -cp Mauve.jar org.gel.mauve.assembly.ScoreAssembly [options]",
+					"[options]", getOptions(), "report bugs to Aaron Darling <aarondarling at ucdavis.edu>");
+			System.exit(-1);
+		}
+	
+		File outDir =  null;
+		if (line.hasOption("outputDir"))
+			outDir = new File(line.getOptionValue("outputDir"));
+		else 
+			outDir = new File(System.getProperty("user.dir"));
+		
+		boolean batch = false;
+		if (line.hasOption("batch"))
+			batch = true;
+		String basename = null;
+		if (line.hasOption("basename"))
+			basename = line.getOptionValue("basename");
+		
+		File alnmtFile = null;
+		AssemblyScorer as = null;
+		
+		if (line.hasOption("alignment")){ // if we don't need to align
+			if ((line.hasOption("reference") || line.hasOption("assembly"))){
+				System.err.println("You gave me an alignment along with a reference and/or assembly genome.\n" +
+				"Do not use use the \"-reference\" and \"-assembly\" flags with the \"-alignment\" flag.");
+				System.exit(-1);
+			}
+			alnmtFile = new File (line.getOptionValue("alignment"));
+			if (basename == null) {
+				basename = alnmtFile.getName();
+				// AED: can't truncate to last . because the file may not have a dot in the name
+			}
+			as = getAS(alnmtFile);
+			AssemblyScorer.printInfo(as,outDir,basename,batch);
+		} else { // we need to do some sort of alignment
+			
+			if (!line.hasOption("reference")){
+				System.err.println("Reference file not given.");
+				System.exit(-1);
+			} else if (!line.hasOption("assembly")){
+				System.err.println("Assembly file not given.");
+				System.exit(-1);
+			}
+			
+			File refFile = new File(line.getOptionValue("reference"));
+			File assPath = new File(line.getOptionValue("assembly"));
+			
+			if (line.hasOption("reorder")){ // we need to reorder first
+				String[] reorderParams = new String[8];
+				reorderParams[0] = "-output";
+				reorderParams[1] = outDir.getAbsolutePath();
+				reorderParams[2] = "-ref";
+				reorderParams[3] = refFile.getAbsolutePath();
+				reorderParams[4] = "-draft";
+				reorderParams[5] = assPath.getAbsolutePath();
+				// Add the following two options when doing a reorder for alignment scoring
+				// These tune the aligner toward high sequence identity, which we
+				// expect when scoring an assembly against a reference
+				reorderParams[6] = "--seed-weight=25";
+				reorderParams[7] = "--solid-seeds";
+				ContigOrderer co = new ContigOrderer(reorderParams, null, false);
+				if (basename != null)
+					as = new AssemblyScorer(co, outDir, basename);
+				else 
+					as = new AssemblyScorer(co, outDir);
+				co.addAlignmentProcessListener(as);
+			} else {
+				if (basename == null) {
+					basename = assPath.getName();
+				}
+				alnmtFile = new File(outDir, basename+".xmfa");
+				as = new AssemblyScorer(alnmtFile, outDir, basename);
+				String[] cmd = makeAlnCmd(refFile,assPath, alnmtFile);
+				System.out.println("Executing");
+				AlignFrame.printCommand(cmd, System.out);
+				AlignWorker worker = new AlignWorker(as, cmd);
+				worker.start();
+			}
+			
+		}
+	}
+
+	private static AssemblyScorer getAS(File alnmtFile){
+		try {
+			//sa = new ScoreAssembly(args,true);
+			XmfaViewerModel model = new XmfaViewerModel(alnmtFile,null);
+			return new AssemblyScorer(model);
+		} catch (Exception e){
+			e.printStackTrace();
+			System.exit(-1);
+		}
+		return null;
+	}
+
+	private static String[] makeAlnCmd(File seq1, File seq2, File output){
+		String[] ret = new String[6 + DEFAULT_ARGS.length];
+		int j = 0;
+		ret[j++] = AlignFrame.getBinaryPath("progressiveMauve");
+		for (int i = 0; i < DEFAULT_ARGS.length; i++)
+			ret[j++] = DEFAULT_ARGS[i];
+		ret[j++] = "--output="+output.getAbsolutePath();
+		ret[j++] = "--backbone-output=" + output.getAbsolutePath()+".backbone";
+		ret[j++] = "--output-guide-tree=" + output.getAbsolutePath()+".guide_tree";
+		ret[j++] = seq1.getAbsolutePath();
+		ret[j++] = seq2.getAbsolutePath();
+		return ret;
+	}
+
+	@SuppressWarnings("static-access")
+	private static Options getOptions(){
+		Options ret = new Options();
+		OptionsBuilder ob = new OptionsBuilder();
+		ob.addBoolean("help", "print this message");
+		ob.addBoolean("batch", "run in batch mode i.e. print summary output " +
+											"on one line to standard output");
+		ob.addBoolean("reorder", "reorder contigs before scoring the assembly");
+		ob.addArgument("string", "basename for output files", "basename",false);
+		ob.addArgument("directory", "save output in <directory>. Default " +
+									"is current directory.", "outputDir",true);
+		ob.addArgument("file", "file containing alignment of assembly to " +
+											"reference genome", "alignment",false);
+		ob.addArgument("file", "file containing reference genome", "reference",false);
+		ob.addArgument("file", "file containing assembly/draft genome to score",
+																	"assembly",false);
+		return ob.getOptions();
+	}
+}
diff --git a/src/org/gel/mauve/backbone/Backbone.java b/src/org/gel/mauve/backbone/Backbone.java
new file mode 100644
index 0000000..ffc98f3
--- /dev/null
+++ b/src/org/gel/mauve/backbone/Backbone.java
@@ -0,0 +1,82 @@
+package org.gel.mauve.backbone;
+
+import java.awt.Color;
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.analysis.Segment;
+
+public class Backbone extends Segment {
+
+	protected int lcb_index;
+
+	protected long left_col;
+
+	protected long length;
+
+	protected boolean seqs[];
+
+	protected Color color;
+
+	public int getLcbIndex () {
+		return lcb_index;
+	}
+
+	public void setLcbIndex (int lcb_index) {
+		this.lcb_index = lcb_index;
+	}
+
+	public long getLeftColumn () {
+		return left_col;
+	}
+
+	public void setLeftColumn (long left_col) {
+		this.left_col = left_col;
+	}
+
+	public long getLength () {
+		return length;
+	}
+
+	public void setLength (long length) {
+		this.length = length;
+	}
+
+	public boolean [] getSeqs () {
+		return seqs;
+	}
+
+	public void setSeqs (boolean [] seqs) {
+		this.seqs = seqs;
+	}
+
+	public long getLeftEnd (Genome g) {
+		int gi = g.getSourceIndex ();
+		return left [gi];
+	}
+
+	public void setLeftEnd (long [] left_end) {
+		this.left = left_end;
+	}
+
+	public long getRightEnd (Genome g) {
+		int gi = g.getSourceIndex ();
+		return right [gi];
+	}
+
+	public void setRightEnd (long [] right_end) {
+		this.right = right_end;
+	}
+
+	public boolean exists (Genome g) {
+		int sI = g.getSourceIndex ();
+		return seqs[sI];
+	}
+
+	public Color getColor () {
+		return color;
+	}
+
+	public void setColor (Color color) {
+		this.color = color;
+	}
+}
diff --git a/src/org/gel/mauve/backbone/BackboneList.java b/src/org/gel/mauve/backbone/BackboneList.java
new file mode 100644
index 0000000..7d95e45
--- /dev/null
+++ b/src/org/gel/mauve/backbone/BackboneList.java
@@ -0,0 +1,150 @@
+package org.gel.mauve.backbone;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Vector;
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.XMFAAlignment;
+
+public class BackboneList {
+	/**
+	 * Compares LCB ids and left-columns of backbone segments
+	 */
+	static class BbLeftEndComparator implements Comparator {
+		protected Genome g;
+	
+		BbLeftEndComparator (Genome g) {
+			this.g = g;
+		}
+	
+		public int compare (Object o_a, Object o_b) {
+			Backbone a = (Backbone) o_a;
+			Backbone b = (Backbone) o_b;
+			boolean a_def = a.getSeqs ()[g.getSourceIndex ()];
+			boolean b_def = b.getSeqs ()[g.getSourceIndex ()];
+			if (!a_def && !b_def)
+				return 0;
+			if (!a_def)
+				return -1;
+			if (!b_def)
+				return 1;
+			long a_lend = a.getLeftEnd (g);
+			long b_lend = b.getLeftEnd (g);
+			if (a_lend == b_lend)
+				return (int) (a.getRightEnd (g) - b.getRightEnd (g));
+			return (int) (a_lend - b_lend);
+		}
+	}
+
+	/**
+	 * Compares LCB ids and left-columns of backbone segments
+	 */
+	static class BbComparator implements Comparator {
+		protected int seq;
+	
+		BbComparator (int seq) {
+			this.seq = seq;
+		}
+	
+		public int compare (Object o_a, Object o_b) {
+			Backbone a = (Backbone) o_a;
+			Backbone b = (Backbone) o_b;
+			boolean a_def = a.getSeqs ()[seq];
+			boolean b_def = b.getSeqs ()[seq];
+			if (!a_def && !b_def)
+				return 0;
+			if (!a_def)
+				return -1;
+			if (!b_def)
+				return 1;
+			int a_lcb_id = a.getLcbIndex ();
+			int b_lcb_id = b.getLcbIndex ();
+			if (a_lcb_id != b_lcb_id)
+				return a_lcb_id - b_lcb_id;
+			return (int) (a.getLeftColumn () - b.getLeftColumn ());
+		}
+	}
+
+	protected Vector seq_bb;
+
+	protected Backbone [] bb_array;
+
+	protected XMFAAlignment xmfa;
+
+	public void setXmfa (XMFAAlignment xmfa) {
+		this.xmfa = xmfa;
+	}
+
+	public void setBackbone (Backbone [] bb_array) {
+		this.bb_array = bb_array;
+	}
+
+	public void setSeqBackbone (Vector seq_bb) {
+		this.seq_bb = seq_bb;
+	}
+
+	public Backbone getNextBackbone (Genome g, long position) {
+		Backbone [] seq_bb_array = (Backbone []) seq_bb.elementAt (g
+				.getSourceIndex ());
+		if (seq_bb_array.length == 0)
+			return null;
+		if (position < 1)
+			position = 1;
+		if (position > g.getLength ())
+			position = g.getLength ();
+		long [] lcb_and_column = xmfa.getLCBAndColumn (g, position);
+		Backbone key = new Backbone ();
+		key.setLcbIndex ((int) lcb_and_column[0]);
+		key.setLeftColumn (lcb_and_column[1]);
+		key.setLength (0);
+		boolean [] seqs = new boolean [seq_bb.size ()];
+		seqs[g.getSourceIndex ()] = true;
+		key.setSeqs (seqs);
+		long [] left_ends = new long [seq_bb.size ()];
+		left_ends[g.getSourceIndex ()] = position;
+		key.setLeftEnd (left_ends);
+		key.setRightEnd (left_ends);
+		BbLeftEndComparator comp = new BbLeftEndComparator (g);
+		int indie = Arrays.binarySearch (seq_bb_array, key, comp);
+		if (indie < 0) {
+			indie = -indie - 1;
+			// scan backwards to see whether the position was contained in any
+			// previous
+			// bb segments
+			if (indie >= seq_bb_array.length
+					|| position < seq_bb_array[indie].getLeftEnd (g)) {
+				int prev_i = indie - 1;
+				while (prev_i >= 0) {
+					if (seq_bb_array[prev_i].getLeftEnd (g) <= position
+							&& position <= seq_bb_array[prev_i].getRightEnd (g)) {
+						indie = prev_i; // contained by the previous bb seg
+						break;
+					}
+					if (prev_i > 0
+							&& seq_bb_array[prev_i].getLeftEnd (g) == seq_bb_array[prev_i - 1]
+									.getLeftEnd (g))
+						prev_i--;
+					else
+						break;
+				}
+			}
+		}
+
+		if (indie == seq_bb_array.length)
+			return null;
+		return seq_bb_array[indie];
+	}
+
+	public Backbone getBackbone (Genome g, long position) {
+		Backbone bb = getNextBackbone (g, position);
+		if (bb != null && bb.getLeftEnd (g) <= position
+				&& position <= bb.getRightEnd (g))
+			return bb;
+		return null;
+	}
+
+	public Backbone [] getBackboneArray () {
+		return bb_array;
+	}
+}
diff --git a/src/org/gel/mauve/backbone/BackboneListBuilder.java b/src/org/gel/mauve/backbone/BackboneListBuilder.java
new file mode 100644
index 0000000..f61eb88
--- /dev/null
+++ b/src/org/gel/mauve/backbone/BackboneListBuilder.java
@@ -0,0 +1,121 @@
+package org.gel.mauve.backbone;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.XMFAAlignment;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.format.FileFinder;
+
+public class BackboneListBuilder {
+	public static File getFileByKey (XmfaViewerModel model, XMFAAlignment xmfa, String key) {
+		Properties meta = xmfa.metadata;
+		// Find the backbone data
+		String bb_fname;
+		if (meta.containsKey (key)) {
+			bb_fname = meta.getProperty (key);
+		} else {
+			return null; // no backbone information
+		}
+    	bb_fname = FileFinder.findFile(model, bb_fname);
+		File src = new File (bb_fname);
+		if (!src.canRead ()) {
+			return null; // can't read the backbone file
+		}
+		return src;
+	}
+
+	public static BackboneList build (XmfaViewerModel model, XMFAAlignment xmfa)
+			throws IOException {
+		int sequenceCount = model.getSequenceCount ();
+		File src = getFileByKey (model, xmfa, "BackboneFile");
+		if (src == null)
+			return null;
+		Vector bbvect = new Vector ();
+		BufferedReader br = new BufferedReader (new java.io.FileReader (src));
+		String cur_line;
+		StringTokenizer stoke = null;
+		while (br.ready ()) {
+			cur_line = br.readLine ();
+			stoke = new StringTokenizer (cur_line);
+			String tok = stoke.nextToken ();
+			int lcb_id = Integer.parseInt (tok);
+			tok = stoke.nextToken ();
+			long left_col = Long.parseLong (tok) - 1; // left col starts at 1
+			// in the file
+			tok = stoke.nextToken ();
+			long length = Long.parseLong (tok);
+			boolean seqs[] = new boolean [sequenceCount];
+			for (int sI = 0; sI < sequenceCount; ++sI)
+				seqs[sI] = false;
+			while (stoke.hasMoreTokens ()) {
+				tok = stoke.nextToken ();
+				int seq = Integer.parseInt (tok);
+				seqs[seq] = true;
+			}
+			Backbone bb = new Backbone ();
+			bb.setLcbIndex (lcb_id);
+			bb.setLeftColumn (left_col);
+			bb.setLength (length);
+			bb.setSeqs (seqs);
+			long lends[] = new long [seqs.length];
+			boolean lend_gaps[] = new boolean [seqs.length];
+			xmfa.getColumnCoordinates (model, lcb_id, left_col, lends,
+					lend_gaps);
+			long rends[] = new long [seqs.length];
+			boolean rend_gaps[] = new boolean [seqs.length];
+			xmfa.getColumnCoordinates (model, lcb_id, left_col + length - 1,
+					rends, rend_gaps);
+			for (int sI = 0; sI < seqs.length; ++sI) {
+				if (!seqs[sI]) {
+					lends[sI] = 0;
+					rends[sI] = 0;
+				} else {
+					if (rends[sI] < lends[sI]) {
+						long tmp = lends[sI];
+						lends[sI] = rends[sI];
+						rends[sI] = tmp;
+					}
+				}
+			}
+			bb.setLeftEnd (lends);
+			bb.setRightEnd (rends);
+
+			bbvect.addElement (bb);
+		}
+		Backbone [] bb_array = new Backbone [bbvect.size ()];
+		bb_array = (Backbone []) bbvect.toArray (bb_array);
+
+		// now, for each genome create subarrays and sort on lcb id
+		Vector seq_bb_vect = new Vector ();
+		for (int gI = 0; gI < model.getSequenceCount (); ++gI) {
+			int bb_count = 0;
+			for (int bbI = 0; bbI < bb_array.length; ++bbI) {
+				if (bb_array[bbI].getSeqs ()[gI])
+					bb_count++;
+			}
+			Backbone [] seq_bb = new Backbone [bb_count];
+			bb_count = 0;
+			for (int bbI = 0; bbI < bb_array.length; ++bbI) {
+				if (bb_array[bbI].getSeqs ()[gI])
+					seq_bb[bb_count++] = bb_array[bbI];
+			}
+			// now sort on LCB id
+			Genome g = model.getGenomeBySourceIndex (gI);
+			BackboneList.BbLeftEndComparator comp = new BackboneList.BbLeftEndComparator (g);
+			Arrays.sort (seq_bb, comp);
+			seq_bb_vect.addElement (seq_bb);
+		}
+		BackboneList bb_list = new BackboneList ();
+		bb_list.setXmfa (xmfa);
+		bb_list.setBackbone (bb_array);
+		bb_list.setSeqBackbone (seq_bb_vect);
+		return bb_list;
+	}
+}
diff --git a/src/org/gel/mauve/chado/ChadoDB.java b/src/org/gel/mauve/chado/ChadoDB.java
new file mode 100644
index 0000000..03e85b9
--- /dev/null
+++ b/src/org/gel/mauve/chado/ChadoDB.java
@@ -0,0 +1,79 @@
+package org.gel.mauve.chado;
+
+
+import java.sql.*;
+
+import org.biojava.bio.seq.Sequence;
+
+public class ChadoDB {
+	// cvterm_id's 
+	private static int contig = 251;
+	
+	private Connection conn;
+	private String url;
+	private String user;
+	private String pass;
+	
+	/**
+	 * 
+	 * @param loc location of PostgreSQL instance
+	 * @param user user with access to PostgreSQL
+	 * @param pass the user's password
+	 * @throws SQLException 
+	 */
+	public ChadoDB(String loc, String user, String pass) throws SQLException{
+		url = "jdbc:postgresql:"+loc+"chado";
+		this.user = user;
+		this.pass = pass;
+		conn = DBUtils.getPostgreSQLConn(url, user, pass);
+		
+	}
+	
+	public void reconnect() throws SQLException{
+		if (conn.isClosed())
+			conn = DBUtils.getPostgreSQLConn(url, user, pass);
+	}
+	
+	public void insertContig(String org_name, String ctgName, Sequence seq) throws SQLException{
+
+		int org_id =  getOrgId(org_name, conn);
+		String sql = 
+			"INSERT INTO feature (organism_id, name, uniquename,residues,seqlen,type_id)" +
+						" VALUES (?,?,?,?,?,?)";
+		PreparedStatement stmt = conn.prepareStatement(sql);
+		stmt.setInt(1, org_id);
+		stmt.setString(2, (ctgName.length()<255?ctgName:ctgName.substring(0, 255)));
+		stmt.setString(3, ctgName);
+		stmt.setString(4, seq.seqString());
+		stmt.setInt(5, seq.length());
+		stmt.setInt(6, contig);
+		stmt.execute();
+	}
+	
+	
+	public void getGenome(String org_name) throws SQLException{
+		int org_id = getOrgId(org_name,conn);
+		
+	}
+	
+	
+	public static int getOrgId(String name, Connection chadoDB) throws SQLException{
+	
+		ResultSet rs = getOrg(name,chadoDB);
+		return rs.getInt("organism_id");
+	}
+	
+	private static ResultSet getOrg(String name, Connection chadoDB) throws SQLException{
+		String sql = "SELECT * FROM organism WHERE common_name LIKE '?';";
+		PreparedStatement stmt = chadoDB.prepareStatement(sql);
+		stmt.setString(1, name);
+		return stmt.executeQuery();
+	}
+	
+	
+	
+	
+
+	
+	
+}
diff --git a/src/org/gel/mauve/chado/ChadoFeatureLoader.java b/src/org/gel/mauve/chado/ChadoFeatureLoader.java
new file mode 100644
index 0000000..4ceddd2
--- /dev/null
+++ b/src/org/gel/mauve/chado/ChadoFeatureLoader.java
@@ -0,0 +1,52 @@
+package org.gel.mauve.chado;
+
+import java.sql.*;
+
+public class ChadoFeatureLoader {
+	
+	// type_id -> cvterm 
+	
+	// join 
+	
+	private Connection conn;
+	
+	public ChadoFeatureLoader (Connection chado_conn){
+		conn = chado_conn;
+	}
+	
+	public void getFeatures(String genus, String species /*...and coming soon, String strain !!!! */) throws SQLException{
+		String query = "SELECT organism_id FROM organism WHERE " +
+							"genus = "+genus+" AND species = "+species+";";
+		Statement st = conn.createStatement();
+		ResultSet rs = st.executeQuery(query);
+		String org_id = rs.getString("organism_id");
+		st = conn.createStatement();
+		query = "SELECT * FROM feature WHERE organism_id = "+org_id;
+		query = "SELECT * FROM feature INNER JOIN annotations WHERE feature.unique_name = annotations.organism_id = "+org_id;
+		rs = st.executeQuery(query);
+		int left = -1;
+		int right = -1;
+		int strand = 0;
+		String type = null;
+		while(rs.next()){
+			left = rs.getInt("fmin");
+			right = rs.getInt("fmax");
+			strand = rs.getInt("strand");
+			type = rs.getString("name");
+			
+		} 
+	}
+	
+	private static String QUERY = 
+	"SELECT featureprop.feature_id, cvterm.name, featureprop.value, featureloc.fmin, featureloc.fmax, featureloc.strand, feature.name, db.name, dbxref.accession, feature.dbxref_id, feature_dbxref.dbxref_id, dbxref.dbxref_id "+
+    	"FROM featureprop, cvterm, featureloc, feature, feature_dbxref, dbxref, db "+ 
+    	"WHERE organism_id = ? " +
+    			"(featureprop.type_id = cvterm.cvterm_id) AND " +
+        		"(featureprop.feature_id = featureloc.feature_id) AND "+ 
+                "(featureprop.feature_id = feature.feature_id)  AND " +
+                "(featureprop.feature_id = feature_dbxref.feature_id) AND "+
+                "(feature_dbxref.dbxref_id = dbxref.dbxref_id) AND "+
+                "(dbxref.db_id = db.db_id) "+
+                "LIMIT 100 OFFSET 0";
+
+}
diff --git a/src/org/gel/mauve/chado/ChadoReader.java b/src/org/gel/mauve/chado/ChadoReader.java
new file mode 100644
index 0000000..1c24c03
--- /dev/null
+++ b/src/org/gel/mauve/chado/ChadoReader.java
@@ -0,0 +1,41 @@
+package org.gel.mauve.chado;
+
+import java.io.*;
+import java.nio.channels.*;
+import java.sql.*;
+
+import org.gel.mauve.XmfaViewerModel;
+
+public class ChadoReader {
+	private Connection conn;
+
+	public ChadoReader(Connection conn){
+		this.conn = conn;
+	}
+
+	public XmfaViewerModel loadAlignment(String name, File tmpDir) throws SQLException {
+		PreparedStatement ps = conn.prepareStatement("SELECT "+name+" FROM alnmts");
+		ResultSet rs = ps.executeQuery();
+		Blob alnmtSrcBlob = rs.getBlob(1);
+		ReadableByteChannel src = Channels.newChannel(alnmtSrcBlob.getBinaryStream());
+		File alnmtFile = new File(tmpDir,name+".xmfa");
+		FileChannel dest = null;
+		XmfaViewerModel model = null;
+		try {
+			dest = new FileOutputStream(alnmtFile).getChannel();
+			System.out.println("Storing alignment in temporary file " + alnmtFile.getAbsolutePath());
+			src.read(dest.map(FileChannel.MapMode.READ_WRITE, 0, dest.size()));
+			src.close();
+			dest.close();
+			ps.close();
+			alnmtSrcBlob.free();
+			model = new XmfaViewerModel(alnmtFile, null);
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return model;
+	}
+	
+}
diff --git a/src/org/gel/mauve/chado/ChadoWriter.java b/src/org/gel/mauve/chado/ChadoWriter.java
new file mode 100644
index 0000000..b432975
--- /dev/null
+++ b/src/org/gel/mauve/chado/ChadoWriter.java
@@ -0,0 +1,41 @@
+package org.gel.mauve.chado;
+
+import java.nio.*;
+import java.nio.channels.*;
+import java.sql.*;
+import java.io.*;
+
+import org.gel.mauve.XmfaViewerModel;
+
+public class ChadoWriter {
+	private Connection conn;
+	
+	public ChadoWriter(Connection conn) {
+		this.conn = conn;
+	}
+	
+	public void insertAlignment(XmfaViewerModel model) throws SQLException{
+		Blob alnmtDestBlob = conn.createBlob();
+		PreparedStatement ps = conn.prepareStatement("INSERT INTO alignments VALUES (?)");
+		ps.setBlob(1, alnmtDestBlob);
+		WritableByteChannel dest = Channels.newChannel(alnmtDestBlob.setBinaryStream(1));
+		FileChannel src = null;
+		try {
+			src = new FileInputStream(model.getSrc()).getChannel();
+			dest.write(src.map(FileChannel.MapMode.READ_ONLY, 0, src.size()));
+			dest.close();
+			ps.close();
+			src.close();
+			alnmtDestBlob.free();
+		} catch (FileNotFoundException e) {
+			System.err.println("Bad source file: " + model.getSrc().getName());
+			e.printStackTrace();
+			return;
+		} catch (IOException e) {
+			e.printStackTrace();
+			System.err.println("Error inserting alignment into chado database");
+			return;
+		}
+	}
+	
+}
diff --git a/src/org/gel/mauve/chado/DBUtils.java b/src/org/gel/mauve/chado/DBUtils.java
new file mode 100644
index 0000000..3ce3072
--- /dev/null
+++ b/src/org/gel/mauve/chado/DBUtils.java
@@ -0,0 +1,18 @@
+package org.gel.mauve.chado;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+public class DBUtils {
+
+	
+	public static Connection getPostgreSQLConn(String url, String user, String pass) throws SQLException{
+		try {
+			Class.forName("org.postgresql.Driver");
+		} catch (ClassNotFoundException e) {
+			throw new SQLException("Can't find PostgreSQL JDBC driver",e);
+		}
+		return DriverManager.getConnection(url, user, pass);
+	}
+}
diff --git a/src/org/gel/mauve/color/BackboneLcbColor.java b/src/org/gel/mauve/color/BackboneLcbColor.java
new file mode 100644
index 0000000..6738947
--- /dev/null
+++ b/src/org/gel/mauve/color/BackboneLcbColor.java
@@ -0,0 +1,34 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+import java.util.Arrays;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.LCB;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.backbone.Backbone;
+import org.gel.mauve.backbone.BackboneList;
+
+public class BackboneLcbColor implements ColorScheme {
+	public void apply (BaseViewerModel model) {
+		if (!(model instanceof XmfaViewerModel))
+			return;
+		XmfaViewerModel xmfa = (XmfaViewerModel) model;
+		BackboneList bb_list = xmfa.getBackboneList ();
+		if (bb_list == null)
+			return;
+
+		Backbone [] all_bb_array = bb_list.getBackboneArray ();
+		LCB [] lcbs = xmfa.getFullLcbList ();
+		for (int bbI = 0; bbI < all_bb_array.length; ++bbI) {
+			Color c = lcbs[all_bb_array[bbI].getLcbIndex ()].color;
+			all_bb_array[bbI].setColor (c);
+		}
+	}
+
+	public String toString () {
+		return "LCB color";
+	}
+
+}
diff --git a/src/org/gel/mauve/color/BackboneMultiplicityColor.java b/src/org/gel/mauve/color/BackboneMultiplicityColor.java
new file mode 100644
index 0000000..16beba8
--- /dev/null
+++ b/src/org/gel/mauve/color/BackboneMultiplicityColor.java
@@ -0,0 +1,209 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.backbone.Backbone;
+import org.gel.mauve.backbone.BackboneList;
+
+// algorithm is going to be
+// 1: get segment presence/absence patterns as BitSets
+// 2: Create a BitSet HashMap and count the number of nucleotides covered by
+//    each pattern
+// 3: assign maximally distinct colors to the most common patterns
+
+public class BackboneMultiplicityColor implements ColorScheme {
+    public static final float HUE_MIN = 0f;
+    public static final float HUE_MAX = 1f;
+    public static final float HUE_LEVELS = 12;
+    public static final float HUE_WEIGHT = 10f;
+    public static final float SAT_MIN = .7f;
+    public static final float SAT_MAX = 1f;
+    public static final float SAT_LEVELS = 2;
+    public static final float BRIGHT_MIN = .5f;
+    public static final float BRIGHT_MAX = 1.0f;
+    public static final float BRIGHT_LEVELS = 3;
+    
+    /**
+     *  Returns an array of visually distinct colors
+     * @param ncols	The requested number of colors.  Fewer colors may be returned than were requested.
+     * @return	An array with colors
+     */
+    public static Color[] getColors(int ncols)
+    {
+    	// HSB is a cylindrical space
+    	// we would like to pick an arbitrary number of maximally distinct
+    	// colors from this space, limiting our range to the values above
+    	// to do so.
+    	// since i don't know how to solve the problem of choosing a set of
+    	// n maximally distant points in a partial cylinder, i will approximate the
+    	// problem as choosing maximally distant points in a 3d rectangle.
+    	// compute the total volume of the target space and divide
+    	// by the number of desired colors
+    	
+    	float max_cols = HUE_LEVELS * SAT_LEVELS * BRIGHT_LEVELS;
+    	max_cols = ncols < max_cols ? ncols : max_cols;
+    	Color[] cols = new Color[(int)max_cols];
+    	float slice_area = (((float)Math.PI * SAT_MAX * SAT_MAX) - ((float)Math.PI * SAT_MIN * SAT_MIN));
+    	float volume = (BRIGHT_MAX - BRIGHT_MIN) * slice_area * HUE_WEIGHT;
+    	float cube_size = volume / max_cols;
+    	float step = (float)Math.pow(cube_size, (1.0/3.0));
+    	int h_steps = (int)Math.ceil(((HUE_MAX-HUE_MIN)*HUE_WEIGHT) / step);
+    	int s_steps = (int)Math.ceil((SAT_MAX-SAT_MIN) / step);
+    	int b_steps = (int)Math.ceil((BRIGHT_MAX-BRIGHT_MIN) / step);
+    	while(h_steps*(s_steps+1)*(b_steps+1) < max_cols)
+    	{
+    		h_steps++;
+    	}
+    	
+    	float s_step = (SAT_MAX-SAT_MIN) / (float)s_steps;
+    	float b_step = (BRIGHT_MAX-BRIGHT_MIN) / (float)b_steps;
+    	float h_step = (HUE_MAX-HUE_MIN) / (float)h_steps;
+    	float h = 0;
+    	int cI = 0;
+    	for(int sI = 0; sI <= s_steps; sI++)
+        	for(int bI = 0; bI <= b_steps; bI++)
+        		for(int hI = 0; hI < h_steps; hI++)
+        		{
+        			cols[cI++] = Color.getHSBColor(
+        					h, 
+        					(s_step * (float)sI) + SAT_MIN, 
+        					(b_step * (float)bI) + BRIGHT_MIN);
+        			h += h_step;
+        			if(cI == cols.length)
+        				return cols;
+        		}
+    	
+    	return cols;
+    }
+    
+    private final static Comparator BB_MULTIPLICITY_TYPE_COMPARATOR = new Comparator()
+    {
+        public int compare(Object o1, Object o2)
+        {
+        	Backbone a = (Backbone)o1;
+        	Backbone b = (Backbone)o2;
+        	boolean a_seqs[] = a.getSeqs();
+        	boolean b_seqs[] = b.getSeqs();
+        	for( int i = 0; i < a_seqs.length; ++i )
+        	{
+        		if(!a_seqs[i] && b_seqs[i])
+        			return -1;
+        		else if(a_seqs[i] && !b_seqs[i])
+        			return 1;
+        	}
+        	return 0;
+        }
+    };
+    
+    /**
+     * Converts an array of booleans to a BitSet
+     * @param b	an array of booleans
+     * @return	a bitset
+     */
+    private java.util.BitSet makeBitSet( boolean[] b )
+    {
+    	java.util.BitSet bs = new java.util.BitSet(b.length);
+    	for(int i = 0; i < b.length; i++)
+    		if(b[i])
+    			bs.set(i);
+    	return bs;
+    }
+
+    public void apply(BaseViewerModel model)
+    {
+
+    	if( ! (model instanceof XmfaViewerModel))
+    		return;
+    	XmfaViewerModel xmfa = (XmfaViewerModel)model;
+    	BackboneList bb_list = xmfa.getBackboneList();
+    	if( bb_list == null )
+    		return;
+
+        Backbone[] all_bb_array = bb_list.getBackboneArray();
+        Backbone[] bb_by_mt = new Backbone[all_bb_array.length];
+        System.arraycopy(all_bb_array,0,bb_by_mt,0,bb_by_mt.length);
+
+        
+        Arrays.sort(bb_by_mt, BB_MULTIPLICITY_TYPE_COMPARATOR);
+        long unique_mt_count = 1;
+        for( int bbI = 1; bbI < bb_by_mt.length; ++bbI )
+        {
+        	if(BB_MULTIPLICITY_TYPE_COMPARATOR.compare(bb_by_mt[bbI-1],bb_by_mt[bbI]) != 0)
+        		unique_mt_count++;
+        }
+        
+        // count up nucleotides covered by each multiplicity type
+        long[] nt = new long[(int)unique_mt_count];
+        Object[] types = new Object[(int)unique_mt_count];
+        int cur_mt = 0;
+        for( int bbI = 0; bbI < bb_by_mt.length - 1; ++bbI )
+        {
+        	if(BB_MULTIPLICITY_TYPE_COMPARATOR.compare(bb_by_mt[bbI],bb_by_mt[bbI+1]) != 0)
+        	{
+        		types[cur_mt] = bb_by_mt[bbI].getSeqs();
+        		cur_mt++;
+        	}
+        	nt[cur_mt] += bb_by_mt[bbI].getLength();
+        }
+		types[cur_mt] = bb_by_mt[bb_by_mt.length-1].getSeqs();
+        
+        // put the nucleotide counts for each SPA pattern in a map
+        TreeMap sm = new java.util.TreeMap();
+        for( int i = 0; i < nt.length; i++ )
+        {
+        	java.util.Vector l = (java.util.Vector)sm.get(new Long(nt[i]));
+            if (l == null)
+                sm.put(new Long(nt[i]), l=new java.util.Vector());
+            l.add(types[i]);
+        }
+        
+        // get the N most distinct colors and assign them round-robin to
+        // SPA patterns in order of nt count
+        Color[] cols = BackboneMultiplicityColor.getColors((int)unique_mt_count);
+        java.util.Set s = sm.entrySet();
+        java.util.Iterator iter = s.iterator();
+        java.util.HashMap spaColors = new java.util.HashMap();
+
+        int colI = 0;
+        while(iter.hasNext())
+        {
+        	java.util.Vector v = (java.util.Vector)((java.util.Map.Entry)iter.next()).getValue();
+        	for(int i = 0; i < v.size(); i++)
+        	{
+        		spaColors.put(makeBitSet((boolean[])v.elementAt(i)), cols[colI++]);
+        		if(colI == cols.length)
+        			colI = 0;
+        	}
+        }
+        
+        // set N-way backbone to mauve!!  :)
+        java.util.BitSet nway = new java.util.BitSet(xmfa.getSequenceCount());
+        nway.set(0, xmfa.getSequenceCount());
+        spaColors.put(nway, java.awt.Color.getHSBColor(276f/360f, 0.31f + 0.15f, 0.97f));
+
+        // now assign colors to segments
+        for( int bbI = 0; bbI < bb_by_mt.length; ++bbI )
+        {
+        	java.awt.Color c = (java.awt.Color)spaColors.get(makeBitSet(bb_by_mt[bbI].getSeqs()));
+        	if(c==null)
+        	{
+        		System.err.println("this is a bug, please report it...\n");
+        		c = Color.BLACK;
+        	}
+        	bb_by_mt[bbI].setColor(c);
+        }
+    }
+
+    public String toString()
+    {
+        return "Backbone multiplicity type";
+    }
+
+}
diff --git a/src/org/gel/mauve/color/ColorMenu.java b/src/org/gel/mauve/color/ColorMenu.java
new file mode 100644
index 0000000..5840cae
--- /dev/null
+++ b/src/org/gel/mauve/color/ColorMenu.java
@@ -0,0 +1,126 @@
+package org.gel.mauve.color;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JMenu;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.XmfaViewerModel;
+
+public class ColorMenu extends JMenu implements ActionListener {
+
+	BaseViewerModel model;
+	
+	JRadioButtonMenuItem bbLcbColor = new JRadioButtonMenuItem("LCB color");
+	JRadioButtonMenuItem bbMultiplicityColor = new JRadioButtonMenuItem("Backbone color");
+	JRadioButtonMenuItem lcbColor = new JRadioButtonMenuItem("LCB color");
+	JRadioButtonMenuItem offsetColor = new JRadioButtonMenuItem("Offset color");
+	JRadioButtonMenuItem normalizedOffsetColor = new JRadioButtonMenuItem("Normalized offset color");
+	JRadioButtonMenuItem multiplicityColor = new JRadioButtonMenuItem("Multiplicity color");
+	JRadioButtonMenuItem multiplicityTypeColor = new JRadioButtonMenuItem("Multiplicity type color");
+	JRadioButtonMenuItem normalizedMultiplicityTypeColor = new JRadioButtonMenuItem("Normalized multiplicity type color");
+
+    ButtonGroup bg = new ButtonGroup();
+
+	BackboneLcbColor backboneLcbColor = new BackboneLcbColor();
+	BackboneMultiplicityColor backboneMultiplicityColor = new BackboneMultiplicityColor();
+	LCBColorScheme lcbColorScheme = new LCBColorScheme();
+	MultiplicityColorScheme multiplicityColorScheme = new MultiplicityColorScheme();
+	MultiplicityTypeColorScheme multiplicityTypeColorScheme = new MultiplicityTypeColorScheme();
+	NormalizedMultiplicityTypeColorScheme normalizedMultiplicityTypeColorScheme = new NormalizedMultiplicityTypeColorScheme();
+	NormalizedOffsetColorScheme normalizedOffsetColorScheme = new NormalizedOffsetColorScheme();
+	OffsetColorScheme offsetColorScheme = new OffsetColorScheme();
+	
+	public ColorMenu()
+	{
+		bbLcbColor.setActionCommand(bbLcbColor.getText());
+		bbMultiplicityColor.setActionCommand(bbMultiplicityColor.getText());
+		lcbColor.setActionCommand(lcbColor.getText());
+		offsetColor.setActionCommand(offsetColor.getText());
+		normalizedOffsetColor.setActionCommand(normalizedOffsetColor.getText());
+		multiplicityColor.setActionCommand(multiplicityColor.getText());
+		multiplicityTypeColor.setActionCommand(multiplicityTypeColor.getText());
+		normalizedMultiplicityTypeColor.setActionCommand(normalizedMultiplicityTypeColor.getText());
+		bbLcbColor.addActionListener(this);
+		bbMultiplicityColor.addActionListener(this);
+		lcbColor.addActionListener(this);
+		offsetColor.addActionListener(this);
+		normalizedOffsetColor.addActionListener(this);
+		multiplicityColor.addActionListener(this);
+		multiplicityTypeColor.addActionListener(this);
+		normalizedMultiplicityTypeColor.addActionListener(this);
+	}
+	
+	public void build(BaseViewerModel model)
+	{
+		bg = new ButtonGroup();
+        bg.add(bbLcbColor);
+        bg.add(bbMultiplicityColor);
+        bg.add(lcbColor);
+        bg.add(offsetColor);
+        bg.add(normalizedOffsetColor);
+        bg.add(multiplicityColor);
+        bg.add(multiplicityTypeColor);
+        bg.add(normalizedMultiplicityTypeColor);
+
+        this.removeAll();
+
+        this.model = model;
+        if (model instanceof XmfaViewerModel)
+        {
+        	boolean have_bb = ((XmfaViewerModel)model).getBackboneList() != null;
+        	if(have_bb)
+        	{
+        		add(bbLcbColor);
+        		add(bbMultiplicityColor);
+        		bbLcbColor.setSelected(true);
+        	}else{
+        		setEnabled(false);
+        		JRadioButtonMenuItem lcbColor = new JRadioButtonMenuItem("LCB color");
+        		add(lcbColor);
+        	}
+        }
+        else
+        {
+        	if(model instanceof LcbViewerModel)
+        		add(lcbColor);
+        	add(offsetColor);
+        	add(normalizedOffsetColor);
+        	add(multiplicityColor);
+        	add(multiplicityTypeColor);
+
+    		if (model.getSequenceCount() < 62)
+        		add(normalizedMultiplicityTypeColor);
+
+        	if(model instanceof LcbViewerModel)
+        		lcbColor.setSelected(true);
+        	else
+        		offsetColor.setSelected(true);
+        }
+		
+	}
+
+	public void actionPerformed(ActionEvent e)
+    {
+        if (e.getActionCommand() == bbLcbColor.getText())
+            model.setColorScheme(backboneLcbColor);
+        if (e.getActionCommand() == bbMultiplicityColor.getText())
+            model.setColorScheme(backboneMultiplicityColor);
+        if (e.getActionCommand() == lcbColor.getText())
+            model.setColorScheme(lcbColorScheme);
+        if (e.getActionCommand() == offsetColor.getText())
+            model.setColorScheme(offsetColorScheme);
+        if (e.getActionCommand() == normalizedOffsetColor.getText())
+            model.setColorScheme(normalizedOffsetColorScheme);
+        if (e.getActionCommand() == multiplicityColor.getText())
+            model.setColorScheme(multiplicityColorScheme);
+        if (e.getActionCommand() == multiplicityTypeColor.getText())
+            model.setColorScheme(multiplicityTypeColorScheme);
+        if (e.getActionCommand() == normalizedMultiplicityTypeColor.getText())
+            model.setColorScheme(normalizedMultiplicityTypeColorScheme);
+    }
+}
diff --git a/src/org/gel/mauve/color/LCBColorScheme.java b/src/org/gel/mauve/color/LCBColorScheme.java
new file mode 100644
index 0000000..0625712
--- /dev/null
+++ b/src/org/gel/mauve/color/LCBColorScheme.java
@@ -0,0 +1,57 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.LCB;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.Match;
+
+/**
+ * Color matches by their LCB number. This will also set the color and
+ * match_color field for all LCBs.
+ */
+public class LCBColorScheme implements ColorScheme {
+	private final static double BUMP_SIZE = 1d / 6d;
+
+	private boolean lcbColorsComputed = false;
+
+	public void apply (BaseViewerModel model) {
+		LcbViewerModel lcbModel = (LcbViewerModel) model;
+
+		if (!lcbColorsComputed) {
+			computeLCBColors (lcbModel);
+			lcbColorsComputed = true;
+		}
+
+		for (int i = 0; i < model.getMatchCount (); i++) {
+			Match m = model.getMatch (i);
+			if (m.lcb >= 0) {
+				m.color = lcbModel.getVisibleLcb (m.lcb).match_color;
+			}
+		}
+	}
+
+	private void computeLCBColors (LcbViewerModel model) {
+		double colorIncrement = 1d / (double) model.getLcbCount ();
+		for (int i = 0; i < model.getFullLcbList ().length; i++) {
+			LCB lcb = model.getFullLcbList ()[i];
+
+			double wrapBump = ((double) i * BUMP_SIZE) / 1d;
+			double colorVal = ((double) i * BUMP_SIZE) % 1d;
+
+			// color the match based on its LCB number.
+			double hue = wrapBump * colorIncrement + colorVal;
+			Color color = Color.getHSBColor ((float) hue, MATCH_SAT,
+					MATCH_BRIGHT);
+			lcb.match_color = color;
+			lcb.color = color;
+		}
+	}
+
+	public String toString () {
+		return "LCB";
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/color/MultiplicityColorScheme.java b/src/org/gel/mauve/color/MultiplicityColorScheme.java
new file mode 100644
index 0000000..220c15c
--- /dev/null
+++ b/src/org/gel/mauve/color/MultiplicityColorScheme.java
@@ -0,0 +1,78 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+import java.util.Vector;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.Genome;
+import org.gel.mauve.Match;
+
+/**
+ * Colors the matches based on their multiplicity.
+ */
+public class MultiplicityColorScheme implements ColorScheme {
+	private Vector _matchColorTable;
+
+	public void apply (BaseViewerModel model) {
+		Vector matchColorTable = getMatchColorTable (model);
+
+		for (int matchI = 0; matchI < model.getMatchCount (); matchI++) {
+			Match cur_match = model.getMatch (matchI);
+
+			// color the match based on its multiplicity.
+			int color_count = 0;
+			for (int seqI = 0; seqI < model.getSequenceCount (); seqI++) {
+				Genome g = model.getGenomeByViewingIndex (seqI);
+				if (cur_match.getStart (g) != Match.NO_MATCH) {
+					color_count++;
+				}
+			}
+			color_count -= 2; // assume that all matches have multiplicity >=
+			// 2
+			cur_match.color = (Color) matchColorTable.elementAt (color_count);
+
+		}
+	}
+
+	private Vector getMatchColorTable (BaseViewerModel model) {
+		if (_matchColorTable == null) {
+			initColorTable (model);
+		}
+
+		return _matchColorTable;
+	}
+
+	private void initColorTable (BaseViewerModel model) {
+
+		_matchColorTable = new Vector ();
+
+		if (model.getSequenceCount () == 0) {
+			throw new RuntimeException (
+					"Model must have at least one sequence.");
+		}
+
+		// modulate the colors through the spectrum for different multiplicities
+		// 
+		// allow a fixed number of multiplicities for each modulation through
+		// the spectrum
+		int max_m = 3; // 5 for testing purposes.
+
+		int cycles = (int) Math.ceil ((double) model.getSequenceCount ()
+				/ (double) max_m);
+
+		for (int seqI = 0; seqI < model.getSequenceCount (); seqI++) {
+			float hue = ((float) seqI / (float) max_m) % 1;
+			// add the cycle increment
+			hue += ((1d / (double) max_m) / (double) cycles)
+					* Math.floor ((double) seqI / (double) max_m);
+			_matchColorTable.addElement (Color.getHSBColor (hue, MATCH_SAT,
+					MATCH_BRIGHT));
+		}
+	}
+
+	public String toString () {
+		return "Multiplicity";
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/color/MultiplicityTypeColorScheme.java b/src/org/gel/mauve/color/MultiplicityTypeColorScheme.java
new file mode 100644
index 0000000..039e820
--- /dev/null
+++ b/src/org/gel/mauve/color/MultiplicityTypeColorScheme.java
@@ -0,0 +1,46 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.Genome;
+import org.gel.mauve.Match;
+
+/**
+ * Color matches by their multiplicity and the sequences they match in. Will not
+ * work with more than 62 sequences.
+ */
+public class MultiplicityTypeColorScheme implements ColorScheme {
+
+	public void apply (BaseViewerModel model) {
+		if (model.getSequenceCount () > 62) {
+			throw new RuntimeException (
+					" Can't color by multiplicity type with more than 62 sequences.");
+		}
+
+		double mult_range = Math.pow (2, model.getSequenceCount ());
+
+		for (int matchI = 0; matchI < model.getMatchCount (); matchI++) {
+			Match cur_match = model.getMatch (matchI);
+
+			// color the match based on its multiplicity.
+			long color_type = 0;
+			for (int seqI = 0; seqI < model.getSequenceCount (); seqI++) {
+				Genome g = model.getGenomeByViewingIndex (seqI);
+				color_type <<= 1;
+				if (cur_match.getStart (g) != Match.NO_MATCH) {
+					color_type |= 1;
+				}
+			}
+			double hue = (double) color_type / mult_range;
+			cur_match.color = Color.getHSBColor ((float) hue, MATCH_SAT,
+					MATCH_BRIGHT);
+		}
+	}
+
+	public String toString () {
+		return "Multiplicity type";
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/color/NormalizedMultiplicityTypeColorScheme.java b/src/org/gel/mauve/color/NormalizedMultiplicityTypeColorScheme.java
new file mode 100644
index 0000000..88a5797
--- /dev/null
+++ b/src/org/gel/mauve/color/NormalizedMultiplicityTypeColorScheme.java
@@ -0,0 +1,73 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+import java.util.Comparator;
+import java.util.Vector;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.Match;
+
+/**
+ * Color matches by their multiplicity and the sequences they match in. Will not
+ * work with more than 62 sequences.
+ */
+public class NormalizedMultiplicityTypeColorScheme implements ColorScheme {
+	private final static Comparator MULTIPLICITY_TYPE_COMPARATOR = new Comparator () {
+		public int compare (Object o1, Object o2) {
+			Match a = (Match) o1;
+			Match b = (Match) o2;
+			return (int) (a.multiplicityType () - b.multiplicityType ());
+		}
+	};
+
+	public void apply (BaseViewerModel model) {
+
+		if (model.getSequenceCount () > 62) {
+			throw new RuntimeException (
+					"Can't color by multiplicity type with more than 62 sequences.");
+		}
+
+		if (model.getMatchCount () == 0)
+			return; // no sense in trying to color what's not there!
+
+		Vector matchesByMT = null;
+		long unique_mt_count = 0;
+		if (matchesByMT == null) {
+			matchesByMT = model.sortedMatches (MULTIPLICITY_TYPE_COMPARATOR);
+			unique_mt_count = 1;
+			long last_mt = ((Match) matchesByMT.get (0)).multiplicityType ();
+			// go thru the sorted list in order counting the number of different
+			// multiplicity types
+			for (int matchI = 1; matchI < matchesByMT.size (); matchI++) {
+				long cur_mt = ((Match) matchesByMT.get (matchI))
+						.multiplicityType ();
+				if (cur_mt != last_mt)
+					unique_mt_count++;
+				last_mt = cur_mt;
+			}
+		}
+
+		long cur_mt_count = 0;
+		long prev_mt = ((Match) matchesByMT.get (0)).multiplicityType ();
+		// go thru the sorted list in order assigning a new color to each new
+		// offset
+		for (int matchI = 0; matchI < matchesByMT.size (); matchI++) {
+			long cur_mt = ((Match) matchesByMT.get (matchI))
+					.multiplicityType ();
+			if (cur_mt != prev_mt)
+				cur_mt_count++;
+			prev_mt = cur_mt;
+
+			// map the generalized offset of this match to a hue
+			Match cur_match = (Match) matchesByMT.get (matchI);
+			float hue = (float) ((double) cur_mt_count / (double) unique_mt_count);
+			cur_match.color = Color.getHSBColor (hue, MATCH_SAT, MATCH_BRIGHT);
+		}
+	}
+
+	public String toString () {
+		return "Normalized multiplicity type";
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/color/NormalizedOffsetColorScheme.java b/src/org/gel/mauve/color/NormalizedOffsetColorScheme.java
new file mode 100644
index 0000000..f1c4033
--- /dev/null
+++ b/src/org/gel/mauve/color/NormalizedOffsetColorScheme.java
@@ -0,0 +1,82 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+import java.util.Comparator;
+import java.util.Vector;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.Match;
+
+/**
+ * Colors the matches based on their generalized offsets using the entire color
+ * spectrum evenly. Uses a very simple linear mapping approach for now. It can
+ * be tricked up later.
+ */
+public class NormalizedOffsetColorScheme implements ColorScheme {
+	private final static Comparator MATCH_OFFSET_COMPARATOR = new Comparator () {
+		public int compare (Object o1, Object o2) {
+			Match a = (Match) o1;
+			Match b = (Match) o2;
+			return (int) (a.offset () - b.offset ());
+		}
+	};
+
+	private Vector matchesByOffset;
+
+	private long uniqueOffsetCount;
+
+	public void apply (BaseViewerModel model) {
+		if (model.getMatchCount () == 0)
+			return; // no sense in trying to color what's not there!
+
+		Vector matchesByOffset = getMatchesByOffset (model);
+
+		double spectrum_bump = SPECTRUM_GAP / 2d;
+		long cur_offset_count = 0;
+		long prev_offset = ((Match) matchesByOffset.get (0)).offset ();
+		// go thru the sorted list in order assigning a new color to each new
+		// offset
+		for (int matchI = 0; matchI < matchesByOffset.size (); matchI++) {
+			long cur_offset = ((Match) matchesByOffset.get (matchI)).offset ();
+			if (cur_offset != prev_offset)
+				cur_offset_count++;
+			prev_offset = cur_offset;
+
+			// map the generalized offset of this match to a hue
+			// Match cur_match = (Match) matchVector.elementAt( matchI );
+			Match cur_match = (Match) matchesByOffset.get (matchI);
+			float hue = (float) (((1d - SPECTRUM_GAP) * ((double) cur_offset_count / (double) uniqueOffsetCount)) + spectrum_bump);
+			cur_match.color = Color.getHSBColor (hue, MATCH_SAT, MATCH_BRIGHT);
+		}
+	}
+
+	/**
+	 * @return Returns the list of the matches sorted by their generalized
+	 *         offset.
+	 */
+	private Vector getMatchesByOffset (BaseViewerModel model) {
+		// if the array of matches sorted by offset hasn't been created then
+		// create it!
+		if (matchesByOffset == null) {
+			matchesByOffset = model.sortedMatches (MATCH_OFFSET_COMPARATOR);
+			this.uniqueOffsetCount = 1;
+			long last_offset = ((Match) matchesByOffset.get (0)).offset ();
+			// go thru the sorted list in order counting the number of different
+			// offsets
+			for (int matchI = 1; matchI < matchesByOffset.size (); matchI++) {
+				long cur_offset = ((Match) matchesByOffset.get (matchI))
+						.offset ();
+				if (cur_offset != last_offset) {
+					this.uniqueOffsetCount = uniqueOffsetCount + 1;
+				}
+				last_offset = cur_offset;
+			}
+		}
+		return matchesByOffset;
+	}
+
+	public String toString () {
+		return "Normalized Offset";
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/color/OffsetColorScheme.java b/src/org/gel/mauve/color/OffsetColorScheme.java
new file mode 100644
index 0000000..774e269
--- /dev/null
+++ b/src/org/gel/mauve/color/OffsetColorScheme.java
@@ -0,0 +1,57 @@
+package org.gel.mauve.color;
+
+import java.awt.Color;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.Match;
+
+/**
+ * Colors the matches based on their generalized offsets. Uses a very simple
+ * linear mapping.
+ * 
+ * @see org.gel.mauve.color.NormalizedOffsetColorScheme for a different
+ *      approach.
+ */
+public class OffsetColorScheme implements ColorScheme {
+	private boolean initialized = false;
+
+	private long maxOffset = Long.MIN_VALUE;
+
+	private long minOffset = Long.MAX_VALUE;
+
+	public void apply (BaseViewerModel model) {
+		if (!initialized) {
+			initOffsets (model);
+			initialized = true;
+		}
+
+		// color by generalized offset (COLOR_OFFSET)
+		long offset_range = maxOffset - minOffset;
+		// bump offset_range up by a designated amount to stay out of the deep
+		// violet part of the spectrum
+		offset_range += (offset_range * SPECTRUM_GAP);
+		long offset_bump = (long) (offset_range * (SPECTRUM_GAP / 2));
+		for (int matchI = 0; matchI < model.getMatchCount (); matchI++) {
+			// map the generalized offset of this match to a hue
+			Match cur_match = model.getMatch (matchI);
+			long m_offset = cur_match.offset () - minOffset + offset_bump;
+			float hue = (float) m_offset / (float) offset_range;
+			cur_match.color = Color.getHSBColor (hue, MATCH_SAT, MATCH_BRIGHT);
+		}
+	}
+
+	private void initOffsets (BaseViewerModel model) {
+		for (int matchI = 0; matchI < model.getMatchCount (); matchI++) {
+			Match match = model.getMatch (matchI);
+			long offset = match.offset ();
+			maxOffset = offset > maxOffset ? offset : maxOffset;
+			minOffset = offset < minOffset ? offset : minOffset;
+		}
+	}
+
+	public String toString () {
+		return "Offset";
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/contigs/ChangedFastaFormat.java b/src/org/gel/mauve/contigs/ChangedFastaFormat.java
new file mode 100644
index 0000000..70e79d0
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ChangedFastaFormat.java
@@ -0,0 +1,25 @@
+package org.gel.mauve.contigs;
+
+import java.util.Hashtable;
+
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.io.FastaFormat;
+
+public class ChangedFastaFormat extends FastaFormat {
+	
+	protected Hashtable names;
+	
+	public ChangedFastaFormat (Hashtable map) {
+		names = map;
+	}
+
+	protected String describeSequence (Sequence seq) {
+		String desc = super.describeSequence(seq);
+		if (names.containsKey (desc))
+			desc = (String) names.get (desc);
+		return desc;
+	}
+	
+	
+
+}
diff --git a/src/org/gel/mauve/contigs/ChangedFeatureWriter.java b/src/org/gel/mauve/contigs/ChangedFeatureWriter.java
new file mode 100644
index 0000000..0162cc7
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ChangedFeatureWriter.java
@@ -0,0 +1,110 @@
+package org.gel.mauve.contigs;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.StrandedFeature;
+import org.gel.air.util.GroupHelpers;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.gui.sequence.FlatFileFeatureConstants;
+import org.gel.mauve.summary.output.AbstractTabbedDataWriter;
+
+public class ChangedFeatureWriter extends AbstractTabbedDataWriter 
+		implements FlatFileFeatureConstants {
+	
+	protected Iterator feats;
+	protected Feature feature;
+	protected Genome genome;
+	protected Chromosome chrom;
+	protected Hashtable inverters;
+	public static final int CONT_IND = 0;
+	public static final int LAB_IND = 1;
+	public static final int STR_IND = 2;
+	public static final int LEF_IND = 3;
+	public static final int RIGHT_IND = 4;
+	public static final int OLD_L_IND = 5;
+	public static final int OLD_R_IND = 6;
+	public static final int CHANGED_IND = 7;
+	public static final int TYPE_IND = 8;
+	public static final String OLD_L_STR = "prev_left";
+	public static final String OLD_R_STR = "prev_right";
+	public static final String REVERSED = "reversed";
+
+	public ChangedFeatureWriter (String file, Hashtable args, Iterator itty, Genome fix) {
+		super (file, args);
+		feats = itty;
+		if (!itty.hasNext ())
+			return;
+		genome = fix;
+		printHeaders ();
+		printData ();
+		doneWritingFile ();
+	}
+	
+	protected void initSubClassParticulars (Hashtable args) {
+		inverters = (Hashtable) args.get (ContigFeatureWriter.REVERSES);
+		super.initSubClassParticulars(args);
+	}
+
+	protected String getData (int column, int row) {
+		long ret = 0;
+		boolean changed = inverters.contains (chrom);
+		boolean rev = changed;
+		if (feature.getAnnotation ().containsProperty (REVERSED)) {
+			rev = new Boolean ((String) feature.getAnnotation ().getProperty (
+					REVERSED)).booleanValue ();
+			if (changed)
+				rev = !rev;
+		}
+		if (!changed && (column == LEF_IND || column == RIGHT_IND))
+			column += 2;
+		switch (column) {
+			case CONT_IND:
+				return chrom.getName ();
+			case LAB_IND:
+				return MauveHelperFunctions.getUniqueId (feature);
+			case STR_IND:
+				return ((StrandedFeature) feature).getStrand () == (changed ?
+					StrandedFeature.NEGATIVE : StrandedFeature.POSITIVE) ? FORWARD : COMPLEMENT;
+			case LEF_IND:
+			case OLD_R_IND:
+				ret = feature.getLocation ().getMax ();
+				break;
+			case RIGHT_IND:
+			case OLD_L_IND:
+				ret = feature.getLocation ().getMin (); 
+				break;
+			case CHANGED_IND:
+				return rev + "";
+			case TYPE_IND:
+				return "asap";
+		}
+		ret = ret - chrom.getStart () + 1;
+		if (changed && column < OLD_L_IND)
+			ret = chrom.getLength () - ret + 1;
+		return ret + "";
+	}
+
+	protected boolean moreRowsToPrint () {
+		return feats.hasNext ();
+	}
+
+	protected Vector setColumnHeaders () {
+		String [] headers = {CONTIG_STRING, LABEL_STRING, STRAND_STRING,
+				LEFT_STRING, RIGHT_STRING, OLD_L_STR, OLD_R_STR, REVERSED, TYPE_STRING};
+		Vector heads = new Vector (headers.length);
+		GroupHelpers.arrayToCollection (heads, headers);
+		return heads;
+	}
+
+	protected boolean shouldPrintRow (int row) {
+		feature = (Feature) feats.next ();
+		chrom = genome.getChromosomeAt (feature.getLocation ().getMin ());
+		return feature.getType().equalsIgnoreCase ("source") ? false : true;
+	}
+
+}
diff --git a/src/org/gel/mauve/contigs/ContigFeatureWriter.java b/src/org/gel/mauve/contigs/ContigFeatureWriter.java
new file mode 100644
index 0000000..5891ab6
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigFeatureWriter.java
@@ -0,0 +1,129 @@
+package org.gel.mauve.contigs;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Vector;
+
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.gui.sequence.FlatFileFeatureConstants;
+import org.gel.mauve.summary.output.AbstractTabbedDataWriter;
+
+public class ContigFeatureWriter extends AbstractTabbedDataWriter 
+		implements FlatFileFeatureConstants {
+
+	public static final String REVERSES = "Contigs to reverse";
+	public static final String ORDERED_CONTIGS = "Ordered Contigs";
+	public static final String CONFLICTED_CONTIGS = 
+			"Contigs with conflicting ordering information";
+	protected Iterator contigs;
+	protected Chromosome current;
+	protected Hashtable inverters;
+	protected Hashtable use;
+	protected HashSet inverted;
+	
+	public ContigFeatureWriter (String file, Hashtable args) {
+		super(file, args);
+	}
+	
+	protected void initSubClassParticulars (Hashtable args) {
+		inverters = (Hashtable) args.get (REVERSES);
+		Vector sorted = new Vector ((inverters).keySet ());
+		Collections.sort (sorted);
+		use = inverters;
+		inverted = (HashSet) args.get(COMPLEMENT);
+		super.initSubClassParticulars (args);
+		correctPseudoCoords ((LinkedList) args.get (ORDERED_CONTIGS));
+		printContigs (sorted, REVERSES);
+		printContigs ((LinkedList) args.get (ORDERED_CONTIGS), ORDERED_CONTIGS);
+		use = (Hashtable) args.get (CONFLICTED_CONTIGS);
+		if (use != null &&  use.size () > 0)
+			printContigs (use.keySet (), CONFLICTED_CONTIGS);
+		doneWritingFile ();
+	}
+	
+	protected void correctPseudoCoords (LinkedList chroms) {
+		long start = 1;
+		for (int i = 0; i < chroms.size(); i++) {
+			Chromosome old = (Chromosome) chroms.get(i);
+			Chromosome chrom = new Chromosome (start, start + old.getLength() - 1,
+					old.getName(), old.getCircular());
+			chroms.set(i, chrom);
+			start = chrom.getEnd() + 1;
+		}
+	}
+
+	protected void printContigs (Collection conts, String descriptor) {
+		System.out.println ("printing");
+		if (conts.size() > 0) {
+			contigs = conts.iterator ();
+			out.println (descriptor);
+			printHeaders ();
+			printData ();
+			out.println ("\n");
+		}
+	}
+
+	protected String getData (int column, int row) {
+		switch (column) {
+			case TYPE:
+				return CONTIG_STRING;
+			case LABEL:
+				return current.getName ();
+			case CONTIG:
+				return "chromosome";
+			case STRAND:
+				boolean invert;
+				if (inverted != null)
+					invert = inverted.contains(current.getName());
+				else
+					invert = inverters.containsKey (current.getName());
+				return invert ? COMPLEMENT : FORWARD;
+			case LEFT:
+				return current.getStart () + "";
+			case RIGHT:
+				return current.getEnd () + "";
+			/*case LEFT:
+				return 1 + "";
+			case RIGHT:
+				return current.relativeLocation (current.getEnd ()) + "";*/
+			default:
+				return null;
+		}
+	}
+	
+	public void printHeaderInfoForFile () {
+		out.println ("Contigs in Reversed Category are those reversed from " + 
+				"the order immediately preceding.\n  The strand is forward if the " +
+				"contig is oriented the same as the original input,\nand complement" +
+				" otherwise.  The left and right ends are in\npseudomolecule " +
+				"coordinates.  The ordered contigs contain all contigs in the " +
+				"correct order,\n and those in the conflicted category had multiple "
+				+ "possible orders.");
+	}
+
+	protected boolean moreRowsToPrint () {
+		return contigs.hasNext ();
+	}
+
+	protected Vector setColumnHeaders () {
+		return null;
+	}
+	public void setColumnHeaders (Vector v) {
+		headers = FLAT_FEATURE_REQ_INFO;
+		current_row = new String [headers.length];
+	}
+
+	protected boolean shouldPrintRow (int row) {
+		Object obj = contigs.next ();
+		if (obj instanceof Chromosome)
+			current = (Chromosome) obj;
+		else
+			current = (Chromosome) use.get (obj);
+		return true;
+	}
+
+}
diff --git a/src/org/gel/mauve/contigs/ContigGrouper.java b/src/org/gel/mauve/contigs/ContigGrouper.java
new file mode 100644
index 0000000..14d3857
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigGrouper.java
@@ -0,0 +1,349 @@
+package org.gel.mauve.contigs;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.analysis.SegmentComparator;
+import org.gel.mauve.backbone.Backbone;
+import org.gel.mauve.backbone.BackboneList;
+import org.gel.mauve.histogram.ZoomHistogram;
+
+public class ContigGrouper implements MauveConstants {
+	
+	protected ContigReorderer central;
+	protected int min_edge_bp;
+	protected int min_contig_bp;
+	protected double min_percent;
+	protected double min_contig_ratio;
+	protected Genome fix;
+	protected ZoomHistogram how_similar;
+	protected Hashtable groups;
+	protected byte min_similarity;
+	protected byte med_similarity;
+	protected SegmentComparator compare;
+	protected int genome_ind;
+	protected BackboneList bb;
+	
+	
+	public ContigGrouper (ContigReorderer orderer, int bp, double percent) {
+		central = orderer;
+		fix = central.fix;
+		genome_ind = fix.getSourceIndex ();
+		groups = new Hashtable ();
+		XmfaViewerModel model = (XmfaViewerModel) central.model;
+		how_similar = model.getSim (fix);
+		bb = model.getBackboneList ();
+		min_edge_bp = bp;
+		min_contig_bp = 200;
+		min_percent = percent;
+		min_similarity = 0;
+		med_similarity = 42;
+		min_contig_ratio = .10;
+		compare = new SegmentComparator (genome_ind, false);
+	}
+	
+	public Chromosome getFirstOfLinkedContigs (LCB lcb, boolean left) {
+		return getFirstOfLinkedContigs (lcb, left, null);
+	}
+	
+	public Chromosome getFirstOfLinkedContigs (LCB lcb, boolean left, ContigGroup group) {
+		Chromosome start = getEndChromosome (lcb, left);
+		groups.put (lcb, group);
+		Chromosome start2 = null;
+		LCB prior = lcb;
+		do {
+			lcb = central.getAdjacentLCB(left, lcb);
+			if (lcb == null)
+				break;
+			start2 = getEndChromosome (lcb, !left);
+			if (start2 == start) {
+				groups.put (start, this);
+				start = getEndChromosome (lcb, left);
+				prior = lcb;
+			}
+			else
+				break;
+		} while (true);
+		if (group != null) {
+			if (left) {
+				group.start = start;
+				group.first = prior;
+			}
+			else {
+				group.end = start;
+				group.last = prior;
+			}
+		}
+		return start;
+	}
+	
+	public boolean onSingleContig (LCB lcb) {
+		return getEndChromosome (lcb, true) == getEndChromosome (lcb, false); 
+	}
+	
+	public Chromosome getEndChromosome (LCB lcb, boolean left) {
+		long pos = left ? lcb.getLeftEnd (fix) : lcb.getRightEnd (fix) - 1;
+		Chromosome chrom = fix.getChromosomeAt (pos);
+		if (chrom == fix.getChromosomeAt (left ? lcb.getRightEnd (fix) - 1 : 
+			lcb.getLeftEnd (fix)) || isSolidlyOnContig (lcb, chrom, left, pos))
+			return chrom;
+		else {
+			lcb = central.getAdjacentLCB (left, lcb);
+			if (lcb != null) {
+				pos = left ? lcb.getRightEnd (fix) - 1 : lcb.getLeftEnd (fix);
+				Chromosome chrom2 = fix.getChromosomeAt (pos);
+				if (chrom2 == chrom /*&& isSolidlyOnContig (lcb, chrom, !left, pos)*/) {
+					pos = left ? chrom.getEnd () + 1 : chrom.getStart () - 1;
+					chrom = fix.getChromosomeAt (pos);
+				}
+			}
+			return chrom;
+		}
+	}
+	
+	/*
+	 * assumes lcb partially overlaps one side of the chromosome.  Since left refers to whether
+	 * end is the left or right side, the lcb would overlap the right side of chrom if left is
+	 * true, otherwise, the opposite is true.
+	 */
+	protected boolean isSolidlyOnContig (LCB lcb, Chromosome chrom, boolean left, long end) {
+		long amount = left ? chrom.getEnd () - end + 1 : end - chrom.getStart () + 1;
+		byte similarity = -1;
+		if (left)
+			similarity = how_similar.getValueForRange (end, chrom.getEnd ());
+		else
+			similarity = how_similar.getValueForRange (chrom.getStart (), end);
+		if (amount < min_contig_bp && amount > min_edge_bp && similarity > med_similarity)
+			return true;
+		if (similarity < min_similarity || amount < min_edge_bp || 
+				(amount / (double) chrom.getLength () < 
+				min_contig_ratio && amount / ((double) lcb.getLength (fix)) < min_percent)) {
+			if (!(similarity < min_similarity)) {
+				if (amount > min_edge_bp)
+				System.out.println ("not enought overlap: " + chrom);
+			}
+			/*else if (amount > min_edge_bp)
+				System.out.println ("bad similarity: " + similarity + " " + chrom + "\n" + lcb.getRightEnd (fix));*/
+			return false;
+		}
+		return true;
+	}
+	
+	public boolean isSolidlyOnContig (LCB lcb, Chromosome chrom, boolean left) {
+		boolean ret = isSolidlyOnContig (lcb, chrom, !left, left ? lcb.getRightEnd (fix) - 1 : 
+			lcb.getLeftEnd (fix));
+		//System.out.println ("solid: " + lcb.getLeftEnd (fix) + " " + ret);
+		//System.out.println ("chrom: " + chrom);
+		return ret;
+	}
+	
+	public boolean onlyLCBOnContig (LCB lcb, Chromosome chrom) {
+		boolean alone;
+		LCB prev = central.getAdjacentLCB (true, lcb);
+		LCB next = central.getAdjacentLCB (false, lcb);
+		if ((prev != null && getEndChromosome (prev, false) == chrom) || 
+				(next != null && getEndChromosome (next, true) == chrom))
+			alone = false;
+		else
+			alone = true;
+		//System.out.println ("alone: " + lcb.getLeftEnd (fix) + " " + alone);
+		//System.out.println ("chrom: " + chrom);
+		return alone;
+	}
+	
+	public ContigGroup getContigGroup (LCB lcb) {
+		ContigGroup group = (ContigGroup) groups.get (lcb);
+		if (group != null)
+			return group;
+		else
+			return new ContigGroup (lcb);
+	}
+	
+	public class ContigGroup {
+
+		protected Chromosome start;
+		protected Chromosome end;
+		protected LCB first;
+		protected LCB last;
+		protected long weight;
+		protected Boolean reversed;
+		//protected int num_lcbs;
+
+
+		public ContigGroup (LCB lcb) {
+			getFirstOfLinkedContigs (lcb, true, this);
+			getFirstOfLinkedContigs (lcb, false, this);
+			LinkedList list = getNonEmpty ();
+			if (list.size() == 0)
+				System.out.println ("size 0: " + lcb.getLeftEnd(fix));
+			start = (Chromosome) list.getFirst();
+			end = (Chromosome) list.getLast();
+			calculateWeight ();
+		}
+		
+		public long getLength () {
+			Iterator itty = getNonEmpty ().iterator ();
+			long total = 0;
+			while (itty.hasNext ())
+				total += ((Chromosome) itty.next ()).getLength ();
+			return total;
+		}
+		
+		/*protected void setCount {
+		  	LCB temp = first;
+			do {
+			num_lcbs++;
+			temp = central.getAdjacent (false, temp);
+		} while (temp != central.getAdjacent (false, last));
+		}*/
+
+		public boolean matchedToEdge (boolean left) {
+			long lcb = left ? first.getLeftEnd(fix) : last.getRightEnd(fix);
+			long chrom = left ? start.getStart() : end.getEnd();
+			lcb = left ? lcb - chrom : chrom - lcb;
+			//System.out.println ("matched: " + left + " lcb: " + lcb + " chrom: " + chrom);
+			//System.out.println ("lcb: " + (left ? 
+			//		first.getLeftEnd (fix) : last.getLeftEnd (fix)));
+			if (lcb < min_edge_bp)
+				return true;
+			else
+				return false;
+		}
+		
+		public String toString () {
+			return "chrom 1: " + start + "\nchrom 2: " + end + "\nlcb 1: " + first +
+					"\nlcb 2: " + last;
+		}
+		
+		public boolean isReversed () {
+			if (reversed != null)
+				return reversed.booleanValue ();
+			else
+				return false;
+				//return central.isReversed (first) && central.isReversed (last);
+		}
+		
+		public void setReversed (boolean reverse) {
+			//System.out.println ("reversing: " + reverse + " " + toString ());
+			reversed = reverse ? Boolean.TRUE : Boolean.FALSE;
+		}
+		
+		public HashSet getLCBs () {
+			LCB current = first;
+			HashSet lcbs = new HashSet ();
+			LCB past = central.getAdjacentLCB(false, last);
+			do {
+				lcbs.add (current);
+				current = central.getAdjacentLCB (false, current);
+			} while (current != past);
+			return lcbs;
+		}
+		
+		public void calculateWeight () {
+			Iterator itty = getLCBs ().iterator ();
+			while (itty.hasNext ())
+				weight += ((LCB) itty.next ()).weight;
+		}
+		
+		public boolean mostWeightForward () {
+			Iterator itty = getLCBs ().iterator ();
+			int weight1 = 0;
+			int weight2 = 0;
+			while (itty.hasNext ()) {
+				LCB lcb = (LCB) itty.next ();
+				if (central.isReversed(lcb))
+					weight2 += lcb.weight;
+				else
+					weight1 += lcb.weight;
+			}
+			return weight1 > weight2;
+		}
+		
+		public LinkedList getNonEmpty () {
+			LinkedList contigs = new LinkedList ();
+			if (start == end) {
+				contigs.add(start);
+				return contigs;
+			}
+			Backbone seg = null;
+			Chromosome current = start;
+			LCB lcb = first;
+			//System.out.println ("getting nonempty");
+			do {
+				//System.out.println ("grouping: " + current + " lcb: " + lcb.getLeftEnd (fix));
+				long start = Math.max (lcb.getLeftEnd (fix), current.getStart ());
+				if (lcb.getLeftEnd (fix) < current.getStart () && 
+						lcb.getRightEnd (fix) < current.getEnd () && !isSolidlyOnContig (lcb,
+								current, true) && !(lcb == last)) {
+					start = lcb.getRightEnd (fix) + 1;
+					lcb = central.getAdjacentLCB (false, lcb);
+				}
+				//System.out.println ("getting seg");
+				seg = bb.getNextBackbone (fix, start);
+				//System.out.println ("got seg");
+				long total = 0;
+				do {
+					if (seg != null && current.getEnd() > seg.left [genome_ind]) {
+						long match_length = seg.getSegmentLength (genome_ind);
+						long end = Math.min (current.getEnd (), seg.right [genome_ind]);
+						start = Math.max (seg.left [genome_ind], current.getStart ());
+						long length = end - start;
+						if (((length / (double) current.getLength() > .51) ||
+								(length / (double) match_length) > min_percent) && 
+								how_similar.getValueForRange (start, end) > min_similarity)
+							total += length;
+						boolean add = false;
+						if (total / (double) current.getLength () > min_contig_ratio) {
+							add = true;
+						}
+						/*if (!add && seg.getRightEnd (fix) > current.getEnd ()) {
+							if ((onlyLCBOnContig (lcb, current) || onSingleContig (lcb))
+									&& total > min_contig_bp)
+								add = true;
+						}*/
+						//System.out.println ("in middle: " + seg);
+						if (!(seg.getRightEnd (fix) > current.getEnd ())) {
+							seg = bb.getNextBackbone (fix, seg.right [genome_ind] + 1);
+							//System.out.println ("new seg: " + seg);
+							if (seg == null || seg.getLeftEnd (fix) > lcb.getRightEnd (fix)) {
+								/*if ((onlyLCBOnContig (lcb, current) || onSingleContig (lcb))
+										&& total > min_contig_bp)
+									add = true;*/
+								boolean past = lcb == last;
+								//System.out.println ("last: " + past);
+								if (!past) {
+									lcb = central.getAdjacentLCB (false, lcb);
+									//System.out.println ("new lcb: " + (lcb == null ? "end" : lcb.getLeftEnd (fix) + ""));
+								}
+								if (past || lcb == null || (lcb.getRightEnd (fix) > current.getEnd () &&
+										!isSolidlyOnContig (lcb, current, false)))
+									seg = null;
+							}
+						}
+						else
+							seg = null;
+						if (add) {
+							//System.out.println ("adding: " + current);
+							contigs.add (current);
+							break;
+						}
+					}
+				} while (seg != null && seg.left [genome_ind] < current.getEnd ());
+				current = fix.getChromosomeAt(current.getEnd() + 1);
+			} while (current != null && current.getStart () < end.getEnd ());
+			if (contigs.size() < 1)
+				System.out.println ("group determined empty");
+			return contigs;
+		}
+		
+	}
+	
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/contigs/ContigHandler.java b/src/org/gel/mauve/contigs/ContigHandler.java
new file mode 100644
index 0000000..66b975d
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigHandler.java
@@ -0,0 +1,37 @@
+package org.gel.mauve.contigs;
+
+import org.gel.mauve.analysis.Segment;
+
+public interface ContigHandler {
+	
+	/**
+	 * changes a pseudolocation to a location relative to the
+	 * start of the contig the the location is on.
+	 * 
+	 * @param sequence The integer representing the model index of the desired sequence
+	 * @param loci		The coordinate (location) of interest
+	 * @return		A long representing the location relative to the beginning of the
+	 * 				contig it's on.
+	 */
+	public long getContigCoord (int sequence, long loci);
+	
+	/**
+	 * returns the name of the contig containing the desired location
+	 * 
+	 * @param sequence		The integer representing the model index of the desired sequence
+	 *  @param loci		The coordinate (location) of interest
+	 *  @return			The name of the contig containing the desired location
+	 */
+	public String getContigName (int sequence, long loci);
+	
+	/**
+	 * Splits a segment into pieces separated at contig boundaries
+	 * 
+	 * @param sequence		The integer representing the model index of the desired sequence
+	 * @param segment		The coordinate (location) of interest
+	 */
+	public void fixSegmentByContigs (int sequence, Segment segment);
+	
+	public long getPseudoCoord (int sequence, long loci, String contig);
+
+}
diff --git a/src/org/gel/mauve/contigs/ContigInverter.java b/src/org/gel/mauve/contigs/ContigInverter.java
new file mode 100644
index 0000000..b0c18a3
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigInverter.java
@@ -0,0 +1,496 @@
+package org.gel.mauve.contigs;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.MauveHelperFunctions;
+
+
+public class ContigInverter implements MauveConstants {
+	
+	protected LcbViewerModel model;
+	protected ContigReorderer central;
+	protected double min_length_ratio;
+	protected ContigGrouper grouper;
+	protected HashSet groups;
+	protected Hashtable befores;
+	protected Hashtable afters;
+	//protected int looping;
+	
+	public ContigInverter (LcbViewerModel mod, ContigReorderer reorder) {
+		init (mod, reorder);
+		reorderContigs ();
+	}
+	
+	public ContigInverter (LcbViewerModel mod, ContigReorderer reorder, String file) {
+		System.out.println ("reading order file");
+		init (mod, reorder);
+		central.readKeepers (file);
+		//invertContigs ();
+		/*placeConflicts ();
+		groups.clear ();
+		matchEdges ();*/
+	}
+	
+	protected void init (LcbViewerModel mod, ContigReorderer reorder) {
+		model = mod;
+		central = reorder;
+		befores = new Hashtable ();
+		afters = new Hashtable ();
+		groups = new HashSet ();
+		grouper = central.grouper;
+	}
+	
+	public void placeUntouched () {
+		long multiplicity = 0;
+		for (int i = 0; i < model.getSequenceCount (); i++) {
+			multiplicity <<= 1;
+			if (i == central.fix.getSourceIndex () || i == central.ref.getSourceIndex ())
+				multiplicity |= 1;
+		}
+		outer: for (int i = 0; i < central.lcbs.length; i++) {
+			//System.out.println ("lcb: " + central.lcbs [i]);
+			if (central.lcbs [i].multiplicityType () == multiplicity) {
+				ContigGrouper.ContigGroup group = grouper.getContigGroup (central.lcbs [i]);
+				/*Iterator itty = group.getLCBs ().iterator ();
+				while (itty.hasNext ()) {
+					if (((LCB) itty.next ()).multiplicityType () != multiplicity)
+						continue outer;
+				}*/
+				Iterator itty = group.getNonEmpty ().iterator ();
+				while (itty.hasNext ()) {
+					if (central.ordered.contains (itty.next ()))
+						continue outer;
+				}
+				//System.out.println ("past mult");
+				int ind = 0;
+				int ind2 = central.ordered.size () + 1;
+				if (locationUnique (group, i)) {
+					if (i > 0)
+						ind = central.ordered.indexOf (grouper.getContigGroup (
+								central.lcbs [i - 1]).end);
+					if (i < central.lcbs.length - 1)
+						ind2 =  central.ordered.indexOf (grouper.getContigGroup (
+								central.lcbs [i + 1]).start);
+					boolean ok = ind2 - ind == 1;
+					addContigGroup (group, ok, ind2);
+					if (group.isReversed ())
+						central.addGroupToInverters (group);
+				}
+			}
+		}
+	}
+	
+	//method no longer necessary, does as ordering
+	public void invertContigs () {
+		LCB [] ordered_lcbs = central.fix_lcbs;
+		for (int i = 0; i < ordered_lcbs.length; i++) {
+			if (ordered_lcbs [i].getLeftEnd (central.ref) != 0 && 
+					ordered_lcbs [i].getLeftEnd (central.fix) != 0) {
+				ContigGrouper.ContigGroup group = grouper.getContigGroup (ordered_lcbs [i]);
+				if (!groups.contains (group.toString ()) && !group.isReversed() && 
+						!group.mostWeightForward()) {
+					group.setReversed(true);
+					if (group.start != group.end) {
+						System.out.println ("LCB spans reversible contig");
+						System.out.println ("chrom1: " + group.start);
+						System.out.println ("chrom2: " + group.end);
+					}
+					central.addGroupToInverters (group);
+					groups.add (group.toString ());
+					while (i < ordered_lcbs.length && ordered_lcbs [i] != group.last)
+						i++;
+				}
+			}
+		}
+		System.out.println ("inverters: " + central.inverters.size());
+	}
+	
+	public boolean contigReversed (Chromosome contig, LCB lcb) {
+		ContigGrouper.ContigGroup group = grouper.getContigGroup (lcb);
+		return group.isReversed ();
+	}
+	
+	public void reorderContigs () {
+		int lcb_index = 0;
+		for (; lcb_index < central.lcbs.length; lcb_index++) {
+			if (central.lcbs [lcb_index].getLeftEnd(central.fix) != 0) {
+				orderContigGroup (lcb_index);
+			}
+		}
+		groups.clear ();
+		placeConflicts ();
+		groups.clear ();
+		matchEdges ();
+	}
+
+	public void matchEdges () {
+		System.out.println ("matching edges");
+		HashSet l_matched = new HashSet ();
+		HashSet r_matched = new HashSet ();
+		ContigGrouper.ContigGroup group1 = null;
+		ContigGrouper.ContigGroup group2 = null;
+		boolean reversed1 = false;
+		boolean reversed2 = false;
+		boolean last = true;
+		boolean first = true;
+		for (int gen = 0; gen < central.ordered_genomes.length; gen++) {
+			//System.out.println ("matching genome: " + gen);
+			central.setReference(central.ordered_genomes [gen]);
+			for (int i = 0; i < central.lcbs.length - 1; i++) {
+				group1 = grouper.getContigGroup (central.lcbs [i]);
+				/*if (print (group1))
+					System.out.println ("match: " + group1.toString ());
+				else if (looping > 0)
+					System.out.println ("matching diff after loop started: " + group1);*/
+				do {
+					first = true;
+					reversed1 = central.isReversed (last ? group1.last : group1.first);
+					/*if (print (group1))
+						System.out.println ("last: " + last + " rev1: " + reversed1);*/
+					if (last != reversed1 && !(last ? r_matched.contains (group1.last) :
+						l_matched.contains (group1.first)) && group1.matchedToEdge (reversed1)
+						&& central.containsEndOfLCB (group1, reversed1)) {
+						do {
+							group2 = grouper.getContigGroup (central.lcbs [i + 1]);
+							/*if (print (group1))
+								System.out.println ("i: " + group2);
+							else if (print (group2)) {
+								System.out.println ("group is second, here is first: ");
+								System.out.println (group1);
+							}*/
+							reversed2 = central.isReversed (first ? group2.first : group2.last);
+							/*if (print (group1))
+								System.out.println ("first: " + first + " rev2: " + reversed2);*/
+							if ((first == reversed2) || (first ? l_matched.contains (group2.first)
+									: r_matched.contains (group2.last)))
+								group2 = group1;
+							//first condition is new 5.10.10
+							if (noCircle (group1, group2) && group2.start != group1.start && group2.matchedToEdge(!reversed2) &&
+									central.containsEndOfLCB (group2, !reversed2) &&
+									central.lcbs [i] == (reversed1 ?  group1.first : group1.last) &&
+									central.lcbs [i + 1] == (reversed2 ?  group2.last : group2.first) &&
+									central.lcbs [i + 1].getLeftEnd(central.ref) - 
+									central.lcbs [i].getRightEnd(central.ref) < 
+									ContigReorderer.MAX_IGNORABLE_DIST * 2) {
+								int ind = central.ordered.indexOf (reversed2 ? group2.end :
+									group2.start);
+								int ind2 = central.ordered.indexOf (group1.isReversed () ? 
+										group1.start : group1.end);
+								//System.out.println ("ind1: " + ind +" ind2:"+ ind2);
+								if (ind != 0 && ind2 != 0) {
+									if (last)
+										r_matched.add (group1.last); 
+									else
+										l_matched.add (group1.first);
+									if (first)
+										l_matched.add (group2.first);
+									else
+										r_matched.add (group2.last);
+									ind = ind - ind2;
+									//System.out.println ("i: " + i);
+									boolean after = group1.weight > group2.weight;
+									if (after)
+										group2.weight = group1.weight;
+									else
+										group1.weight = group2.weight;
+									//System.out.println ("g1: " + group1.weight + " gr2: " + group2.weight);
+									System.out.println ("new group: " + central.lcbs [i].getRightEnd(central.fix) + ", " +
+											central.lcbs [i + 1].getLeftEnd(central.fix) + " " + after);
+									System.out.println ("rev1: " + reversed1 + "rev2: " + reversed2);
+									boolean leader = group1.getLength () > group2.getLength ();
+									if (reversed1 != reversed2) {
+										if (group1.isReversed () == group2.isReversed ()) {
+											System.out.println ("one longer: " + leader);
+											if (leader)
+												setReversed (group2, !group2.isReversed ());
+											else {
+												setReversed (group1, !group1.isReversed ());
+												reversed1 = !reversed1;
+											}
+										}
+									}
+									else if (group1.isReversed () != group2.isReversed ()) {
+										boolean rev = leader && group1.isReversed () || !leader &&
+										group2.isReversed ();
+										ContigGrouper.ContigGroup change = leader ? group2 : group1;
+										if (!rev) {
+											//System.out.println ("not reversed");
+											reversed1 = false;
+										}
+										setReversed (change, rev);
+									}
+									if (reversed1 && last || !reversed1 && !last) {
+										//System.out.println ("reversed");
+										ContigGrouper.ContigGroup temp = group1;
+										group1 = group2;
+										group2 = temp;
+										after = !after;
+										ind = -ind;
+									}
+									afters.put (group1.toString (), group2);
+									befores.put (group2.toString (), group1);
+									if (ind != 1) {
+										ContigGrouper.ContigGroup move = (ContigGrouper.ContigGroup) 
+										(after ? afters.get (group2.toString()) : 
+											befores.get(group1.toString()));
+										removeContigGroup (after ? group2 : group1);
+										putNextTo (group1, group2, after);
+										if (!after)
+											group2 = group1;
+										int loopy = 0;
+										while (move != null) {
+											System.out.println ("oldish new code (" + loopy + "): " + move);
+											putNextTo (after ? group2 : move, after ? move : group2,
+													after);
+											group2 = move;
+											move = (ContigGrouper.ContigGroup) 
+											(after ? afters.get (group2.toString()) : 
+												befores.get(group2.toString()));
+										}
+									}
+								}
+							}
+							first = !first;
+						} while (!first);
+					}
+					last = !last;
+				} while (!last);
+			}
+		}
+	}
+	
+	//new method 5.10.10
+	protected boolean noCircle (ContigGrouper.ContigGroup group1, ContigGrouper.ContigGroup group2) {
+		Hashtable matched = befores;
+		/*if (looping > 0 && group1.start.getName().indexOf("97418") == -1 &&
+				group1.start.getName().indexOf("97420") == -1)
+			System.out.println ("not that one: " + group1.start.getName ());
+		else if (group1.start.getName().indexOf("97418") > -1) {
+			System.out.println ("in new method");
+			looping++;
+		}*/
+		do {
+			int count = 0;
+			ContigGrouper.ContigGroup cur = (ContigGrouper.ContigGroup) matched.get(group1.toString());
+			while (cur != null) {
+				/*if (print (group1)) {
+					System.out.println ("linked to: " + cur);
+					System.out.println ("group2: " + group2);
+				}*/
+				if (group2.toString().equals(cur.toString())) {
+					System.out.println ("rejecting b/c of loop");
+					return false;
+				}
+				cur = (ContigGrouper.ContigGroup) matched.get(cur.toString ());
+			}
+			if (matched == befores)
+				matched = afters;
+			else
+				matched = null;
+		} while (matched != null);
+		return true;
+	}
+	
+	protected boolean print (ContigGrouper.ContigGroup group) {
+		/*if (group.start.getName().indexOf("97418") > -1 ||
+				group.start.getName().indexOf("97420") > -1)
+			return true;
+		else*/
+			return false;
+	}
+	
+	/*
+	 * expects to be called from match edges - assumes will be in neither or only one
+	 * of the two hashtables befores and afters
+	 */
+	private void setReversed (ContigGrouper.ContigGroup group, boolean reverse) {
+		System.out.println ("setting: " + reverse + " was: " + group.isReversed () + " " +
+				group.start.getName());
+		if (group.isReversed () != reverse) {
+			Hashtable from = null;
+			Hashtable to = null;
+			String key = group.toString ();
+			if (befores.containsKey (key)) {
+				from = befores;
+				to = afters;
+			}
+			else if (afters.containsKey (key)) {
+				from = afters;
+				to = befores;
+			}
+			int already = central.ordered.indexOf(reverse ? 
+					group.start : group.end);
+			if (already > -1)
+				removeContigGroup (group);
+			if (!reverse)
+				central.removeGroupFromInverters (group);
+			else
+				central.addGroupToInverters (group);
+			group.setReversed (reverse);
+			if (already > -1)
+				addContigGroup (group, true, already);
+			if (from != null) {
+				ContigGrouper.ContigGroup group2 = (ContigGrouper.ContigGroup) from.get (key);
+				System.out.println ("accessing");
+				to.put (key, group2);
+				from.remove (key);
+				key = group2.toString ();
+				to.remove (key);
+				setReversed (group2, !group2.isReversed ());
+				from.put (key, group);
+			}
+		}
+		else
+			group.setReversed (reverse);
+	}
+	
+	public void removeContigGroup (ContigGrouper.ContigGroup group) {
+		Iterator itty = group.getNonEmpty ().iterator ();
+		while (itty.hasNext ())
+			central.ordered.remove (itty.next ());
+	}
+	
+	public void placeConflicts () {
+		System.out.println ("placing conflicts");
+		int [] placements = new int [central.fix.getChromosomes ().size () + 1];
+		LinkedList [] cur_lcbs = new LinkedList [placements.length];
+		HashSet misplaced = new HashSet ();
+		//System.out.println ("size: " + placements.length);
+		int highest = 0;
+		//System.out.println ("central: " + central.ordered);
+		for (int i = 0; i < central.fix_lcbs.length; i++) {
+			ContigGrouper.ContigGroup group = grouper.getContigGroup(central.fix_lcbs [i]);
+			if (!groups.contains(group.toString ()) && !central.ordered.contains(group.start)) {
+				Iterator itty = group.getLCBs().iterator();
+				ContigGrouper.ContigGroup group2 = null;
+				ContigGrouper.ContigGroup best_group = null;
+				while (itty.hasNext ()) {
+					LCB cur = (LCB) itty.next ();
+					central.setReference(central.getClosestRelation (cur));
+					int ind = Arrays.binarySearch(central.lcbs, cur, central.left_compare);
+					int other = -1;
+					while (other == -1 && ind > -1) {
+						if (!misplaced.contains (central.lcbs [ind--])) {
+							group2 = grouper.getContigGroup(central.lcbs [ind + 1]);
+							other = central.ordered.indexOf(group2.isReversed() ? group2.end : 
+								group2.start);
+						}
+					}
+					placements [++other] += cur.weight;
+					if (cur_lcbs [other] == null)
+						cur_lcbs [other] = new LinkedList ();
+					cur_lcbs [other].add (cur);
+					misplaced.add (cur);
+					if (placements [highest] < placements [other]) {
+						highest = other;
+						best_group = group2;
+					}
+				}
+				Iterator placed = cur_lcbs [highest].iterator ();
+				LCB best = null;
+				while (placed.hasNext ()) {
+					LCB cor = (LCB) placed.next();
+					if (best == null || cor.weight > best.weight)
+						best = cor;
+				}
+				//System.out.println ("not misplaced: " + best.getLeftEnd (central.fix));
+				group.weight = best.weight;
+				group.setReversed(central.isReversed(best));
+				putNextTo (highest == 0 ? null : best_group, group, true);
+				misplaced.remove (best);
+				Arrays.fill(placements, 0);
+				Arrays.fill (cur_lcbs, null);
+				highest = 0;
+			}
+		}
+	}
+	
+	public void putNextTo (ContigGrouper.ContigGroup one, ContigGrouper.ContigGroup two, boolean after) {
+		int ind = -1;
+		boolean ordered = true;
+		if (one != null) {
+			Chromosome prev = after ? (one.isReversed () ? one.start : one.end) :
+				(two.isReversed () ? two.end : two.start);
+			ind = central.ordered.indexOf (prev);
+			if (ind == -1)
+				ordered = false;
+			if (after)
+				ind++;
+		}
+		else
+			ind++;
+		//System.out.println ("next ind: " + ind);
+		addContigGroup (after ? two : one, true, ind);
+	}
+	
+	public void orderContigGroup (int lcb_index) {
+		ContigGrouper.ContigGroup group = grouper.getContigGroup (central.lcbs [lcb_index]);
+		if (!groups.contains (group.toString ())) {
+			boolean ok = locationUnique (group, lcb_index);
+			if (ok && !group.isReversed() && (group.first.getReverse (central.fix) ||
+					group.last.getReverse(central.fix)) && (group.last.getLeftEnd(
+							central.ref) < group.first.getLeftEnd(central.ref) ||
+							group.first == group.last))
+				group.setReversed(true);
+			addContigGroup (group, ok, central.ordered.size ());
+		}
+	}
+	
+	public void addContigGroup (ContigGrouper.ContigGroup group, boolean in_order, int index) {
+		LinkedList contigs = group.getNonEmpty ();
+		groups.add (group.toString ());
+		boolean reversed = group.isReversed();
+		if (reversed)
+			central.addGroupToInverters (group);
+		while (contigs.size () > 0) {
+			Chromosome chrom = (Chromosome) (reversed ? contigs.removeLast () :
+					contigs.removeFirst());
+			if (in_order) {
+				int ind2 = central.ordered.indexOf (chrom);
+				if (ind2 > -1) {
+					if (ind2 < index)
+						index--;
+					central.ordered.remove (ind2);
+					//System.out.println ("ordered twice: " + ind2 + 
+					//		" new " + index + " start: " + chrom);
+					//System.out.println ("prev: " + (ind2 == 0 ? null : central.ordered.get (--ind2)));
+				}
+				//else
+					//System.out.println ("ordering: " + chrom + " prev: " + 
+					//		(index > 0 ? central.ordered.get (index - 1) : "first"));
+				central.ordered.add(index++, chrom);
+			}
+			else
+				MauveHelperFunctions.addChromByStart (central.conflicts, chrom);
+		}
+	}
+	
+	public boolean locationUnique (ContigGrouper.ContigGroup group, int lcb_index) {
+		LCB current = group.first;
+		boolean ok = true;
+		HashSet grouped_lcbs = group.getLCBs ();
+		int count = grouped_lcbs.size () - 1; 
+		for (int i = count; i > 0 && lcb_index < central.lcbs.length - 1; i--) {
+			if (!grouped_lcbs.remove (central.lcbs [++lcb_index])) {
+				//System.out.println ("didn't find: " + lcbs [lcb_index].getLeftEnd (central.fix));
+				ok = false;
+			}
+		}
+		return ok;
+		
+	}
+	
+
+	
+	
+	
+
+}
diff --git a/src/org/gel/mauve/contigs/ContigMauveAlignFrame.java b/src/org/gel/mauve/contigs/ContigMauveAlignFrame.java
new file mode 100644
index 0000000..f46ae20
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigMauveAlignFrame.java
@@ -0,0 +1,168 @@
+package org.gel.mauve.contigs;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollBar;
+
+import org.gel.mauve.gui.Mauve;
+import org.gel.mauve.gui.ProgressiveMauveAlignFrame;
+import org.gel.mauve.gui.dnd.DnDList;
+
+public class ContigMauveAlignFrame extends ProgressiveMauveAlignFrame {
+	
+	protected ContigOrderer orderer;
+	protected boolean first;
+	protected File current_dir;
+	protected Hashtable <String, String> more_args;
+
+	public ContigMauveAlignFrame(Mauve mauve, ContigOrderer orderer, boolean gui) {
+		super(mauve,gui);
+		this.orderer = orderer;
+		initComponents ();
+	}
+	
+	public void initComponents () {
+		super.initComponents();
+		if(frame != null){
+			frame.setTitle("Align and Reorder Contigs");
+		}
+		first = true;
+		minLcbWeightText.setText("200");
+		refineCheckBox.setSelected(false);
+		seedFamiliesCheckBox.setSelected(false);
+		JLabel seq_label = new JLabel ("<html>Reference sequence expected first,<br/>" +
+				" draft second.</html>");
+		seq_label.setLocation(new Point(30, 184));
+		seq_label.setVisible(true);
+		seq_label.setSize(new Dimension (290, 25));
+		outputFileText.setLocation(new Point (85, 240));
+		outputFileText.setEditable(false);
+		outputFileText.setBackground(Color.white);
+		sequencesPanel.remove(outputButton);
+		//outputButton.setLocation(new Point (310, 240));
+		outputLabel.setText("Output: ");
+		outputLabel.setLocation(new Point (10, 240));
+		alignButton.setText("Start");
+		sequencesPanel.add(seq_label);
+	}
+	
+	public void setArgs (Hashtable <String, String> args) {
+		if (args.containsKey("--seed-family")) {
+			seedFamiliesCheckBox.setSelected(true);
+			args.remove("--seed-family");
+		}
+		args.remove("--output");
+		args.remove("--mums");
+		args.remove("--apply-backbone");
+		args.remove("--disable-backbone");
+		args.remove("--collinear");
+		args.remove("--output-guide-tree");
+		args.remove("--backbone-output");
+		more_args = args;
+	}
+	
+    protected String[] makeAlignerCommand() {
+    	String [] cmd = super.makeAlignerCommand();
+    	if (more_args == null)
+    		return cmd;
+    	Vector <String> extra = new Vector <String> (); 
+    	Iterator  <String> itty = more_args.keySet().iterator();
+    	while (itty.hasNext()) {
+    		String val = itty.next();
+    		if (val.charAt (1) == '-') {
+    			if (more_args.get(val).length() > 0)
+    				val += "=" + more_args.get(val);
+    			extra.add(val);
+    		}
+    	}
+    	String [] temp = new String [cmd.length + extra.size()];
+    	System.arraycopy(cmd, 0, temp, 0, cmd.length - 2);
+    	if (extra.size() > 0)
+    		System.arraycopy(extra.toArray(), 0, temp, cmd.length - 2, extra.size ());
+    	//two sequences are last
+    	System.arraycopy(cmd, cmd.length - 2, temp, temp.length - 2, 2);
+    	return temp;
+    }
+    
+    
+	public void setVisible (boolean show) {
+		if (show) {
+			String text = "Cancel ";
+			if (orderer.count < orderer.iterations)
+				text += "reorder " + (orderer.count - orderer.start);
+			else
+				text += "final";
+			cancelButton.setText(text);
+		}
+		super.setVisible(show);
+		System.out.println ("shown");
+	}
+	
+	public void displayFileInput () {
+		try {
+			current_dir = orderer.getAlignDir ();
+			//current_dir.mkdirs ();
+			setOutput(current_dir.getParentFile ().getAbsolutePath ());
+			if (!first)
+				sequenceListModel.clear ();
+			current_dir = new File (current_dir, orderer.DIR_STUB + orderer.count);
+			JScrollBar scroller = listScrollPane.getHorizontalScrollBar ();
+			scroller.setValue (scroller.getMaximum ());
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	public void addSequence (String file) {
+		DefaultListModel model = (DefaultListModel) sequenceList.getModel ();
+		model.addElement(file);
+	}
+	
+	public void alignButtonActionPerformed (ActionEvent e) {
+		DefaultListModel model = (DefaultListModel) sequenceList.getModel ();
+		if (first) {
+			if(sequenceList instanceof DnDList)
+				((DnDList)sequenceList).setDropActive (false);
+			if (sequenceList.getModel ().getSize() != 2) {
+				JOptionPane.showMessageDialog(this,	"Alignment should be two sequences;" +
+						"reference\nfirst followed by sequence to reorder.",
+						"Wrong Number of Sequences", JOptionPane.ERROR_MESSAGE);
+				return;
+			}
+			orderer.reference = new File ((String) model.getElementAt (0));
+			orderer.unordered = new File ((String) model.getElementAt (1));
+			orderer.copyInputFiles ();
+			sequencesPanel.remove (addButton);
+			sequencesPanel.remove (removeButton);
+			first = false;
+			orderer.directory = new File (getOutput ());
+		}
+		model.clear();
+		model.addElement (orderer.reference.getAbsolutePath ());
+		model.addElement (orderer.unordered.getAbsolutePath ());
+		setOutput (current_dir.getAbsolutePath());
+		super.alignButtonActionPerformed(e);
+		setOutput (current_dir.getParentFile().getParentFile().getAbsolutePath());
+	}
+	
+	public void addButtonActionPerformed (ActionEvent e) {
+		fc.setFileSelectionMode(fc.FILES_ONLY);
+		super.addButtonActionPerformed(e);
+	}
+	
+	public void outputButtonActionPerformed (ActionEvent e) {
+		fc.setFileSelectionMode(fc.DIRECTORIES_ONLY);
+		super.outputButtonActionPerformed(e);
+	}
+	
+}
diff --git a/src/org/gel/mauve/contigs/ContigOrderer.java b/src/org/gel/mauve/contigs/ContigOrderer.java
new file mode 100644
index 0000000..0a51f88
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigOrderer.java
@@ -0,0 +1,345 @@
+package org.gel.mauve.contigs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Vector;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.event.EventListenerList;
+
+import org.gel.air.util.IOUtils;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.ModelListener;
+import org.gel.mauve.MyConsole;
+import org.gel.mauve.XMFAAlignment;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.gui.AlignmentProcessListener;
+import org.gel.mauve.gui.MauveFrame;
+
+public class ContigOrderer implements MauveConstants {
+	
+	protected File directory;
+	protected File unordered;
+	protected File reference;
+	protected File align_dir;
+	public static final String DIR_STUB = "alignment";
+	protected int count = 1;
+	protected int start = 0;
+	protected ContigReorderer reorderer;
+	protected MauveFrame parent;
+	protected ContigMauveAlignFrame align;
+	public static final int DEFAULT_ITERATIONS = 15;
+	protected int iterations = DEFAULT_ITERATIONS;
+	public static final String ALIGN_START = "Start from alignment file.";
+	public static final String SEQ_START = "Start from sequence files.";
+	protected boolean align_start;
+	protected Vector past_orders;
+	protected boolean gui;
+	protected static final String OUTPUT_DIR = "-output";
+	protected static final String REF_FILE = "-ref";
+	protected static final String DRAFT_FILE = "-draft";
+	private EventListenerList alnListeners;
+	private File alnmtFile;
+	
+	public ContigOrderer (String [] args, Vector frames, boolean gui) {
+		init (args, frames, gui);
+		if (gui)
+			initGUI ();
+		else
+			initParams (args);
+	}
+	
+	public ContigOrderer (String [] args, Vector frames) {
+		this (args, frames, true);
+	}
+	
+	public void initParams (String [] args) {
+		Hashtable <String, String> pairs = IOUtils.parseDashPairedArgs(args);
+		String error = null;
+		try {
+			if (pairs.containsKey(OUTPUT_DIR)) {
+				directory = new File (pairs.get(OUTPUT_DIR));
+				if (!directory.exists()) {
+					if (!directory.mkdirs())
+						error = "Couldn't create output directory";
+				}
+				else if (getAlignDir ().exists())
+					error = "Directory already contains reorder";
+			}
+			else
+				error = "Output dir not given";
+			if (pairs.containsKey(REF_FILE)) {
+				System.out.println ("ref file: " + align);
+				align.addSequence(pairs.get(REF_FILE));
+			}
+			else
+				error = "no reference file given";
+			if (pairs.containsKey(DRAFT_FILE)) {
+				align.addSequence(pairs.get(DRAFT_FILE));
+			}
+			else
+				error = "no draft file given";
+		} catch (Exception e) {
+			e.printStackTrace();
+			error = e.getMessage();
+		}
+		if (error != null) {
+			if(gui){
+				JOptionPane.showMessageDialog(null, error);
+			}else{
+				System.err.println(error);
+			}
+			System.exit(0);
+		}
+		else {
+			align.setArgs (pairs);
+			startAlignment (false);
+		}
+	}
+	
+	public void initGUI () {
+		reorderer.init();
+		if (getFiles ()) {
+			startAlignment (true);
+		}
+		else
+			iterations = 0;
+	}
+	
+	public void init (String [] args, Vector frames, boolean gui) {
+		this.gui = gui;
+		alnListeners = new EventListenerList();
+		past_orders = new Vector ();
+		iterations = DEFAULT_ITERATIONS;
+		if (args != null && args.length > 0) {
+			try {
+				iterations = Integer.parseInt (args [0]);
+			} catch (NumberFormatException e) {
+			}
+		}
+		reorderer = new ContigReorderer (this, frames);
+		MyConsole.setUseSwing (gui);
+		MyConsole.showConsole ();
+		reorderer.ref_ind = 0;
+		reorderer.reorder_ind = 1;
+		align = new ContigMauveAlignFrame (
+				reorderer, this, gui);
+	}
+	
+	protected void startAlignment (boolean show_message) {
+		align.displayFileInput ();
+		align.setVisible(gui);
+		if (show_message) {
+			JOptionPane.showMessageDialog (null, 
+					"The reordering will begin when the start button is pressed.  " +
+					"It is an iterative process,\nand may take anywhere " +
+					"from a half hour to several hours.  It may\nbe cancelled " +
+					"at any point (intermediary results will be viewable).\n" +
+					"If it is cancelled after the first reorder "
+					+ "the data will be\navailable in fasta files in the " +
+					"corresponding output directory, although\nan alignment of" +
+					" the last order will not be produced.  If the ordering" +
+					" process\n is not manually ended, it will terminate when it "
+					+ "finds an order has repeated.\nSometimes the order will" + 
+					" cycle through several possibilities; this\nindicates it" +
+					" cannot determine which of them is most likely.\n " +
+					"Alignment parameters may be changed before reorder starts or " +
+					"any time between alignments.");
+		}
+		else
+			align.alignButtonActionPerformed (null);
+	}
+	
+	public boolean getFiles () {
+		JFileChooser chooser = new JFileChooser ();
+		chooser.setDialogTitle("Choose location to keep output files and folders.");
+		chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+		chooser.setMultiSelectionEnabled (false);
+		boolean active = true;
+		while (active) {
+			int choice = chooser.showDialog(parent, "OK");
+			if (choice == JFileChooser.APPROVE_OPTION) {
+				directory = chooser.getSelectedFile();
+				if (getAlignDir ().exists()) {
+					JOptionPane.showMessageDialog (parent, "Directory already " +
+							"contains reorder; please choose new directory", 
+							"Chooose new directory", 
+							JOptionPane.INFORMATION_MESSAGE);
+				}
+				else {
+					/*Object val = JOptionPane.showInputDialog (parent, "Choose input type:", 
+					"Start from:", JOptionPane.QUESTION_MESSAGE, null, new String [] {
+					SEQ_START, ALIGN_START}, SEQ_START);
+			if (val == null)
+				return false;
+			chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+			if (val == SEQ_START) {
+				return true;
+			}
+			else {
+				JOptionPane.showMessageDialog (parent, "Alignment should contain only the " +
+						"source and reference genome;\n the reference genome should be displayed " +
+						"above the genome to reorder.\n  Select the alignment file and not the " +
+						"directory containing the alignment.\n  For alignments generated from the " +
+						"contig reordering program,\n this is the file with the identical name to " +
+						"the containing directory\n (alignment followed by a number).",
+						"Start from alignment", JOptionPane.INFORMATION_MESSAGE);
+				chooser.setDialogTitle ("Select alignment file");
+				choice = chooser.showDialog (parent, "OK");
+				if (choice == JFileChooser.APPROVE_OPTION) {
+					align_start = true;
+					align_dir = chooser.getSelectedFile ();
+					reorderer.loadFile (align_dir);
+				}
+			}*/
+					return true;
+				}
+			}
+			else
+				return false;
+		}
+		return false;
+	}
+	
+	protected void setFilesFromAlignStart () {
+		align_dir = align_dir.getParentFile ();
+		XMFAAlignment xmfa = ((XmfaViewerModel) parent.getModel ()).getXmfa ();
+		reference = new File (align_dir, new File (xmfa.getName (0)).getName ());
+		String name = new File (xmfa.getName (1)).getName ();
+		unordered = new File (align_dir, name);
+		File file = new File (align_dir,
+				reorderer.file + ContigReorderer.FEATURE_EXT);
+		if (file.exists ())
+			parent.getFeatureImporter ().importAnnotationFile (file, reorderer.fix);
+		//iterations = 0;
+	}
+	
+	public boolean shouldReorder () {
+		boolean ok = count <= iterations;
+		if (align_start)
+			setFilesFromAlignStart ();
+		/*if (!ok)
+			renameLastAlignment ();*/
+		return ok;
+	}
+	
+	public void renameLastAlignment () {
+		File from = getAlignDir ();
+		File to = new File (directory, "final_" + DIR_STUB);
+		from.renameTo (to);
+	}
+	
+	public void reorderDone () {
+		try {
+			System.out.println ("C: " + count);
+			File temp = null;
+			if (!align_start)
+				temp = new File (getAlignDir (), CONTIG_OUTPUT);
+			else {
+				temp = new File (align_dir, CONTIG_OUTPUT);
+			}
+			// ugh, this is really ugly hardcoding.
+			String alnmtFilename = directory + "/alignment" + count;
+			alnmtFilename += "/alignment" + count;
+			alnmtFile = new File( alnmtFilename );
+			count++;
+			if (orderRepeated () || count > iterations) {
+				iterations = 0;
+				IOUtils.deleteDir (temp);
+				reorderer.active = false;
+				if (gui) {
+					JOptionPane.showMessageDialog(parent, "The reordering process is done.\n" +
+							"Results are displayed, and data is in output directory.", 
+							"Reorder Done", JOptionPane.INFORMATION_MESSAGE);
+					reorderer.inverted_from_start.clear ();
+				}
+				else if (alnListeners != null){
+					fireAlignmentEvent();
+				}else {
+					System.exit(0);
+				}
+			}
+			else {
+				past_orders.add(reorderer.ordered);
+				File to = makeAlignDir ();
+				temp.renameTo (to);
+				temp = new File (to, reference.getName ());
+				IOUtils.copyFile (reference, temp);
+				reference = temp;
+				unordered = new File (to, MauveHelperFunctions.genomeNameToFasta (
+						reorderer.fix));
+				reorderer.feature_file = null;
+				startAlignment (align_start);
+				align_start = false;
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	protected boolean orderRepeated () {
+		for (int i = 0; i < past_orders.size(); i++) {
+			if (reorderer.ordered.equals(past_orders.get(i)))
+				return true;
+		}
+		return false;
+	}
+
+	public void addAlignmentProcessListener(AlignmentProcessListener listener){
+		alnListeners.add(AlignmentProcessListener.class, listener);
+	}
+
+	private void fireAlignmentEvent(){
+		Object [] listeners = alnListeners.getListenerList ();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == AlignmentProcessListener.class) {
+				((AlignmentProcessListener) listeners[i + 1]).completeAlignment (0);
+			}
+		}
+	}
+
+	public File getAlignmentFile(){
+		return alnmtFile;
+	}		
+	public void copyInputFiles () {
+		try {
+			File dir = makeAlignDir ();
+			dir.mkdirs ();
+			File file = new File (dir, reference.getName ());
+			IOUtils.copyFile (reference, file);
+			reference = file;
+			file = new File (dir, unordered.getName ());
+			IOUtils.copyFile (unordered, file);
+			unordered = file;
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+	
+	protected File makeAlignDir () {
+		File dir = getAlignDir ();
+		while (dir.exists ()) {
+			count++;
+			start++;
+			iterations++;
+			dir = getAlignDir ();
+		}
+		//dir.mkdirs ();
+		return dir;
+	}
+	
+	public File getAlignDir () {
+		return new File (directory.getAbsolutePath(), DIR_STUB + count);
+	}
+	
+	
+	public static void main (String [] args) {	
+		new ContigOrderer (args, null, args.length == 0);
+	}
+
+}
diff --git a/src/org/gel/mauve/contigs/ContigRenamer.java b/src/org/gel/mauve/contigs/ContigRenamer.java
new file mode 100644
index 0000000..b3a198f
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigRenamer.java
@@ -0,0 +1,67 @@
+package org.gel.mauve.contigs;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.biojava.bio.seq.Feature;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.MauveHelperFunctions;
+
+public class ContigRenamer extends ContigReorderer {
+	
+	public Hashtable names;
+	
+	public void process () {
+		names = new Hashtable ();
+		Iterator itty = fix.getChromosomes ().iterator ();
+		int length = (fix.getChromosomes ().size () + "").length ();
+		int number = 0;
+		while (itty.hasNext ()) {
+			Chromosome chrom = (Chromosome) itty.next ();
+			String add = getContigName (chrom);
+			chrom.setName (add);
+			names.put (chrom.getName (), add);
+			ordered.add (chrom);
+		}
+	}
+	
+	public String getContigName (Chromosome chrom) {
+		return chrom.getName ();
+	}
+	
+	/**
+	 * removes number and underscore from beginning of name.
+	 * 
+	 * @param chrom
+	 * @param format_length
+	 * @return
+	 */
+	public String getContigName (Chromosome chrom, int format_length) {
+		String add = chrom.getName ();
+		return add.substring (format_length + 1, add.length ());
+	}
+	/**
+	 * for renaming contigs to prepend their current order to their name.
+	 * 
+	 * @param chrom
+	 * @param number
+	 * @param format_length
+	 * @return
+	 */
+	public String getContigName (Chromosome chrom, int number, int format_length) {
+		String add = number++ + "_";
+		while (add.length () <= format_length)
+			add = "0" + add;
+		add = add + chrom.getName ();
+		names.put (chrom.getName (), add);
+		return add;
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main (String [] args) {
+		new ContigRenamer ().init (args);
+	}
+
+}
diff --git a/src/org/gel/mauve/contigs/ContigReorderer.java b/src/org/gel/mauve/contigs/ContigReorderer.java
new file mode 100644
index 0000000..2bce5c5
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ContigReorderer.java
@@ -0,0 +1,574 @@
+package org.gel.mauve.contigs;
+
+import java.awt.event.ActionEvent;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.JMenuItem;
+
+import org.gel.air.util.GroupHelpers;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.LCBLeftComparator;
+import org.gel.mauve.LCBlist;
+import org.gel.mauve.LcbIdComparator;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.ModelBuilder;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.backbone.BackboneList;
+import org.gel.mauve.contigs.ContigGrouper.ContigGroup;
+import org.gel.mauve.gui.Mauve;
+import org.gel.mauve.gui.MauveFrame;
+
+public class ContigReorderer extends Mauve implements MauveConstants {
+
+	public static final int REF_IND = 1;
+	public static final int REORDER_IND = 2;
+	public static final int FILE_IND = 3;
+	public static final int MAX_IGNORABLE_DIST = 50;
+	public static final double MIN_LENGTH_RATIO = .01;
+	protected int ref_ind;
+	protected int reorder_ind;
+	protected LcbViewerModel model;
+	public LcbIdComparator id_compare;
+	protected LCB dummy;
+	protected Hashtable inverters;
+	protected LinkedList ordered;
+	protected Hashtable conflicts;
+	protected Hashtable nexts;
+	protected LCBLeftComparator left_compare;
+	protected String input_file;
+	protected String slast_ordered;
+	protected String feature_file;
+	public static final String CONTIG_EXT = "_contigs.tab";
+	public static final String FEATURE_EXT = "_features.tab";
+	protected MauveFrame frame;
+	protected Hashtable args;
+	protected String file;
+	protected File directory;
+	protected LCB[] lcbs;
+	protected LCB [] fix_lcbs;
+	protected Genome ref;
+	protected Genome fix;
+	protected Hashtable lcb_table;
+	protected Hashtable comparator_table;
+	protected Genome [] ordered_genomes;
+	protected ContigGrouper grouper;
+	protected ContigOrderer orderer;
+	protected boolean active;
+	protected HashSet inverted_from_start;
+	protected HashSet inverted_from_read;
+	
+	public ContigReorderer (ContigOrderer order, Vector parent) {
+		active = true;
+		check_updates = false;
+		if (parent != null) {
+			frames = parent;
+		}
+		orderer = order;
+	}
+	
+	public ContigReorderer () {
+		this (null, null);
+	}
+	
+	/**
+	 * Initializes Mauve environment.
+	 * 
+	 * @param args		See description of args in main (String [] args).
+	 */
+	public void init (String [] args) {
+		ref_ind = Integer.parseInt (args [REF_IND]);
+		reorder_ind = Integer.parseInt (args [REORDER_IND]);
+		if (args.length > 3) {
+			input_file = (String) args [FILE_IND];
+			if (args.length > 4) {
+				feature_file = args [4];
+				System.out.println ("ff: " + feature_file);
+			}
+		}
+		super.init (args [0]);
+	}
+	
+	public void init () {
+		if (frames == null)
+			super.init();
+	}
+	
+	/**
+	 * Makes a new Mauve frame that calls fixContigs once the alignment has
+	 * been set up.
+	 */
+	protected MauveFrame makeNewFrame () {
+		if (active) {
+			frame = new ReordererMauveFrame (this);
+			frames.add (frame);
+			return frame;
+		}
+		else
+			return super.makeNewFrame ();
+	} 
+	
+	protected void initMauveData () {
+		if (inverted_from_start == null)
+			inverted_from_start = new HashSet ();
+		args = new Hashtable ();
+		inverters = new Hashtable ();
+		conflicts = new Hashtable ();
+		nexts = new Hashtable ();
+		lcb_table = new Hashtable ();
+		comparator_table = new Hashtable ();
+		orderGenomes ();
+		if (input_file != null && input_file.indexOf (FEATURE_EXT) > 0) {
+			feature_file = input_file;
+			input_file = null;
+		}
+		fix = model.getGenomeBySourceIndex (reorder_ind);
+		id_compare = new LcbIdComparator ();
+		grouper =  new ContigGrouper (this, MAX_IGNORABLE_DIST, MIN_LENGTH_RATIO);
+		args.put (ContigFeatureWriter.REVERSES, inverters);
+		args.put (ContigFeatureWriter.CONFLICTED_CONTIGS, conflicts);
+		args.put(ContigFeatureWriter.COMPLEMENT, inverted_from_start);
+		file = fix.getDisplayName ();
+		int period = file.toLowerCase ().indexOf (".fas");
+		if (period > -1)
+			file = file.substring (0, period);
+		if (file.endsWith("."))
+				file = file.substring (0, file.length() - 1);
+		directory = MauveHelperFunctions.getRootDirectory (model);
+		File feats = new File (directory, file + ContigReorderer.FEATURE_EXT);
+		if (feats.exists ())
+			feature_file = feats.getAbsolutePath ();
+		if (feature_file != null && frame != null)
+			frame.getFeatureImporter ().importAnnotationFile (new File (
+					feature_file), fix);
+		if (ordered == null) {
+			ordered = new LinkedList (fix.getChromosomes());
+			args.put (ContigFeatureWriter.ORDERED_CONTIGS, ordered);
+			output (false);
+		}
+		directory = new File (directory + File.separator +
+				CONTIG_OUTPUT);
+		ordered = new LinkedList ();
+		args.put (ContigFeatureWriter.ORDERED_CONTIGS, ordered);
+		if (!directory.exists())
+			directory.mkdir();
+
+	}
+	
+	protected void initModelData () {
+		initMauveData ();
+		lcbs = ContigReorderer.this.model.getFullLcbList ();
+		if (orderer == null || (active && orderer.shouldReorder ())) {
+			fixContigs ();
+			if (orderer != null)
+				orderer.reorderDone ();
+		}
+	}
+	
+	protected void orderGenomes () {
+		ordered_genomes = new Genome [model.getSequenceCount () - 1];
+		for (int i = ref_ind; i < model.getSequenceCount (); i++) {
+			if (i != reorder_ind)
+				ordered_genomes [i] = model.getGenomeBySourceIndex(i);
+		}
+		ref = ordered_genomes [ref_ind];
+	}
+	
+	protected void fixContigs () {
+		process ();
+		output (true);
+	}
+	
+	public void process () {
+		System.out.println ("in process. . .");
+		adjustLCBs ();
+		LCB [] ids = (LCB []) lcb_table.get(fix);
+		fix_lcbs = new LCB [ids.length];
+		System.arraycopy (ids, 0, fix_lcbs, 0, ids.length);
+		Arrays.sort (fix_lcbs, id_compare);
+		if (input_file == null)
+			new ContigInverter (model, this);
+		else
+			new ContigInverter (model, this, input_file);
+	}
+	
+	public void output (boolean fasta) {
+		if (fasta)
+			new FastAContigChangeWriter (this);
+		new ContigFeatureWriter (new File (
+				directory, file + CONTIG_EXT).getAbsolutePath (), args);
+		Iterator feats = MauveHelperFunctions.getFeatures (model, reorder_ind);
+		if (feats.hasNext ()) {
+			new ChangedFeatureWriter (new File (
+					directory, file + FEATURE_EXT).getAbsolutePath (), args, feats, fix);
+		}
+	}
+	
+	public void setReference (Genome gen) {
+		ref = gen;
+		lcbs = (LCB []) lcb_table.get(ref);
+		left_compare = (LCBLeftComparator) comparator_table.get(ref);
+	}
+	
+	protected void removeBadLCBs () {
+		left_compare = new LCBLeftComparator (fix);
+		Arrays.sort (lcbs, left_compare);
+		fix_lcbs = new LCB [lcbs.length];
+		System.arraycopy (lcbs, 0, fix_lcbs, 0, lcbs.length);
+		Vector data = new Vector (lcbs.length);
+		Arrays.sort (fix_lcbs, id_compare);
+		GroupHelpers.arrayToCollection (data, lcbs);
+		Iterator itty = data.iterator ();
+		while (itty.hasNext ()) {
+			LCB lcb = null;
+			try {
+				lcb = (LCB) itty.next ();
+				if (lcb.getLeftEnd (fix) > 0) {
+					ContigGrouper.ContigGroup group = grouper.getContigGroup (lcb);
+				}
+			} catch (Exception e) {
+				System.out.println ("removed lcb: " + lcb.getLeftEnd (fix));
+				itty.remove ();
+			}
+		}
+		lcbs = new LCB [data.size ()];
+		data.toArray (lcbs);
+		LCBlist.computeLCBAdjacencies (lcbs, model);
+	}
+	
+	protected void adjustLCBs () {
+		dummy = new LCB (0);
+		removeBadLCBs ();
+		for (int i = 0; i < model.getSequenceCount(); i++) {
+			Genome cur = model.getGenomeBySourceIndex(i);
+			left_compare = new LCBLeftComparator (cur);
+			Arrays.sort (lcbs, left_compare);
+			Vector vec = new Vector ();
+			for (int ind = 0; ind < lcbs.length; ind++) {
+				if (lcbs [ind].getRightEnd(cur) > 0 && lcbs [ind].getRightEnd(fix) > 0)
+					vec.add(lcbs [ind]);
+			}
+			LCB [] temp = new LCB [vec.size()];
+			vec.toArray(temp);
+			lcb_table.put(cur, temp);
+			comparator_table.put (cur, left_compare);
+		}
+		setReference (ref);
+		System.out.println ("new lcbs: " + lcbs.length);
+	}
+	
+	protected void trimLCBs (String last_ordered) {
+		if (last_ordered != null) {
+			Iterator itty = ref.getChromosomes ().iterator ();
+			long last = ref.getLength ();
+			while (itty.hasNext ()) {
+				Chromosome chrom = (Chromosome) itty.next ();
+				System.out.println ("chrom: " + chrom.getName ());
+				if (last_ordered.equals (chrom.getName ())) {
+					last = chrom.getEnd ();
+					System.out.println ("setting last: " + last);
+					break;
+				}
+			}
+			System.out.println ("start size: " + lcbs.length);
+			for (int i = 0; i < lcbs.length; i++) {
+				if (lcbs [i].getLeftEnd (ref) > last) {
+					LCB [] temp = new LCB [i + 1];
+					System.arraycopy (lcbs, 0, temp, 0, i + 1);
+					lcbs = temp;
+					break;
+				}
+			}
+			System.out.println ("end size: " + lcbs.length);
+		}
+	}
+	
+	protected LCB getAdjacentLCB (boolean left, LCB cur) {
+		try {
+			int id = -1;
+			if (left)
+				id = cur.getLeftAdjacency (fix);
+			else
+				id = cur.getRightAdjacency (fix);
+			dummy.id = id;
+			id = Arrays.binarySearch (fix_lcbs, dummy, id_compare);
+			return id > -1 ? fix_lcbs [id] : null;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+	
+	/*
+	 * gets next lcb that is also in the reference genome
+	 */
+	/*protected LCB getAdjacentLCB (boolean left, LCB cur) {
+		LCB next = null;
+		try {
+			int id = -1;
+			if (left)
+				id = cur.getLeftAdjacency (fix);
+			else
+				id = cur.getRightAdjacency (fix);
+			dummy.id = id;
+			id = Arrays.binarySearch (fix_lcbs, dummy, id_compare);
+			if (id != -1) {
+				next = fix_lcbs [id];
+				if (next.getLeftEnd(ref) == 0)
+					next = getAdjacentLCB (left, next);
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return next;
+	}*/
+	
+	public boolean isReversed (LCB lcb) {
+		return lcb.getReverse(fix) != lcb.getReverse(ref);
+	}
+	
+	//from contig reorderer _contig.tab file
+	/*public void readOrdered (String file) {
+		try {
+			System.out.println ("getting ordered");
+			Hashtable chroms = new Hashtable (fix.getChromosomes ().size ());
+			Iterator itty = fix.getChromosomes ().iterator ();
+			while (itty.hasNext ()) {
+				Chromosome chrom = (Chromosome) itty.next ();
+				chroms.put (chrom.getName ().trim (), chrom);
+			}
+			BufferedReader in = new BufferedReader (new FileReader (file));
+			String input = new String (in.readLine ());
+			while (!input.trim ().equals (ContigFeatureWriter.ORDERED_CONTIGS))
+				input = in.readLine ();
+			input = in.readLine ();
+			input = in.readLine ();
+			while (input != null && !input.trim ().equals ("")) {
+				StringTokenizer toke = new StringTokenizer (input);
+				toke.nextToken ();
+				String name = toke.nextToken ();
+				System.out.println ("name: " + name);
+				ordered.add (chroms.get (name));
+				input = in.readLine ();
+			}
+			in.close ();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}*/
+	
+	//from tab or comma separated file with contig names in first row; cuts off name before
+	//first underscore - specialized for projector output
+	public void readOrdered (String file) {
+		try {
+			System.out.println ("getting ordered");
+			Hashtable <String, Chromosome> chroms = new Hashtable <String, Chromosome> 
+					(fix.getChromosomes ().size ());
+			Iterator itty = fix.getChromosomes ().iterator ();
+			while (itty.hasNext ()) {
+				Chromosome chrom = (Chromosome) itty.next ();
+				//System.out.println ("putting: " + chrom.getName ().trim ());
+				chroms.put (chrom.getName ().trim (), chrom);
+			}
+			BufferedReader in = new BufferedReader (new FileReader (file));
+			String input = new String (in.readLine ().trim());
+			while (input != null && !input.equals ("")) {
+				StringTokenizer toke = new StringTokenizer (input, "\t,", false);
+				String name = toke.nextToken ().trim ();
+				//for long named contigs separated by underscores
+				/*int under = name.indexOf ("_bp_");
+				if (under > -1) {
+					name = name.substring (0, under);
+					name = name.substring (0, name.lastIndexOf ('_'));
+				}*/
+				if (chroms.containsKey (name))
+					ordered.add (chroms.get (name));
+				input = in.readLine ();
+			}
+			in.close ();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	public void readKeepers (String file) {
+		try {
+			System.out.println ("getting keepers");
+			HashSet <String> chroms = new HashSet <String> 
+					(fix.getChromosomes ().size ());
+			BufferedReader in = new BufferedReader (new FileReader (file));
+			String input = new String (in.readLine ().trim());
+			while (input != null && !input.equals ("")) {
+				StringTokenizer toke = new StringTokenizer (input, "\t,", false);
+				String name = toke.nextToken ().trim ();
+				//for long named contigs separated by underscores
+				/*int under = name.indexOf ("_bp_");
+				if (under > -1) {
+					name = name.substring (0, under);
+					name = name.substring (0, name.lastIndexOf ('_'));
+				}*/
+				chroms.add(name);
+				input = in.readLine ();
+			}
+			in.close ();
+
+			Iterator itty = fix.getChromosomes ().iterator ();
+			while (itty.hasNext ()) {
+				Chromosome chrom = (Chromosome) itty.next ();
+				//System.out.println ("putting: " + chrom.getName ().trim ());
+				if (chroms.contains(chrom.getName ().trim ()))
+					ordered.add(chrom);
+			}
+			System.out.println ("keepers: " + ordered.size());
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	public boolean containsEndOfLCB (ContigGrouper.ContigGroup group, boolean left) {
+		LCB lcb = left ? group.first : group.last;
+		long loc = left ? lcb.getLeftEnd (ref) : lcb.getRightEnd (ref) - 10;
+		Segment segment = grouper.bb.getNextBackbone (ref, loc);
+		return segment.left [reorder_ind] == 0 ? false : true;
+	}
+	
+	public LCB getLCBAt (LCB start, long pos) {
+		while (start.getRightEnd(fix) < pos)
+			start = getAdjacentLCB (false, start);
+		if (start.getLeftEnd(fix) > pos)
+			start = null;
+		if (start != null)
+			System.out.println ("lcb: " + start.getLeftEnd(fix));
+		return start;
+	}
+	
+	public Genome getClosestRelation (LCB lcb) {
+		for (int i = 0; i < ordered_genomes.length; i++) {
+			if (lcb.getLeftEnd(ordered_genomes [i]) != 0)
+				return ordered_genomes [i];
+		}
+		return null;
+	}
+	
+	public void addGroupToInverters (ContigGroup group) {
+		Iterator contigs = group.getNonEmpty ().iterator();
+		Chromosome contig;
+		while (contigs.hasNext()) {
+			contig = (Chromosome) contigs.next();
+			if (MauveHelperFunctions.getChromByStart(inverters, contig) == null) {
+				MauveHelperFunctions.addChromByStart (inverters, 
+						contig);
+				switchOverallOrientation (contig);
+			}
+		}
+	}
+	
+	protected void switchOverallOrientation (Chromosome contig) {
+		String chrom = contig.getName();
+		if (inverted_from_start.contains(chrom))
+			inverted_from_start.remove(chrom);
+		else
+			inverted_from_start.add (chrom);
+	}
+	
+	public void removeGroupFromInverters (ContigGroup group) {
+		Iterator contigs = group.getNonEmpty ().iterator();
+		Chromosome contig;
+		while (contigs.hasNext()) {
+			contig = (Chromosome) contigs.next();
+			if (MauveHelperFunctions.removeChromByStart (inverters, 
+					contig) != null)
+				switchOverallOrientation (contig);
+		}
+	}
+	
+	
+
+	@Override
+	public void loadFile(File rr_file) {
+		if (orderer == null || orderer.gui)
+			super.loadFile(rr_file);
+		else {
+			try {
+				model = (LcbViewerModel) ModelBuilder.buildModel (rr_file, null);
+				initModelData ();
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	protected synchronized MauveFrame getNewFrame() {
+		return super.getNewFrame();
+	}
+	
+	/**
+	 * always starts with gui.  Files can be input from command line,
+	 * or entered via gui.
+	 * 
+	 * @param args			Index 0 should be the name of the alignment file,
+	 * 						  1 the reference genome's index, 2 the index of the
+	 * 						  genome to reorder.
+	 */
+	public static void main (String [] args) {
+		if (args.length > 0)
+			new ContigReorderer ().init (args);
+		else
+			new ContigOrderer (null, null);
+	}
+
+	 public class ReordererMauveFrame extends MauveFrame {
+		 
+		 	public ReordererMauveFrame (ContigReorderer ord) {
+		 		super (ord);
+		 		if (orderer != null)
+					orderer.parent = this;
+		 		System.out.println ("made reorder frame");
+		 	}
+		 	
+			public void setModel (BaseViewerModel mod) {
+				super.setModel (mod);
+				ContigReorderer.this.model = (LcbViewerModel) mod;
+				new Thread (new Runnable () {
+					public void run () {
+						System.out.println ("initing data");
+						initModelData ();
+						System.out.println ("inited data");
+					}
+				}).start ();				
+			}
+			
+			 public void actionPerformed (ActionEvent ae) {
+				 JMenuItem source = (JMenuItem) (ae.getSource());
+				 if (source == jMenuFileOpen || ae.getActionCommand().equals("Open"))
+				 {
+					 ((MauveFrame) frames.get(0)).doFileOpen();
+				 }
+				 else if (source == jMenuFileAlign)
+				 {
+					 ((MauveFrame) frames.get(0)).doAlign();
+				 }
+				 else if (source == jMenuFileProgressiveAlign)
+				 {
+					 ((MauveFrame) frames.get(0)).doProgressiveAlign();
+				 }
+				 else
+					 super.actionPerformed(ae);
+			 }
+	 }
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/contigs/DefaultContigHandler.java b/src/org/gel/mauve/contigs/DefaultContigHandler.java
new file mode 100644
index 0000000..b775734
--- /dev/null
+++ b/src/org/gel/mauve/contigs/DefaultContigHandler.java
@@ -0,0 +1,60 @@
+package org.gel.mauve.contigs;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.analysis.Segment;
+
+public class DefaultContigHandler implements ContigHandler, MauveConstants {
+	
+	//reference to model that contains sequence data
+	protected BaseViewerModel model;
+	
+	public DefaultContigHandler (BaseViewerModel mod) {
+		model = mod;
+	}
+
+	public long getContigCoord(int sequence, long loci) {
+		Genome genome = model.getGenomeBySourceIndex (sequence);
+		if (loci == 0 || genome.getChromosomes () == null ||
+				genome.getChromosomeAt (loci) == null)
+			return loci;
+		else
+			return genome.getChromosomeAt (loci).relativeLocation (loci);
+	}
+	
+	public long getPseudoCoord (int sequence, long loci, String contig) {
+		Chromosome chrom = getChromosomeFromName (sequence, contig);
+		if (chrom != null)
+			return loci + chrom.getStart() - 1;
+		else
+			return loci;
+	}
+	
+	public Chromosome getChromosomeFromName (int sequence, String contig) {
+		Iterator chroms = model.getGenomeBySourceIndex(sequence).getChromosomes().iterator();
+		while (chroms.hasNext()) {
+			Chromosome chrom = (Chromosome) chroms.next ();
+			if (chrom.getName().toLowerCase().indexOf(contig.toLowerCase()) > -1)
+				return chrom;
+		}
+		return null;
+	}
+
+	public void fixSegmentByContigs(int sequence, Segment segment) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public String getContigName(int sequence, long loci) {
+		if (loci == 0)
+			return "";
+		Chromosome chrom = model.getGenomeBySourceIndex (sequence).getChromosomeAt (loci);
+		return chrom == null ? DEFAULT_CONTIG : chrom.getName ();
+	}
+
+}
diff --git a/src/org/gel/mauve/contigs/FastAContigChangeWriter.java b/src/org/gel/mauve/contigs/FastAContigChangeWriter.java
new file mode 100644
index 0000000..745d7a8
--- /dev/null
+++ b/src/org/gel/mauve/contigs/FastAContigChangeWriter.java
@@ -0,0 +1,175 @@
+package org.gel.mauve.contigs;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.biojava.bio.seq.ComponentFeature;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceTools;
+import org.biojava.bio.seq.io.FastaFormat;
+import org.biojava.bio.seq.io.SeqIOTools;
+import org.biojava.bio.seq.io.StreamWriter;
+import org.biojava.bio.symbol.IllegalAlphabetException;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.MauveHelperFunctions;
+
+public class FastAContigChangeWriter implements MauveConstants {
+	
+	protected Genome genome;
+	protected Hashtable inverters;
+	protected Sequence seq;
+	protected PrintStream out;
+	protected LinkedList ordered;
+	protected Hashtable conflicts;
+	protected Hashtable all_contigs;
+	protected Hashtable nexts;
+	protected PrintStream out2;
+	protected FastaFormat format;
+	protected StreamWriter writer;
+	protected StreamWriter writer2;
+	
+	public FastAContigChangeWriter (ContigReorderer central) {
+		genome = central.fix;
+		inverters = central.inverters;
+		ordered = central.ordered;
+		conflicts = central.conflicts;
+		nexts = central.nexts;
+		File dir = central.directory;
+		if (central instanceof ContigRenamer) {
+			format = new ChangedFastaFormat (((ContigRenamer) central).names);
+		}
+		else
+			format = new FastaFormat ();
+		boolean print_extra = false;
+		try {
+			String file = MauveHelperFunctions.genomeNameToFasta (genome);
+			out = new PrintStream (new FileOutputStream (new File (
+					dir, file).getAbsolutePath ()));
+			if (print_extra) {
+				file = "extra_" +file;
+				out2 = new PrintStream (new FileOutputStream (new File (
+						dir, file).getAbsolutePath ()));
+				writer2 = new StreamWriter (out2, format);
+			}
+			writer = new StreamWriter (out, format);
+		} catch (FileNotFoundException e1) {
+			e1.printStackTrace();
+		}
+		try {
+			all_contigs = getContigFeatures (genome);
+			writeContigs (ordered.iterator (), false);
+			System.out.println ("done with ordered. " + all_contigs.size());
+			ArrayList list = new ArrayList (conflicts.keySet ());
+			Collections.sort (list);
+			writeContigs (list.iterator (), true);
+			System.out.println ("done with conflicted. " + all_contigs.size());
+			list = new ArrayList (all_contigs.keySet ());
+			Collections.sort (list);
+			for (int i = 0; i < list.size(); i++)
+				ordered.add(genome.getChromosomeAt(((ComponentFeature)
+						all_contigs.get(list.get(i))).getLocation().getMin())); 
+			writeContigs (list.iterator (), true, print_extra);
+			System.out.println ("done with unordered. " + all_contigs.size());
+		} catch (Exception e) {
+			e.printStackTrace ();
+		}
+		out.flush ();
+		out.close ();
+		if (print_extra) {
+			out2.flush ();
+			out2.close ();
+		}
+	}
+	
+	public void writeContigs (Iterator itty, boolean key) {
+		writeContigs (itty, key, false);
+	}
+	public void writeContigs (Iterator itty, boolean key, boolean print_extra) {
+		if (!itty.hasNext())
+			return;
+		Long start = new Long (-1);
+		Object obj = null;
+		ListSequenceIterator list = new ListSequenceIterator ();
+		ListSequenceIterator list2 = new ListSequenceIterator ();
+		long total = 0;
+		try {
+			while (true) {
+				obj = nexts.remove(start);
+				if (obj != null)
+					System.out.println ("got from table: " + obj);
+				if (obj == null && itty.hasNext())
+					obj = itty.next();
+				if (obj == null)
+					break;
+				if (obj instanceof Chromosome)
+					start = new Long (((Chromosome) obj).getStart ());
+				else
+					start = (Long) obj;
+				//System.out.println ("start: " + start);
+				ComponentFeature feat = (ComponentFeature) all_contigs.remove (start);
+				if (feat == null) {
+					continue;
+				}
+				Chromosome chrom = (Chromosome) inverters.get (start);
+				Sequence seq = null;
+				if (chrom != null) {
+					seq = SequenceTools.reverseComplement (
+							feat.getComponentSequence ());
+				}
+				else {
+					seq = feat.getComponentSequence ();
+					if (print_extra) {
+						total += feat.getLocation ().getMax () - feat.getLocation ().getMin ();
+						//SeqIOTools.writeFasta (out2, feat.getComponentSequence ());
+						list2.add (feat.getComponentSequence ());
+					}
+				}
+				//SeqIOTools.writeFasta (out, seq);
+				list.add (seq);
+			}
+			writer.writeStream (list);
+			if (out2 != null)
+				writer2.writeStream (list2);
+		} catch (IllegalAlphabetException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		System.out.println ("unmatched length: " + total);
+	}
+
+/*System.out.println ("a: " + feat.getComponentSequence ().seqString ());
+System.out.println ("b: " + two.seqString ());
+ComponentFeature.Template template = (ComponentFeature.Template) 
+feat.makeTemplate ();
+template.componentSequence = two;
+seq.removeFeature (feat);
+seq.createFeature (template);*/
+	
+	public static Hashtable getContigFeatures (Genome genome) {
+		Sequence seq = genome.getAnnotationSequence();
+		Iterator itty = seq.features();
+		Hashtable all_contigs = new Hashtable (seq.countFeatures());
+		while (itty.hasNext()) {
+			Object obj = itty.next ();
+			if (obj instanceof ComponentFeature) {
+				ComponentFeature feat = (ComponentFeature) obj;
+				all_contigs.put(new Long (feat.getLocation().getMin()), feat);
+			}
+		}
+		return all_contigs;
+	}
+
+}
diff --git a/src/org/gel/mauve/contigs/FeatureFixer.java b/src/org/gel/mauve/contigs/FeatureFixer.java
new file mode 100644
index 0000000..52164f4
--- /dev/null
+++ b/src/org/gel/mauve/contigs/FeatureFixer.java
@@ -0,0 +1,84 @@
+package org.gel.mauve.contigs;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import org.biojava.bio.seq.Feature;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.MauveHelperFunctions;
+
+public class FeatureFixer extends ContigReorderer {
+	
+	public void readOrdered (String file) {
+		try {
+			Hashtable chroms = new Hashtable (fix.getChromosomes ().size ());
+			BufferedReader in = new BufferedReader (new FileReader (file));
+			String input = new String (in.readLine ());
+			while (!input.trim ().equals (ContigFeatureWriter.ORDERED_CONTIGS))
+				input = in.readLine ();
+			input = in.readLine ();
+			input = in.readLine ();
+			while (input != null && !input.trim ().equals ("")) {
+				StringTokenizer toke = new StringTokenizer (input);
+				toke.nextToken ();
+				String name = toke.nextToken ();
+				System.out.println ("name: " + name);
+				//ordered.add (chroms.get (name));
+				chroms.put (name.substring (name.indexOf ("_") + 1, name.length ()), name);
+				input = in.readLine ();
+			}
+			in.close ();
+			System.out.println ("getting ordered");
+			Iterator itty = fix.getChromosomes ().iterator ();
+			while (itty.hasNext ()) {
+				Chromosome chrom = (Chromosome) itty.next ();
+				chrom.setName (((String) chroms.get (chrom.getName ())).trim ());
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	public void process () {
+		readOrdered (input_file);
+		Iterator itty = fix.getChromosomes ().iterator ();
+		int length = (fix.getChromosomes ().size () + "").length ();
+		int number = 0;
+		Hashtable contigs = new Hashtable ();
+		while (itty.hasNext ()) {
+			Chromosome chrom = (Chromosome) itty.next ();
+			ordered.add (chrom);
+			contigs.put (chrom.getName (), chrom);
+		}
+		try {
+			BufferedReader in = new BufferedReader (new FileReader (feature_file));
+			in.readLine ();
+			String s = null; 
+			while ((s = in.readLine ()) != null) {
+				StringTokenizer toke = new StringTokenizer (s);
+				s = toke.nextToken ();
+				for (int i = 1; i <= 6; i++)
+					toke.nextToken ();
+				boolean flip = new Boolean (toke.nextToken ()).booleanValue ();
+				System.out.println ("s: " + flip);
+				if (flip)
+					MauveHelperFunctions.addChromByStart (inverters, (Chromosome) contigs.get (s));
+			}
+			in.close ();
+		}
+		catch (Exception e) {
+			e.printStackTrace ();
+		}
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main (String [] args) {
+		new FeatureFixer ().init (args);
+	}
+
+}
diff --git a/src/org/gel/mauve/contigs/ListSequenceIterator.java b/src/org/gel/mauve/contigs/ListSequenceIterator.java
new file mode 100644
index 0000000..ac9dfdd
--- /dev/null
+++ b/src/org/gel/mauve/contigs/ListSequenceIterator.java
@@ -0,0 +1,31 @@
+package org.gel.mauve.contigs;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+import org.biojava.bio.BioException;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+
+public class ListSequenceIterator extends LinkedList implements
+		SequenceIterator {
+
+	protected Iterator iterator;
+	
+	public boolean hasNext () {
+		if (iterator == null)
+			iterator = iterator ();
+		boolean ret = iterator.hasNext ();
+		if (!ret)
+			iterator = null;
+		return ret;
+	}
+
+	public Sequence nextSequence () throws NoSuchElementException, BioException {
+		if (iterator == null)
+			return null;
+		return (Sequence) iterator.next ();
+	}
+
+}
diff --git a/src/org/gel/mauve/dcj/DCJ.java b/src/org/gel/mauve/dcj/DCJ.java
new file mode 100644
index 0000000..e7f3c0e
--- /dev/null
+++ b/src/org/gel/mauve/dcj/DCJ.java
@@ -0,0 +1,758 @@
+package org.gel.mauve.dcj;
+
+import java.util.*;
+import java.io.*;
+/**
+ * @deprecated
+ * @author Mike Tsai
+ *
+ */
+public class DCJ {
+
+	/***************************************************************************
+	 * Synteny Block object holds Vertices left,right, value of genomeA and
+	 * value of genome B performs traversing along black lines to print state of
+	 * genome A
+	 **************************************************************************/
+	class SB {
+		private Vert left; // left vert
+
+		private Vert right; // right vert
+
+		private String identity;
+
+		private int valueA; // int value for genome A
+
+		private int valueB; // int value for genome B
+
+		private boolean visited = false;
+
+		// default constructor
+		public SB () {
+			left = null;
+			right = null;
+			identity = "";
+		}// end SB constructor
+
+		public SB (Vert l, Vert r, String i) {
+			left = l;
+			right = r;
+			identity = i;
+			l.setSB (this);
+			r.setSB (this);
+		}// end SB constructor
+
+		public SB (Vert l, Vert r) {
+			left = l;
+			right = r;
+			identity = "[" + l.getGene () + " " + r.getGene () + "]";
+			l.setSB (this);
+			r.setSB (this);
+		}// end SB constructor
+
+		// mutator methods
+		public void setVisited (boolean b) {
+			this.visited = b;
+		}
+
+		public void setLeft (Vert v) {
+			this.left = v;
+		}
+
+		public void setRight (Vert v) {
+			this.right = v;
+		}
+
+		public void setIdentity (String s) {
+			this.identity = s;
+		}
+
+		public void turnValue (int s) {
+			this.valueA = s;
+			// {try{this.valueA=Integer.parseInt(s);}catch(Exception
+			// e){System.out.println("format error\n"+e);System.exit(0);}
+		}
+
+		public void setValueA (int i) {
+			this.valueA = i;
+		}
+
+		public void setValueB (int i) {
+			this.valueB = i;
+		}
+
+		// //newly constructed vertices need to be set to point to this SB.
+		public boolean setVertPointers () {
+			if (this.getLeft () == null || this.getRight () == null) {
+				return false;
+			} else {
+				this.getLeft ().setSB (this);
+				this.getRight ().setSB (this);
+			}
+			return true;
+		}
+
+		// //retrieval methods
+		public boolean wasVisited () {
+			return this.visited;
+		}
+
+		public Vert getLeft () {
+			return this.left;
+		}
+
+		public Vert getRight () {
+			return this.right;
+		}
+
+		public String getIdentity () {
+			return this.identity;
+		}
+
+		public int getValueA () {
+			return this.valueA;
+		}
+
+		public int getValueB () {
+			return this.valueB;
+		}
+
+		// //polarity of the SB, pos value head is right Vert, neg value head is
+		// left Vert.
+		public Vert getHeadA () {
+			if (this.getValueA () < 0) {
+				return this.getLeft ();
+			} else {
+				return this.getRight ();
+			}
+		}
+
+		public Vert getTailA () {
+			if (this.getValueA () < 0) {
+				return this.getRight ();
+			} else {
+				return this.getLeft ();
+			}
+		}
+
+		public Vert getHeadB () {
+			// if(this.getValueB()<0){return this.getLeft();}else{return
+			// this.getRight();}
+			if (this.getValueB () == this.getValueA ()) {
+				return this.getRight ();
+			} else {
+				return this.getLeft ();
+			}
+		}
+
+		public Vert getTailB () {
+			// if(this.getValueB()<0){return this.getRight();} else{return
+			// this.getLeft();}
+			if (this.getValueB () == this.getValueA ()) {
+				return this.getLeft ();
+			} else {
+				return this.getRight ();
+			}
+		}
+
+	}// end SB
+
+	/***************************************************************************
+	 * Class Vert for Vertices Every SB has a left and right Vert Vert contains
+	 * Variables black and grey which point to other Verts
+	 **************************************************************************/
+	class Vert {
+		private Vert black; // black line
+
+		private Vert grey; // grey arc
+
+		private SB sb; // point to parent SB
+
+		private String gene; // name the Vert
+
+		private String isCap; // name the cap ie. A1 A2 B1 B2...
+
+		private String chromosome; // currently not used
+
+		private boolean seenCap;// quick fix to know what caps were
+
+		// visited for closing AA,AB, and BB paths
+		// constructor
+		public Vert () {
+			black = null;
+			grey = null;
+			sb = null;
+			gene = "";
+			chromosome = "";
+			isCap = "";
+			seenCap = false;
+		}// end Vert constructor
+
+		// Vert constructor, doesn't assign SB
+		public Vert (Vert b, Vert g, String s) {
+			this.black = b;
+			this.grey = g;
+			this.gene = s;
+			sb = null;
+			chromosome = "";
+			isCap = "";
+			seenCap = false;
+		}
+
+		public Vert (String s) {
+			this.black = null;
+			this.grey = null;
+			this.gene = s;
+			sb = null;
+			chromosome = "";
+			isCap = "";
+			seenCap = false;
+		}
+
+		// MUTATOR methods
+		public void setBlack (Vert v) {
+			this.black = v;
+		}
+
+		public void setGrey (Vert v) {
+			this.grey = v;
+		}
+
+		public void setSB (SB s) {
+			this.sb = s;
+		}
+
+		public void setGene (String s) {
+			this.gene = s;
+		}
+
+		// name the chromosome the vertex belongs to
+		public void setChromosome (String s) {
+			this.chromosome = s;
+		}
+
+		// name the cap
+		public void setIsCap (String b) {
+			this.isCap = b;
+		}
+
+		// cap a vertex, true is A cap(black line), false is B cap (grey line)
+		public void setSeen (boolean b) {
+			this.seenCap = b;
+		}
+
+		// generates a new Vert object that has the profile of a cap.
+		public Vert cap (boolean b) {
+			Vert newcap;
+			if (b) {
+				newcap = new Vert ("A");
+				newcap.setIsCap (newcap.getGene ());
+				newcap.setSB (new SB ());
+				newcap.setBlack (newcap);
+				newcap.setGrey (newcap);
+				this.joinBlack (newcap);
+				newcap.setGene ("" + newcap.getGene () + (++counta));
+			} else {
+				newcap = new Vert ("B");
+				newcap.setIsCap (newcap.getGene ());
+				newcap.setSB (new SB ());
+				newcap.setBlack (newcap);
+				newcap.setGrey (newcap);
+				this.joinGrey (newcap);
+				newcap.setGene ("" + newcap.getGene () + (++countb));
+			}
+			return newcap;
+		}
+
+		// retrieval methods
+		public String getGene () {
+			return this.gene;
+		}
+
+		public Vert getBlack () {
+			return this.black;
+		}
+
+		public Vert getGrey () {
+			return this.grey;
+		}
+
+		public SB getSB () {
+			return this.sb;
+		}
+
+		public String getCap () {
+			return this.isCap;
+		}
+
+		public boolean isCap () {
+			return ((this.getCap ()).length () > 0);
+		}
+
+		public String getChromosome () {
+			return this.chromosome;
+		}
+
+		public boolean seen () {
+			return this.seenCap;
+		}
+
+		// print methods
+		// prints the main variables of Vert, try-catch for events where vars
+		// have not been initialized.
+		public void printMe () {
+			buf.append ("Block: ");
+			try {
+				buf.append (this.getGene ());
+			} catch (Exception e) {
+			}
+			buf.append ("	black:");
+
+			try {
+				buf.append (this.getBlack ().getGene ());
+			} catch (Exception e) {
+			}
+			buf.append ("	Grey: ");
+
+			try {
+				buf.append (this.getGrey ().getGene ());
+			} catch (Exception e) {
+			}
+			buf.append ("	SB: ");
+
+			try {
+				buf.append (this.getSB ().getIdentity ());
+			} catch (Exception e) {
+			}
+			if (this.isCap ()) {
+				buf.append ("	cap:" + this.getCap ());
+			}
+			buf.append ("\n");
+		}
+
+		// traverse capped genome with alternating black/grey paths (for closing
+		// paths to generate HP cycle) and returns the end cap
+		public Vert traversePath (boolean b) {
+			if (this.isCap ())
+				return this;
+			if (b)
+				return this.getBlack ().traversePath (false);
+			return this.getGrey ().traversePath (true);
+		}
+
+		// overloaded method, same as previous but populates a vector
+		public Vert traversePath (boolean b, Vector v) {
+			// this.printMe();
+			// populate greyVector (vector of grey lines)
+			if (!b) {
+				v.addElement (this);
+			}
+			if (this.isCap ())
+				return this;
+			if (b)
+				return this.getBlack ().traversePath (false, v);
+			return this.getGrey ().traversePath (true, v);
+		}
+
+		// this = a = cap, b is another cap. closepath forms a cycle, the method
+		// depends on the Cap values (A&B A&A or B&B)
+		public void closePath (Vert b) {
+			// a and b are both B caps
+			if ((this.getCap ().equals (b.getCap ()))
+					&& (this.getCap ().equals ("B"))) {
+				this.joinBlack (b);
+			}
+			// a and b are both A caps
+			if ((this.getCap ().equals (b.getCap ()))
+					&& (this.getCap ().equals ("A"))) {
+				this.joinGrey (b);
+			}
+			// a!=b and a is an A cap (black line out)
+			if (!(this.getCap ().equals (b.getCap ()))
+					&& this.getCap ().equals ("A")) {
+				this.setGrey (b.getGrey ());
+				(b.getGrey ()).setGrey (this);
+			}
+			// a!=b and a is a B cap (grey line out) - is reverse of above code
+			if (!(this.getCap ().equals (b.getCap ()))
+					&& this.getCap ().equals ("B")) {
+				b.setGrey (this.getGrey ());
+				(this.getGrey ()).setGrey (b);
+			}
+		}// end closepath
+
+		// used in DCJ moves and joining caps to form cycles
+		public void joinBlack (Vert b) {
+			this.setBlack (b);
+			b.setBlack (this);
+		}
+
+		// used in joining two B caps to form cycles
+		public void joinGrey (Vert b) {
+			this.setGrey (b);
+			b.setGrey (this);
+		}// end joinGrey
+
+		// tests if the Vert exists as a 1-cycle. Which is characterized as a
+		// black and grey line pointing to the same Gene
+		public boolean isOneCycle () {
+			return (this.grey == this.black);
+		}
+
+	}// end Vert
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// ////////////////////////////////////////////////////////////////////////////
+	// ////////////////////////////////////////////////////////////////////////////
+	/***************************************************************************
+	 * Class DCJ Loads the data and parses Creates and populates Vectors for SBs
+	 * and grey lines in the process.
+	 **************************************************************************/
+	public int counta = 0;
+
+	public int countb = 0;
+
+	public int countDCJ = 0;
+
+	public int bufsize = 256;
+
+	public StringBuffer buf; // buffer holding log output
+
+	public StringBuffer opBuf; // operation buffer
+
+	public boolean suppress = false;
+
+	String speciesA = "";
+
+	String speciesB = "";
+
+	Vector capVector;
+
+	Vector sbVector;
+
+	Vector greyVector;
+
+	public DCJ () {
+		this.capVector = new Vector ();
+		this.sbVector = new Vector ();
+		greyVector = new Vector ();
+		buf = new StringBuffer (bufsize);
+	}
+
+	public DCJ (StringTokenizer A, StringTokenizer B) {
+		buf = new StringBuffer (bufsize);
+		opBuf = new StringBuffer ();
+		this.capVector = new Vector ();
+		this.sbVector = new Vector ();
+		greyVector = new Vector ();
+		loadGenome (A, B);
+		this.printSBV ();
+		this.cycleDCJ ();
+
+	}
+
+	public DCJ (StringTokenizer A, StringTokenizer B, String species1,
+			String species2) {
+		buf = new StringBuffer (bufsize);
+		opBuf = new StringBuffer ();
+		speciesA = species1;
+		speciesB = species2;
+		this.capVector = new Vector ();
+		this.sbVector = new Vector ();
+		greyVector = new Vector ();
+		loadGenome (A, B);
+		this.printSBV ();
+		this.cycleDCJ ();
+
+	}
+
+	public void cycleDCJ () {
+		Vert temp;
+		if (!suppress)
+			buf.append (greyVector.size () + " Grey lines\n");
+		this.printSBBlack ();
+		while (greyVector.size () > 0) {
+			temp = (Vert) greyVector.firstElement ();
+			if (this.DCJ (temp)) {
+				if (!suppress) {
+					this.printSBBlack ();
+				}
+			}
+		}// end while
+		if (suppress)
+			buf.append (countDCJ + "	\n");
+	}
+
+	public String getLog () {
+		return this.buf.toString ();
+	}
+
+	public String getOpBuf () {
+		return this.opBuf.toString ();
+	}
+
+	// takes an sbArray which is the SB vector converted into an array
+	// String s is genome B that is tokenized and parsed into an array
+	// of integers
+	// genome B values are empty and are set using intArray values.
+	// grey arcs are they formed from head to tail
+	// caps are then formed on both ends
+	// run time is O(n) n=size of s.
+
+	public int getCount () {
+		return countDCJ;
+	}
+
+	// DCJ operation. 4 verts for v.DCJ(): v, v.black, v.grey, and v.black.grey
+	public boolean DCJ (Vert v) {
+		StringBuffer tempbuf = new StringBuffer ();
+		// check for 1 cycle first, if it is, delete and move on
+		// check because a previous call may have generated 2 1-cycles.
+		if (v.isOneCycle ()) {
+			// code for removing 1-cycle from Vector of lines, this 1-cycle is
+			// formed from a previous call that was a 2-cycle forked into 2
+			// 1-cycles, it is not counted as a DCJ call
+			// remove(object) from a vector is O(n) time however since the
+			// object is always the first object, it's constant.
+			this.greyVector.remove (v);
+		} else {
+			// don't mess with the following orders
+			// v.black -join- v.grey.black
+			++countDCJ;
+			if (!suppress) {
+				tempbuf
+						.append (countDCJ
+								+ " JOIN: "
+								+ v.getBlack ().getBlack ().getGene ()
+								+ " to "
+								+ v.getGrey ().getBlack ().getBlack ()
+										.getGene () + "	");
+			}
+			v.getBlack ().joinBlack (v.getGrey ().getBlack ());
+			// v -join- v.grey, this will always form a 1-cycle
+			if (!suppress) {
+				tempbuf.append ("JOIN: " + v.getBlack ().getGene () + " to "
+						+ v.getGrey ().getBlack ().getGene () + "\n");
+			}
+			v.joinBlack (v.getGrey ());
+			this.greyVector.remove (this); // remove The 1-cycle formed from
+			// the DCJ
+			buf.append (tempbuf.toString ());
+			opBuf.append (tempbuf.toString ());
+			return true;
+		}
+		buf.append (tempbuf.toString ());
+		opBuf.append (tempbuf.toString ());
+		return false;
+	}// end DCJ
+
+	public void parseAndLoadGrey (String s, SB [] sbArray) {
+		int [] intArray = this.toIntArray (s);
+		int count = 0;
+		// use intArray[] to manipulate existing objects in sbArray[] (join
+		// grey)
+		for (int i = 0; i < intArray.length - 1; i++) {
+			sbArray[Math.abs (intArray[i]) - 1].setValueB (intArray[i]);
+			sbArray[Math.abs (intArray[i + 1]) - 1].setValueB (intArray[i + 1]);
+			sbArray[Math.abs (intArray[i]) - 1].getHeadB ().joinGrey (
+					sbArray[Math.abs (intArray[i + 1]) - 1].getTailB ());
+			// sbArray[Math.abs(intArray[i])-1].getRight().joinGrey(sbArray[Math.abs(intArray[i+1])-1].getLeft());
+
+		}// end for
+		capVector.add (sbArray[Math.abs (intArray[0]) - 1].getTailB ().cap (
+				false));
+		capVector.add (sbArray[Math.abs (intArray[intArray.length - 1]) - 1]
+				.getHeadB ().cap (false));
+	}
+
+	// parses string s, adds a capped sb vector with black lines
+	// connected to global vector, and populates the cap vector.
+	// This method would have to be called k times where k is the number of
+	// chromosomes. run time of O(n)
+	public void parseAndLoadBlack (String s) {
+		StringTokenizer token = new StringTokenizer (s);
+		String tempInt; // for parsed strings of s
+		SB tempSB; // temp SB
+		Vector tempSbVector = new Vector (); // vector of SBs that're created
+		while (token.hasMoreTokens ()) {
+			tempInt = token.nextToken ().trim ();
+			tempSB = new SB (new Vert (tempInt + "L"), new Vert (tempInt + "R"));
+			tempSB.setVertPointers ();
+			tempSB.turnValue (Integer.parseInt (tempInt));
+			tempSbVector.add (tempSB);
+			// System.out.print(tempInt+" ");
+		}// end while
+		for (int i = 0; i < tempSbVector.size () - 1; i++) {
+			// CONNECT black lines
+			((SB) tempSbVector.elementAt (i)).getRight ().joinBlack (
+					((SB) tempSbVector.elementAt (i + 1)).getLeft ());
+		}// end for
+		// add A Caps to front and end of chromosome
+		capVector.add ((((SB) tempSbVector.firstElement ()).getLeft ())
+				.cap (true));
+		capVector.add ((((SB) tempSbVector.lastElement ()).getRight ())
+				.cap (true));
+
+		this.sbVector.addAll (tempSbVector);
+	}// end parseAndLoadBlack
+
+	/***************************************************************************
+	 * traverses the genome starting from caps from the cap vector checks if the
+	 * caps have been visited, if visited it passes it otherwise it calls
+	 * traversePath from class Vert. Then it closes the path from the start cap
+	 * to the end cap. At this point the genome data structure is complete And
+	 * lastly, it populates the grey vector by iterating through the SB Vector
+	 * and adding all grey lines.
+	 **************************************************************************/
+	public void traverseAndClose () {
+		Vector dump = new Vector (); // dump for visited verts
+		Vector greybin = new Vector ();
+		Vert start;
+		Vert end;
+		for (int i = 0; i < this.capVector.size (); i++) {
+			start = (Vert) this.capVector.elementAt (i);
+			if (!start.seen ()) {
+				if (start.getCap ().equals ("A")) {
+					// start.printMe();
+					end = start.getBlack ().traversePath (false, greybin);
+				} else { // is a B cap
+					greybin.add (start.getGrey ());
+					end = start.getGrey ().traversePath (true, greybin);
+				}
+				start.setSeen (true);
+				end.setSeen (true);
+				start.closePath (end);
+				dump.add (start);
+			}// end if
+		}// end for
+		// this.greyVector=greybin;
+		SB tempSB;
+		for (int i = 0; i < sbVector.size (); i++) {
+			tempSB = (SB) sbVector.elementAt (i);
+			greyVector.add (tempSB.getLeft ().getGrey ());
+			greyVector.add (tempSB.getRight ().getGrey ());
+		}
+
+		// this.capVector=dump;
+	}// end traverseAndClose
+
+	// generates an Array of SB Vector of size 0 to Max Value of genome.
+	public SB [] toSBArray () {
+		SB [] sbArray;
+		SB tempSB;
+		int max = 0; // max value of genes for array size
+		for (Enumeration e = this.sbVector.elements (); e.hasMoreElements ();) {
+			tempSB = (SB) e.nextElement ();
+			if (max < Math.abs (tempSB.getValueA ()))
+				max = Math.abs (tempSB.getValueA ());
+		}
+		sbArray = new SB [max];// size of array
+		// load SB array indexed on gene values
+		for (Enumeration e = this.sbVector.elements (); e.hasMoreElements ();) {
+			tempSB = (SB) e.nextElement ();
+			sbArray[Math.abs (tempSB.getValueA ()) - 1] = tempSB;
+		}
+		return sbArray;
+	}
+
+	public int [] toIntArray (String s) {
+		StringTokenizer token = new StringTokenizer (s);
+		int intArray[] = new int [token.countTokens ()];
+		int count = 0;
+		while (token.hasMoreTokens ()) {
+			intArray[count++] = (Integer.parseInt (token.nextToken ()));
+		}// end while hasMoreTokens
+		return intArray;
+	}// end toSBArray
+
+	public void loadGenome (StringTokenizer tokenA, StringTokenizer tokenB) {
+		while (tokenA.hasMoreTokens ()) {
+			try {
+				this.parseAndLoadBlack ((tokenA.nextToken ()).trim ());
+			} catch (Exception e) {
+				System.out.println ("loadBlack error");
+				System.out.println (e);
+			}
+		}// end while
+		// this.printSBV();
+		SB [] sbArray = this.toSBArray ();
+		while (tokenB.hasMoreTokens ()) {
+			try {
+				this.parseAndLoadGrey ((tokenB.nextToken ()).trim (), sbArray);
+			} catch (Exception e) {
+				System.out.println ("load Grey error");
+				System.out.println (e);
+			}
+		}// end while
+		this.traverseAndClose ();
+	}
+
+	public Vert traverseBlack (Vert last, SB current) {
+		try {
+			current.setVisited (true);
+			Vert next = current.getLeft ().getBlack ();
+			if (last == current.getLeft ()) {
+				next = current.getRight ().getBlack ();
+				buf.append (current.getValueA () + " ");
+			} else {
+				buf.append ((current.getValueA () * -1) + " ");
+			}
+			if (!next.isCap () && !next.getSB ().wasVisited ()) {
+				return traverseBlack (next, next.getSB ());
+			}
+			return next;
+		} catch (Exception e) {
+			return last;
+		}
+	}
+
+	public void resetSbVisit () {
+		SB tempSB;
+		for (Enumeration e = this.sbVector.elements (); e.hasMoreElements ();) {
+			tempSB = (SB) e.nextElement ();
+			tempSB.setVisited (false);
+		}
+	}
+
+	// prints state of SB vector
+	public void printSBV () {
+		SB tempSB;
+		for (Enumeration e = this.sbVector.elements (); e.hasMoreElements ();) {
+			tempSB = (SB) e.nextElement ();
+			tempSB.getLeft ().printMe ();
+			tempSB.getRight ().printMe ();
+
+		}// end for
+		/***********************************************************************
+		 * uncomment to print the caps** Vert tempCap; for(Enumeration
+		 * e=this.capVector.elements();e.hasMoreElements();){
+		 * tempCap=(Vert)e.nextElement(); tempCap.printMe(); }//end for
+		 **********************************************************************/
+	}
+
+	// calls traverseBlack of class SB and prints the SB Values.
+	// currently buggy
+	public void printSBBlack () {
+		Vert v;
+		for (int i = 0; i < this.capVector.size (); i++) {
+			v = (Vert) this.capVector.elementAt (i);
+			if (!v.getBlack ().getSB ().wasVisited ()
+					&& !v.getBlack ().isCap ()) {
+				buf.append ("[" + v.getGene () + " ");
+				v = traverseBlack (v.getBlack (), v.getBlack ().getSB ());
+				buf.append (v.getGene () + "]\n");
+			}
+		}
+		SB tempSB;
+		for (Enumeration e = this.sbVector.elements (); e.hasMoreElements ();) {
+			tempSB = (SB) e.nextElement ();
+			if (!tempSB.wasVisited ()) {
+				buf.append ("[CI ");
+				traverseBlack (tempSB.getLeft (), tempSB);
+				buf.append ("]\n");
+			}
+		}
+		this.resetSbVisit ();
+	}// end printSBBlack
+
+}// end class DCJ
diff --git a/src/org/gel/mauve/dcj/DCJWindow.java b/src/org/gel/mauve/dcj/DCJWindow.java
new file mode 100644
index 0000000..583aa13
--- /dev/null
+++ b/src/org/gel/mauve/dcj/DCJWindow.java
@@ -0,0 +1,261 @@
+package org.gel.mauve.dcj;
+
+import java.util.*;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import javax.swing.text.*;
+import java.applet.Applet;
+/**
+ * @deprecated
+ * @author Mike Tsai
+ *
+ */
+public class DCJWindow extends JWindow {
+
+	TextArea input, output, log, ops;
+
+	int fWIDTH = 600;
+
+	int fHEIGHT = 300;
+
+	Panel toptopPanel;
+
+	CardLayout cards;
+
+	String box;
+
+	String pattern;
+
+	String defaultInput = ""
+			+ "1 -32 17 2 23 12 3 20 6 30 7 8 21 31 24 9 -10 -18 11 33 -28 19 14 34 13 25 4 22 -29 26 5 35 -15 -27 -16 -36 $,\n1 30 7 2 23 12 3 -32 6 8 21 31 9 -10 11 19 14 18 33 -13 -5 -22 -4 -25 -20 -36 17 -26 34 -16 -35 15 -24 -27 29 -28 $,\n1 30 7 2 23 12 3 -32 6 8 21 31 9 -10 11 19 14 18 33 28 -29 27 24 -15 35 16 -34 26 -17 36 20 25 4 22 5 13 $,\n1 25 2 23 17 12 3 20 6 15 30 27 31 18 -19 -9 -21 -8 -7 33 -28 10 11 32 -4 -24 -13 -34 -14 22 -29 26 5 35 -16 -36 $,\n1 25 2 23 17 12 3 20 6 15 30 2 [...]
+
+	public static void startDCJ (String s1) {
+		DCJWindow w = new DCJWindow ();
+		w.performDCJ (s1);
+	}
+
+	private Vector parseInput (String s) {
+		StringTokenizer token = new StringTokenizer (s, ",");
+		StringTokenizer newtoken;
+		Vector v = new Vector ();
+		String st;
+		while (token.hasMoreTokens ()) {
+			st = (token.nextToken ().trim ());
+			if (st.length () > 0)
+				v.add (st);
+		}
+		return v;
+
+	}// end parseInput
+
+	public void performDCJ (String s) {
+		build ();
+		
+		String box2 = "";
+		output.setText (box2);
+		log.setText (box2);
+		StringBuffer boxa = new StringBuffer (256);
+		StringBuffer boxb = new StringBuffer ();
+		StringBuffer boxc = new StringBuffer ();
+		Vector v = parseInput (s.trim ());
+		DCJ d;
+		int [][] distances = new int [v.size ()] [v.size ()];
+		for (int i = 0; i < v.size (); i++) {
+			distances[i][i] = 0;
+		}
+		for (int i = 0; i < v.size (); i++) {
+			for (int j = i + 1; j < v.size (); j++) {
+				boxa.append (i
+						+ " to "
+						+ j
+						+ "\n"
+						+ (d = new DCJ (new StringTokenizer ((String) v
+								.elementAt (i), "$"), new StringTokenizer (
+								(String) v.elementAt (j), "$"))).getLog ());
+				distances[i][j] = d.getCount ();
+				distances[j][i] = d.getCount ();
+				boxc.append ("\n" + i + " to " + j + "\n");
+				boxc.append (d.getOpBuf ());
+			}// end for j
+		}// end for i
+	/*	for (int i = 0; i < v.size (); i++) {
+			for (int j = 0; j < v.size (); j++) {
+				boxb.append (distances[i][j] + "	");
+			}
+			boxb.append ("\n");
+		}*/
+		output.setText (boxb.toString ());
+		ops.setText (boxc.toString ());
+		log.setText (boxa.toString ());
+		
+
+	}// end actionPerformed
+
+	public void build () {
+		JFrame frame = new JFrame ("DCJ");
+		frame.setSize (fWIDTH, fHEIGHT);
+		JPanel content = new JPanel (new BorderLayout ());
+		frame.getContentPane ().add (content, BorderLayout.CENTER);
+		box = "";
+		setLayout (new BorderLayout ());
+		// /create top panel
+		Panel topPanel = new Panel ();
+
+		topPanel.setLayout (new BorderLayout ());
+		// top top panel with cards
+		cards = new CardLayout ();
+		toptopPanel = new Panel ();
+		toptopPanel.setLayout (cards);
+		topPanel.add (toptopPanel, BorderLayout.CENTER);
+		// top lower panel of buttons
+		Panel toplowerPanel = new Panel ();
+		GridLayout butts = new GridLayout (1, 0);
+		butts.setHgap (50);
+		toplowerPanel.setLayout (butts);
+		topPanel.add (toplowerPanel, BorderLayout.SOUTH);
+		// make buttons
+		Button Bout = new Button ("matrix");
+		Button Bop = new Button ("operations");
+		Button Blog = new Button ("log");
+		toplowerPanel.add (Bout);
+		toplowerPanel.add (Bop);
+		toplowerPanel.add (Blog);
+		// make listener
+		ChangeCards cc = new ChangeCards ();
+		// register buttons
+		Bout.addActionListener (cc);
+		Blog.addActionListener (cc);
+		Bop.addActionListener (cc);
+		// /add the top panel
+		add (topPanel, BorderLayout.NORTH);
+		content.add (topPanel, BorderLayout.CENTER);
+
+		// /Add output text to cards panel
+		output = new TextArea (box, 25, 40);
+		output.setEditable (false);
+		output.setFont (new Font ("monospaced", Font.ITALIC, 12));
+		toptopPanel.add ("matrix", output);
+		cards.show (toptopPanel, "matrix");
+		// /Add DCJ Operations text to cards panel
+		ops = new TextArea (box, 25, 40);
+		ops.setEditable (false);
+		ops.setFont (new Font ("monospaced", Font.PLAIN, 12));
+		toptopPanel.add ("operations", ops);
+		// /Add log text area
+		log = new TextArea ("", 25, 40);
+		log.setEditable (false);
+		log.setFont (new Font ("monospaced", Font.PLAIN, 12));
+		toptopPanel.add ("log", log);
+		// //////////////////////////////////////////////////////////////
+		// /Create bottom panel
+		/** ************************************************************* */
+		Panel textInputField = new Panel ();
+		Panel forTheButtons = new Panel ();
+		textInputField.setLayout (new BorderLayout ());
+		forTheButtons.setLayout (new GridLayout (1, 0));
+		add (textInputField);
+		// /create submit button for bottom pane
+		Button submitB = new Button ("Submit");
+		Button clear = new Button ("Clear");
+		forTheButtons.add (submitB);
+		forTheButtons.add (clear);
+		textInputField.add (forTheButtons, BorderLayout.SOUTH);
+		// /Here's the text area for it.
+		input = new TextArea ();
+		textInputField.add (input);
+		// /add listener submitButton
+		ClearData clearit = new ClearData ();
+		SubmitData submitButton = new SubmitData ();
+		// /register listener to button
+		submitB.addActionListener (submitButton);
+		clear.addActionListener (clearit);
+
+		input.setText (defaultInput);
+		/** *************************************************************** */
+		output.setText ("");
+		frame.setVisible (true);
+	}
+
+	private class ChangeCards implements ActionListener {
+		public void actionPerformed (ActionEvent e) {
+			if (e.getActionCommand () == "matrix") {
+				cards.show (toptopPanel, "matrix");
+			}
+			if (e.getActionCommand () == "log") {
+				cards.show (toptopPanel, "log");
+			}
+			if (e.getActionCommand () == "operations") {
+				cards.show (toptopPanel, "operations");
+			}
+		}// end actionPerformed
+	}// end ChangeCards
+
+	private class ClearData implements ActionListener {
+		public void actionPerformed (ActionEvent e) {
+			input.setText ("");
+			output.setText ("");
+			log.setText ("");
+		}// end actionPerformed
+	}// end clearData
+
+	private class SubmitData implements ActionListener {
+
+		private Vector parseInput (String s) {
+			StringTokenizer token = new StringTokenizer (s, ",");
+			StringTokenizer newtoken;
+			Vector v = new Vector ();
+			String st;
+			while (token.hasMoreTokens ()) {
+				st = (token.nextToken ().trim ());
+				if (st.length () > 0)
+					v.add (st);
+			}
+			return v;
+
+		}// end parseInput
+
+		public void actionPerformed (ActionEvent e) {
+			String box2 = "";
+			output.setText (box2);
+			log.setText (box2);
+			StringBuffer boxa = new StringBuffer (256);
+			StringBuffer boxb = new StringBuffer ();
+			StringBuffer boxc = new StringBuffer ();
+			Vector v = parseInput ((input.getText ()).trim ());
+			DCJ d;
+			int [][] distances = new int [v.size ()] [v.size ()];
+			for (int i = 0; i < v.size (); i++) {
+				distances[i][i] = 0;
+			}
+			for (int i = 0; i < v.size (); i++) {
+				for (int j = i + 1; j < v.size (); j++) {
+					boxa.append (i
+							+ " to "
+							+ j
+							+ "\n"
+							+ (d = new DCJ (new StringTokenizer ((String) v
+									.elementAt (i), "$"), new StringTokenizer (
+									(String) v.elementAt (j), "$"))).getLog ());
+					distances[i][j] = d.getCount ();
+					distances[j][i] = d.getCount ();
+					boxc.append ("\n" + i + " to " + j + "\n");
+					boxc.append (d.getOpBuf ());
+				}// end for j
+			}// end for i
+			for (int i = 0; i < v.size (); i++) {
+				for (int j = 0; j < v.size (); j++) {
+					boxb.append (distances[i][j] + "	");
+				}
+				boxb.append ("\n");
+			}
+			output.setText (boxb.toString ());
+			ops.setText (boxc.toString ());
+			log.setText (boxa.toString ());
+
+		}// end actionPerformed
+
+	}// end SubmitData
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/dcjx/Adjacency.java b/src/org/gel/mauve/dcjx/Adjacency.java
new file mode 100644
index 0000000..ae6c972
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/Adjacency.java
@@ -0,0 +1,102 @@
+package org.gel.mauve.dcjx;
+
+public class Adjacency{
+	
+	private String first;
+	private String second;
+	private boolean isTelo;
+	
+	private Adjacency e1;
+	
+	private Adjacency e2;
+	
+	private boolean visited;
+	
+	public Adjacency(String first, String second){
+		this.first = first;
+		this.second = second;
+		isTelo = false;
+		visited = false;
+	}
+	public Adjacency(String first){
+		this.first = first;
+		this.second = Constants.TELOMERE;
+		isTelo = true;
+		visited = false;
+	}
+	
+	public String toString(){
+	 	if (isTelo){
+	 		return "{"+first+"}";
+	 	} else {
+	 		return "{"+first+","+second+"}";
+	 	}
+	}
+	
+	public boolean isTelo(){
+		return isTelo;
+	}
+	
+	public boolean wasVisited(){
+		return visited;
+	}
+	
+	public void resetVisited(){
+		visited = false;
+	}
+	
+	public void setVisited(){
+		visited = true;
+	}
+	
+	public Adjacency getE1(){
+		return e1;
+	}
+	
+	public Adjacency getE2(){
+		return e2;
+	}
+	
+	public static void addAdjacencyEdge(Adjacency a, Adjacency b){
+	//	System.out.println("Linking " + a.toString() + " and " + b.toString());
+		if (a.e1 == null){
+			a.e1 = b;
+			if (b.e1 == null){
+				b.e1 = a;
+			} else if (b.e2 == null) {
+				b.e2 = a;
+			} else {
+				throw new IllegalArgumentException("this Adjacency is full");
+			}
+		} else if (a.e2 == null){
+			a.e2 = b;
+			if (b.e1 == null){
+				b.e1 = a;
+			} else if (b.e2 == null) {
+				b.e2 = a;
+			} else {
+				throw new IllegalArgumentException("this Adjacency is full");
+			}
+		} else {
+			throw new IllegalArgumentException("this Adjacency is full");
+		}
+		
+	}
+	
+	public String getFirstBlock(){
+		return first.substring(0,first.length()-2);
+	}
+	
+	public String getFirstBlockEnd(){
+		return first;
+	}
+	
+	public String getSecondBlockEnd(){
+		return second;
+	}
+	
+	public String getSecondBlock(){
+		return second.substring(0,second.length()-2);
+	}
+	
+}
diff --git a/src/org/gel/mauve/dcjx/AdjacencyGraph.java b/src/org/gel/mauve/dcjx/AdjacencyGraph.java
new file mode 100644
index 0000000..400124e
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/AdjacencyGraph.java
@@ -0,0 +1,255 @@
+package org.gel.mauve.dcjx;
+
+public class AdjacencyGraph {
+	
+	private Adjacency[] adjA;
+	
+//	private boolean[] visitA;
+	
+	private Adjacency[] adjB;
+	
+	private int numCycles;
+	
+	private int numOddPaths;
+	
+	private int numLen2Cycles;
+	
+	private int numLen1Paths;
+	
+	private int numPathsGreaterThan2;
+	
+//	private boolean[] visitB;
+	
+	public AdjacencyGraph(Permutation A, Permutation B){
+		adjA = A.getAdjacencies();
+		adjB = B.getAdjacencies();
+//		visitA = new boolean[adjA.length];
+//		visitB = new boolean[adjB.length];
+		FastAccessTable fatA = A.getFAT(); // this may be useful later
+		FastAccessTable fatB = B.getFAT();
+		
+		for (int i = 0; i < adjA.length; i++){
+			try {
+				
+				if (adjA[i].isTelo()){
+					Adjacency.addAdjacencyEdge(adjA[i],fatB.getAdjacency(adjA[i].getFirstBlockEnd()));
+				} else {
+					Adjacency.addAdjacencyEdge(adjA[i],fatB.getAdjacency(adjA[i].getFirstBlockEnd()));
+					Adjacency.addAdjacencyEdge(adjA[i],fatB.getAdjacency(adjA[i].getSecondBlockEnd()));
+				}
+			
+			} catch (NullPointerException e ){
+				String msg = e.getMessage();
+				msg = msg + " : null at adjA["+i+"]";
+				System.err.println(msg);
+				throw e;
+			}
+		}
+		numCycles = countCycles();
+		numOddPaths = countOddPaths();
+		numLen2Cycles = countLen2Cycles();
+		numLen1Paths = countSingleEdgePaths();
+		numPathsGreaterThan2 = countPathsGreaterThan2(); 
+		
+	}
+	
+	public Adjacency[] getGenomeA(){
+		return adjA;
+	}
+	
+	public Adjacency[] getGenomeB(){
+		return adjB;
+	}
+	
+	/**
+	 * Returns the number of paths in the adjacency
+	 * graph with an odd length
+	 * 
+	 * @return the number of paths with an odd length
+	 */
+	public int numOddPaths(){
+		return numOddPaths;
+	}
+	
+	/**
+	 * Returns the number of cycles in the adjacency graph
+	 * 
+	 * @return the number of cycles in the adjacency graph
+	 */
+	public int numCycles(){
+		return numCycles;
+	}
+	
+	/**
+	 * Returns the number of cycles in the adjacency
+	 * graph of length 2
+	 * 
+	 * @return the number of cycles of length 2
+	 */
+	public int numLen2Cycles(){
+		return numLen2Cycles;
+	}
+	
+	/**
+	 * Returns the number of paths in the adjacency
+	 * graph of length 1
+	 * 
+	 * @return the number of paths of length 1
+	 */
+	public int numLen1Paths(){
+		return numLen1Paths;
+	}
+	
+	/**
+	 * Returns the number of paths in the adjacency 
+	 * graph of length 2 or more
+	 * 
+	 * @return the number of paths of length 2 or more
+	 */
+	public int numPaths2(){
+		return numPathsGreaterThan2;
+	}
+	
+	private int countLen2Cycles(){
+		int numCyc = 0;	
+		for (int i = 0; i < adjA.length; i++){
+			if (adjA[i].wasVisited()){
+				continue;
+			} else {
+				if (isCycle(adjA[i])){
+					resetVisited(adjA[i]);
+					if (countEdges(adjA[i]) == 2)
+						numCyc++;
+				}
+			}
+		}
+		resetVisitedAll();
+		return numCyc;
+	}
+	
+	private int countCycles(){
+		int numCyc = 0;	
+		for (int i = 0; i < adjA.length; i++){
+			if (adjA[i].wasVisited()){
+				continue;
+			} else {
+				if (isCycle(adjA[i])){
+					numCyc++;
+				}
+			}
+		}
+		resetVisitedAll();
+		return numCyc;
+	}
+	
+	private int countOddPaths(){
+		int numOddPath = 0;
+		for (int i = 0; i < adjA.length; i++){
+			if (adjA[i].wasVisited()) {
+				continue;
+			} else {
+				if (countEdges(adjA[i]) % 2 == 1)
+					numOddPath++;
+			}
+		}
+		resetVisitedAll();
+		return numOddPath;
+	}
+	
+	private int countSingleEdgePaths(){
+		int numSingleEdgePath = 0;
+		for (int i = 0; i < adjA.length; i++){
+			if (adjA[i].wasVisited()) {
+				continue;
+			} else {
+				if (countEdges(adjA[i]) == 1)
+					numSingleEdgePath++;
+			}
+		}
+		resetVisitedAll();
+		return numSingleEdgePath;
+	}
+	
+	private int countPathsGreaterThan2(){
+		int numPathGreaterThan2 = 0;
+		for (int i = 0; i < adjA.length; i++){
+			if (adjA[i].wasVisited()) {
+				continue;
+			} else {
+				if (!isCycle(adjA[i])){
+					resetVisited(adjA[i]);
+					if (countEdges(adjA[i]) >= 2)
+						numPathGreaterThan2++;
+				} 
+			}
+		}
+		resetVisitedAll();
+		return numPathGreaterThan2;
+	}
+	
+	private int countEdges(Adjacency a){
+		if (a.wasVisited()){  // we've found a cycle 
+			return 0;         
+		}
+		if (a.isTelo()){
+			a.setVisited();
+			if (a.getE1().wasVisited()){ // the only vertex directly connected was visited, so we're at the end of a path
+				return 0;
+			} else { // this is the first vertex we've visited in traversing this connected component
+				return 1 + countEdges(a.getE1());
+			}
+		
+		} else { // not at a telomere 
+			a.setVisited();
+			if (!(a.getE1().wasVisited() || a.getE2().wasVisited())) { // started at internal node
+				// check both. we might not come back here. Don't know if this is a path or a cycle
+				return 2 + countEdges(a.getE1()) + countEdges(a.getE2());
+			} else if (a.getE1().wasVisited() && !a.getE2().wasVisited()) {   // make sure we don't go back to
+				return 1 + countEdges(a.getE2());                             // the vertex we just came from
+			} else if (!a.getE1().wasVisited() && a.getE2().wasVisited()) {   // make sure we don't go back to
+				return 1 + countEdges(a.getE1());                             // the vertex we just came from
+			} else { // we've found a cycle. Stop the recursion
+				return 0;
+			}
+		}
+	}
+	
+	private boolean isCycle(Adjacency a){
+		if (a.wasVisited()){
+			return true;
+		} else if (a.isTelo()){
+			a.setVisited();
+			return false;
+	//	} else if (!(a.getE1().wasVisited() || a.getE2().wasVisited())){
+	//		a.setVisited();
+	//		return 
+		} else {
+			a.setVisited();
+			return isCycle(a.getE1()) && isCycle(a.getE2());
+		}
+	}
+	
+	private void resetVisitedAll(){
+		for (int i = 0; i < adjA.length; i++){
+			adjA[i].resetVisited();	
+		} 
+		for (int i = 0; i < adjB.length; i++){
+			adjB[i].resetVisited();
+		}
+	}
+	
+	private void resetVisited(Adjacency a){
+		if (!a.wasVisited()){
+			a.resetVisited();
+			resetVisited(a.getE1());
+			if (!a.isTelo()){
+				resetVisited(a.getE2());
+			}
+		} else
+			return;
+	}
+	
+	
+	
+
+}
diff --git a/src/org/gel/mauve/dcjx/Block.java b/src/org/gel/mauve/dcjx/Block.java
new file mode 100644
index 0000000..faf3ae5
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/Block.java
@@ -0,0 +1,61 @@
+package org.gel.mauve.dcjx;
+
+public class Block {
+	
+	/** 
+	 * The index of this block on the chromosome. 
+	 * This number is only unique within the chromosome holding this block
+	 * 
+	 * NOTE: this is not the same as a block ID, which can be obtained from 
+	 *       blockIdMap using this.name
+	 */
+	private int blockIdx;
+	
+	private String name;
+	
+	private boolean inv;
+	
+	public Block(int blockIdx, String blockName, boolean inv){
+		this.blockIdx = blockIdx;
+		this.name = blockName;
+		this.inv = inv;
+	}
+	
+	public String toString(){
+		if (inv){
+			return "-"+name;
+		} else {
+			return name;
+		}
+	}
+	
+	public String getLeftEnd(){
+		if (inv){
+			return name + BlockEnd.HEAD_TAG;
+		} else {
+			return name + BlockEnd.TAIL_TAG;
+		}
+	}
+	
+	public String getRightEnd(){
+		if (inv){
+			return name + BlockEnd.TAIL_TAG;
+		} else {
+			return name + BlockEnd.HEAD_TAG;
+		}
+	}
+	
+	public int getIdx(){
+		return blockIdx;
+	}
+	
+	public String getName(){
+		return name;
+	} 
+	
+	public boolean isInverted(){
+		return inv;
+	}
+	
+	
+}
diff --git a/src/org/gel/mauve/dcjx/BlockEnd.java b/src/org/gel/mauve/dcjx/BlockEnd.java
new file mode 100644
index 0000000..65d871b
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/BlockEnd.java
@@ -0,0 +1,12 @@
+package org.gel.mauve.dcjx;
+
+public interface BlockEnd {
+	
+
+	public static final String TAIL_TAG = "_t";
+	
+	public static final String HEAD_TAG = "_h";
+	
+	String getName();
+
+}
diff --git a/src/org/gel/mauve/dcjx/Constants.java b/src/org/gel/mauve/dcjx/Constants.java
new file mode 100644
index 0000000..01f7f0d
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/Constants.java
@@ -0,0 +1,12 @@
+package org.gel.mauve.dcjx;
+
+public interface Constants {
+
+	//public static final String CIRCULAR_CHAR = "*";
+	
+	public static final String TELOMERE = "<>";
+	
+	public static final String TAIL_TAG = "_t";
+	
+	public static final String HEAD_TAG = "_h";
+}
diff --git a/src/org/gel/mauve/dcjx/Contig.java b/src/org/gel/mauve/dcjx/Contig.java
new file mode 100644
index 0000000..d8a6efb
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/Contig.java
@@ -0,0 +1,109 @@
+package org.gel.mauve.dcjx;
+
+import java.util.Comparator;
+
+
+
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.gel.mauve.MauveConstants;
+
+//import org.halophiles.assembly.stat.LCB;
+
+// This might just be over kill, but it might help.
+/**
+ * 
+ * @author atritt
+ */
+public class Contig {
+	
+	private static String VALID_BLOCK_NAME = "[A-Za-z0-9_-]+";
+	
+	private boolean isCirc;
+	
+	private int numBlocks;
+	
+	private Block[] blocks;
+	
+	private String id;
+	
+	private String name = "";
+	
+	public Contig (String ch){
+		if (ch.endsWith(MauveConstants.CIRCULAR_CHAR)) {
+			isCirc = true;
+			ch = ch.substring(0,ch.length()-1);
+		} else {
+			isCirc = false;
+		}
+		StringTokenizer tok = new StringTokenizer(ch,",");
+		numBlocks = 0;
+		while(tok.hasMoreTokens()){
+			String tmp = tok.nextToken().trim();
+			if (tmp.matches(VALID_BLOCK_NAME)){
+				numBlocks++;
+			}
+		}
+		tok =  new StringTokenizer(ch,",");
+		blocks = new Block[numBlocks];
+		int i = 0;  // i := block index/count
+		while (tok.hasMoreTokens()){
+			String block = tok.nextToken().trim();
+			if (!block.matches(VALID_BLOCK_NAME)){
+				continue;
+			} 
+			// check to see if this block is inverted. 
+			boolean inv = false;
+			if (block.startsWith("-")){ 
+				inv = true;
+				// remove "-" to get the actual name
+				block = block.substring(1);
+			}
+			blocks[i] = new Block(i,block,inv);
+			i = i + 1;
+			// don't forget to manage our blockIdMap!
+		//	if (!blockIdMap.containsKey(block) ){
+		//		blockIdMap.put(block, blockIdMap.size());
+		//	}
+		}
+	}
+	
+	public Contig(String ch,  String name){
+		// Map<String,Integer> blockIdMap,
+		this(ch);
+		this.name = name;
+	}
+	
+	public boolean isCirc(){
+		return isCirc;
+	}
+	
+	public Block[] getBlocks(){
+		return blocks;
+	}
+	
+	public boolean hasBlocks(){
+		return blocks.length > 0;
+	}
+	
+	public String toString(){
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < blocks.length; i++){
+			sb.append(blocks[i].toString());
+			if (i < blocks.length - 1){
+				sb.append(",");
+			}
+		}
+		if(isCirc){
+			sb.append(MauveConstants.CIRCULAR_CHAR);
+		}
+		return sb.toString();
+	}
+	
+	public boolean equals(Contig o){
+		return id.equalsIgnoreCase(o.id);
+	}
+	
+} 
\ No newline at end of file
diff --git a/src/org/gel/mauve/dcjx/DCJ.java b/src/org/gel/mauve/dcjx/DCJ.java
new file mode 100644
index 0000000..d4cada9
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/DCJ.java
@@ -0,0 +1,116 @@
+package org.gel.mauve.dcjx;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DCJ {
+	
+	private Map<String,Integer> blockIdMap;
+	
+	private Permutation x;
+	
+	private Permutation y;
+	
+	private AdjacencyGraph adjXY;
+	
+	private int dcjDist;
+	
+	private int scjDist;
+	
+	private int bpDist;
+	
+	private int bpReuse;
+	
+	public DCJ(String genomeX, String genomeY){
+		blockIdMap = new HashMap<String,Integer>();
+		loadBlockIDMap(genomeX, blockIdMap);
+		loadBlockIDMap(genomeY, blockIdMap);
+		x = new Permutation(genomeX,blockIdMap);
+		y = new Permutation(genomeY,blockIdMap);
+		adjXY = new AdjacencyGraph(x,y);
+		calculateDistances();
+	}
+	
+	private void calculateDistances(){
+		dcjDist = blockIdMap.size() - 
+			(adjXY.numCycles() + adjXY.numOddPaths()/2);
+		bpDist = blockIdMap.size() - 
+			(adjXY.numLen2Cycles() + adjXY.numLen1Paths()/1);
+		scjDist = 2*bpDist - adjXY.numPaths2();
+	}
+	
+	public int numBlocks(){
+		return blockIdMap.size();
+	}
+	
+	/**
+	 * dcj = N - (C-P(odd)/2)
+	 * @return
+	 */
+	public int dcjDistance(){
+		return dcjDist;
+	}
+	/**
+	 * scj = 2*bp - P<sub>>=2</sub> 
+	 * @return
+	 */
+	public int scjDistance(){
+		return scjDist;
+	}
+	/**
+	 * bp = N - (C<sub>2</sub> + P<sub>1</sub>/2)
+	 *  
+	 * @return breakpoint distance
+	 */
+	public int bpDistance(){
+		return bpDist;
+	}
+	
+	public AdjacencyGraph getAdjacencyGraph(){
+		return adjXY;
+	}
+	
+	private static void loadBlockIDMap(String perm, Map<String,Integer> map){
+		String[] blks = perm.split("[\\*$, ]+");
+		for (int i = 0; i < blks.length; i++){
+			blks[i] = blks[i].trim();
+			if (blks[i].length() == 0) // empty contig
+				continue;
+			if (blks[i].startsWith("-")){
+				blks[i] = blks[i].substring(1);
+			}
+			if (blks[i].endsWith("*")){
+				blks[i] = blks[i].substring(0,blks[i].length()-1);
+			}
+			if(!map.containsKey(blks[i])){
+				int count = map.size();
+				map.put(blks[i], count);
+			}
+		}
+		
+	}
+	
+	public static int computeDCJ(Permutation x, Permutation y, Map<String,Integer> blockIdMap){
+		AdjacencyGraph agXY = new AdjacencyGraph(x, y);
+		int C = agXY.numCycles();
+		int I = agXY.numOddPaths();
+		int ret = blockIdMap.size() - (C + I/2);
+		return ret;
+	}
+	
+	public static int computeDCJ(String genomeX, String genomeY){
+		Map<String,Integer> blockIdMap = 
+			new HashMap<String,Integer>();
+		DCJ.loadBlockIDMap(genomeX, blockIdMap);
+		DCJ.loadBlockIDMap(genomeY, blockIdMap);
+		Permutation x = new Permutation(genomeX, blockIdMap);
+		Permutation y = new Permutation(genomeX, blockIdMap);
+		return computeDCJ(x, y, blockIdMap);
+	}
+	
+	public static void main(String[] args){
+		
+	}
+	
+
+}
diff --git a/src/org/gel/mauve/dcjx/DCJDistance.java b/src/org/gel/mauve/dcjx/DCJDistance.java
new file mode 100644
index 0000000..cd887ce
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/DCJDistance.java
@@ -0,0 +1,231 @@
+package org.gel.mauve.dcjx;
+
+import java.awt.CardLayout;
+import java.awt.Panel;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Vector;
+
+import javax.swing.JTextArea;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.analysis.PermutationExporter;
+import org.gel.mauve.gui.AnalysisDisplayWindow;
+
+public class DCJDistance {
+
+//	private JTextArea matrix;
+	
+	private static final String NWAY_COMMAND = "N-Way";
+	private static final String NWAY_DESC = "Distances based on N-Way LCBs";
+	private static final String PWISE_COMMAND = "Pairwise";
+	private static final String PWISE_DESC = "Distances based on Pairwise LCBs";
+	private static final String NBLKS_COMMAND = "NoBlocks";
+	private static final String NBLKS_DESC = "Number of blocks between each pair";
+	
+	private static final String temp = "Running...";
+	private static final String error = "Error computing DCJ distances! Please report bug to atritt at ucdavis.edu";
+	
+	private static HashMap<String,DCJDistance> modelMap;
+	
+	private JTextArea nwayTA, pwiseTA;
+	
+	private AnalysisDisplayWindow adw;
+	
+	private int fWIDTH = 400;
+
+	private int fHEIGHT = 400;
+	
+	private DCJ[][] nWayDist;
+	
+	private DCJ[][] pWiseDist;
+	
+	private static DCJDistance curr;
+	
+	
+//	private JTextArea log;
+	// event listener
+	
+	public static void launchWindow(BaseViewerModel model) {
+		if (modelMap == null)
+			modelMap = new HashMap<String,DCJDistance>();
+		String key = model.getSrc().getAbsolutePath();
+		if (modelMap.containsKey(key)) {	
+			modelMap.get(key).adw.showWindow();
+		}else if (model instanceof XmfaViewerModel){
+			modelMap.put(key, new DCJDistance((XmfaViewerModel)model));
+		} else {
+			System.err.println("Can't compute DCJ distance without contig boundaries.");
+		}	
+	}
+	
+	public DCJDistance (XmfaViewerModel model) {
+		int numGenomes = model.getGenomes().size();
+		// lets have some fun with interfaces
+		DistanceFunction dcj = new DistanceFunction(){
+			public int distance(DCJ dcj) { return dcj.dcjDistance();}
+		};
+		DistanceFunction scj = new DistanceFunction(){
+			public int distance(DCJ dcj){ return dcj.scjDistance();}
+		};
+		DistanceFunction bp = new DistanceFunction(){
+			public int distance(DCJ dcj){ return dcj.bpDistance();}
+		};
+		if (numGenomes > 2){
+			adw = new AnalysisDisplayWindow("DCJ - "+model.getSrc().getName(), fWIDTH, fHEIGHT);
+			nwayTA = adw.addContentPanel(NWAY_COMMAND, NWAY_DESC, true);
+			pwiseTA = adw.addContentPanel(PWISE_COMMAND, PWISE_DESC, false);
+			adw.showWindow();
+			Vector<Genome> v = model.getGenomes();
+			Genome[] genomes = v.toArray(new Genome[v.size()]);
+			int numGen = genomes.length;
+			nWayDist = new DCJ[numGen][numGen];
+			pWiseDist = new DCJ[numGen][numGen];
+			nwayTA.append(temp);
+			pwiseTA.append(temp);
+			try {
+				loadMatrices(model, nWayDist, pWiseDist);
+				nwayTA.replaceRange("", 0, temp.length());
+				pwiseTA.replaceRange("", 0, temp.length());
+				nwayTA.append("# Rearrangement distances based on N-way LCBs\n");
+				nwayTA.append("# No. of blocks = " + nWayDist[1][0].numBlocks()+" #\n\n");
+				pwiseTA.append("# Rearrangement distances based on pairwise LCBs #\n\n");
+				printHeader(pwiseTA,model);
+				printHeader(nwayTA,model);
+			//	PrintStream out = new PrintStream(textArea2OutputStream(nwayOut));
+				nwayTA.append("\n");
+				pwiseTA.append("\n");
+				PrintStream out = new PrintStream(textArea2OutputStream(nwayTA));
+				out.print("# DCJ distance\n");
+				printDistance(out, nWayDist, dcj);
+				out.print("\n# SCJ distance\n");
+				printDistance(out, nWayDist, scj);
+				out.print("\n# Breakpoint distance\n");
+				printDistance(out, nWayDist, bp);
+				DistanceFunction nblks = new DistanceFunction(){
+					public int distance(DCJ dcj){ return dcj.numBlocks();}
+				};
+				out = new PrintStream(textArea2OutputStream(pwiseTA));
+				out.print("# Number of Blocks\n");
+				printDistance(out, pWiseDist, nblks);
+				out.print("\n# DCJ distance\n");
+				printDistance(out, pWiseDist, dcj);
+				out.print("\n# SCJ distance\n");
+				printDistance(out, pWiseDist, scj);
+				out.print("\n# Breakpoint distance\n");
+				printDistance(out, pWiseDist, bp);
+			} catch (Exception e){
+				nwayTA.replaceRange(error, 0, temp.length());
+				pwiseTA.replaceRange(error, 0, temp.length());
+				e.printStackTrace();
+			}
+		} else {
+			adw = new AnalysisDisplayWindow("DCJ - "+model.getSrc().getName(), fWIDTH, fHEIGHT);
+			nwayTA = adw.addContentPanel("DCJ", NWAY_DESC, true);
+			adw.showWindow();
+			nwayTA.append(temp);
+			try {
+				String[] perms = PermutationExporter.getPermStrings(model,true);
+				DCJ dcjDist = new DCJ(perms[0], perms[1]);
+				nwayTA.replaceRange("", 0, temp.length());
+				StringBuilder sb = new StringBuilder();
+				sb.append("no. of blocks = " + dcjDist.numBlocks()+" #\n\n");
+				sb.append("# DCJ distance\n");
+				sb.append("0\n"+dcjDist.dcjDistance()+"\t0\n\n");
+				sb.append("# SCJ distance\n");
+				sb.append("0\n"+dcjDist.scjDistance()+"\t0\n\n");
+				sb.append("# Breakpoint distance\n");
+				sb.append("0\n"+dcjDist.bpDistance()+"\t0\n\n");
+				nwayTA.setText(sb.toString());
+			} catch (Exception e){
+				nwayTA.replaceRange(error, 0, temp.length());
+				System.err.println(error);
+				e.printStackTrace();
+				
+			}
+		}
+		
+	}
+
+	
+	private static void printNBlks(PrintStream out, DCJ[][] mat){
+		for (int i = 0; i < mat.length; i++){
+			for (int j = 0; j < i; j++){
+				out.print(mat[i][j].numBlocks()+"\t");
+			}
+			out.print("0\n");
+		}
+	}
+	
+	private static void printDistance(PrintStream out, DCJ[][] mat, DistanceFunction func){
+		for (int i = 0; i < mat.length; i++){
+			for (int j = 0; j < i; j++){
+				out.print(func.distance(mat[i][j])+"\t");
+			}
+			out.print("0\n");
+		}
+	}
+	
+	private static void printHeader(JTextArea nblksTA2, XmfaViewerModel model){
+		Vector<Genome> v = model.getGenomes();
+		Genome[] genomes = v.toArray(new Genome[v.size()]);
+		for (int i = 0; i < genomes.length; i++){
+			nblksTA2.append("# "+(i+1)+": " + genomes[i].getDisplayName()+"\n");
+		}
+	}
+	
+	private static void printHeader(StringBuilder sb, XmfaViewerModel model){
+		Vector<Genome> v = model.getGenomes();
+		Genome[] genomes = v.toArray(new Genome[v.size()]);
+		for (int i = 0; i < genomes.length; i++){
+			sb.append("# "+(i+1)+": " + genomes[i].getDisplayName()+"\n");
+		}
+	}
+	
+	private void loadMatrices(XmfaViewerModel model, DCJ[][] nway, DCJ[][] pwise){
+		Vector<Genome> v = model.getGenomes();
+		Genome[] genomes = new Genome[v.size()];
+		v.toArray(genomes);
+		Genome[] pair = new Genome[2];
+		String[] nWayPerms = PermutationExporter.getPermStrings(model, genomes,true);
+		String[] pairPerm = null;
+		DCJ[][] nWayDist = nway;
+		DCJ[][] pWiseDist = pwise;
+		for (int i = 0; i < genomes.length; i++){
+			for (int j = 0; j < i; j++){
+				pair[0] = genomes[i];
+				pair[1] = genomes[j];
+				pairPerm = PermutationExporter.getPermStrings(model, pair,true);
+				
+				if (!Permutation.equalContents(pairPerm[0], pairPerm[1])){
+					System.err.println("Unequal contents between genomes "+
+							i + " and " + j+"\n" + pairPerm[0] +"\n"+pairPerm[1]);
+				}		
+				nWayDist[i][j] = new DCJ(nWayPerms[i],nWayPerms[j]);
+				pWiseDist[i][j] = new DCJ(pairPerm[0],pairPerm[1]);
+			}
+		}		
+	}
+	
+	// add
+	
+
+
+	OutputStream textArea2OutputStream(final JTextArea t)
+	    { return new OutputStream()
+	       { JTextArea ta = t;
+	         public void write(int b) //minimum implementation of an OutputStream
+	          { byte[] bs = new byte[1]; bs[0] = (byte) b;
+	            ta.append(new String(bs));
+	          }//write
+	       };
+	    }
+	
+	private interface DistanceFunction {
+		public int distance(DCJ dcj);
+	}
+
+}
diff --git a/src/org/gel/mauve/dcjx/FastAccessTable.java b/src/org/gel/mauve/dcjx/FastAccessTable.java
new file mode 100644
index 0000000..65672c8
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/FastAccessTable.java
@@ -0,0 +1,38 @@
+package org.gel.mauve.dcjx;
+
+import java.util.Map;
+
+public class FastAccessTable {
+	
+	private Adjacency[] adjacencies;
+	
+	private int[][] locations;
+	
+	private Map<String,Integer> blockIdMap;
+	
+	public FastAccessTable(Adjacency[] adjs, int[][] locs, Map<String,Integer> blockIdMap){
+		adjacencies = adjs;
+		locations = locs;
+		this.blockIdMap = blockIdMap;
+	}
+	
+	public Adjacency getHead(String block){
+		return adjacencies[locations[blockIdMap.get(block)][1]];
+	}
+	
+	public Adjacency getTail(String block){
+		return adjacencies[locations[blockIdMap.get(block)][0]];
+	}
+	
+	public Adjacency getAdjacency(String blockEnd){
+		if (blockEnd.endsWith(BlockEnd.HEAD_TAG)){
+			String block = blockEnd.substring(0,blockEnd.length()-2);
+		 	return adjacencies[locations[blockIdMap.get(block)][1]];
+		} else {
+			String block = blockEnd.substring(0,blockEnd.length()-2);
+			return adjacencies[locations[blockIdMap.get(block)][0]];
+		}
+	}
+	
+}
+
diff --git a/src/org/gel/mauve/dcjx/Head.java b/src/org/gel/mauve/dcjx/Head.java
new file mode 100644
index 0000000..130eb60
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/Head.java
@@ -0,0 +1,18 @@
+package org.gel.mauve.dcjx;
+
+public class Head implements BlockEnd {
+
+	private String name;
+	
+//	private Block block;
+	
+	public Head(Block blk){
+//		block = blk;
+		name = blk.getName() + BlockEnd.HEAD_TAG;
+	}
+	
+	public String getName() {
+		return name;
+	}
+
+}
diff --git a/src/org/gel/mauve/dcjx/Permutation.java b/src/org/gel/mauve/dcjx/Permutation.java
new file mode 100644
index 0000000..cb9e3e0
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/Permutation.java
@@ -0,0 +1,196 @@
+package org.gel.mauve.dcjx;
+
+import java.io.PrintStream;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * 
+ * @author atritt
+ */
+public class Permutation {
+	
+	/** A map for holding identifiers */
+//	public static HashMap<String, Integer> blockIdMap = new HashMap<String, Integer>();
+	
+//	public static int BLOCK_COUNT = 0;
+	
+//	private Map<String, Integer> blockIdMap;
+	
+	private int numChrom;
+	
+	private Adjacency[] adj;
+	
+	/**
+	 *  a BLOCK_COUNT x 2 array storing positions of heads and tails in the Adjacency array
+	 *  loc[][0] := tail loc[][1] := head 
+	 */
+	private int[][] loc;
+	
+	private FastAccessTable fat;
+	
+	private Contig[] chrom;
+	
+	private int numLinear;
+	
+	private String name = "";
+	
+	public Permutation (String g, Map<String,Integer> blockIdMap, String name){
+		this(g, blockIdMap);
+		this.name = name;
+	}
+	
+	public Permutation (String g, Map<String,Integer> blockIdMap){
+	//	this.blockIdMap = blockIdMap;
+		StringTokenizer tok = new StringTokenizer(g,"$");
+		numChrom = tok.countTokens();
+		chrom = new Contig[numChrom];
+		int i = 0;
+		while(tok.hasMoreTokens()){
+			String c = tok.nextToken();
+				chrom[i] = new Contig(c);
+				if (!chrom[i].isCirc() && chrom[i].hasBlocks())
+					numLinear++;
+			i++;
+		}
+		loc = new int[blockIdMap.size()][2];
+		
+		// for N blocks and k linear chromosomes, the number of adjacencies = N + k
+		adj = new Adjacency[blockIdMap.size()+numLinear];
+		addAdjacencies(chrom,adj,loc, blockIdMap);  // we're doing a lot here... lets make a function for this.
+		fat = new FastAccessTable(adj, loc, blockIdMap);
+	}
+	
+	public String getName(){
+		return name;
+	}
+
+	public Adjacency[] getAdjacencies(){
+		return adj;
+	}
+	
+	public FastAccessTable getFAT(){
+		return fat;
+	}
+	
+	public void printDesc(PrintStream out){
+		out.println(name + ": " + this.toString());
+	}
+	
+	public String toString(){
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < chrom.length; i++){
+			sb.append(chrom[i].toString());
+			sb.append("$");
+		}
+		return sb.toString();
+	}
+	public static void addLocations(Block b1, Block b2, int idx, int[][] loc, Map<String,Integer> blockIdMap){
+		// a,b,c*$ a,b,-c*$ -a,b,-c*$ 
+		// first is c
+		// second is a
+		
+		if(b1.isInverted()){  // we'll get c_t
+			loc[blockIdMap.get(b1.getName())][0] = idx;
+		} else {                // we'll get c_h
+			loc[blockIdMap.get(b1.getName())][1] = idx;
+		}
+		if (b2.isInverted()){  // we'll get a_h
+			loc[blockIdMap.get(b2.getName())][1] = idx;
+		} else {                   // we'll get a_t
+			loc[blockIdMap.get(b2.getName())][0] = idx;
+		}
+	}
+
+	/**
+	 * 
+	 * @param chrom
+	 * @param adj
+	 * @param loc
+	 */
+	public static void addAdjacencies(Contig[] chrom, Adjacency[] adj, int[][] loc, Map<String,Integer> blockIdMap){
+		int adjIdx = 0;
+		for (int i = 0; i < chrom.length; i++){
+			Block[] blk = chrom[i].getBlocks();
+			if (blk.length == 0){
+				continue;
+			}
+			if (chrom[i].isCirc()){
+				addLocations(blk[blk.length-1], blk[0], adjIdx, loc, blockIdMap);
+				adj[adjIdx] = new Adjacency(blk[blk.length-1].getRightEnd(),blk[0].getLeftEnd());
+				adjIdx++;
+			} else {
+				// add left telomere location
+				if (blk[0].isInverted()){
+					loc[blockIdMap.get(blk[0].getName())][1] = adjIdx;
+				} else {
+					loc[blockIdMap.get(blk[0].getName())][0] = adjIdx;
+				}
+				adj[adjIdx] = new Adjacency(blk[0].getLeftEnd());
+				adjIdx++;
+			}
+			for (int j = 1; j < blk.length; j++){
+				addLocations(blk[j-1], blk[j], adjIdx, loc, blockIdMap);
+				adj[adjIdx] = new Adjacency(blk[j-1].getRightEnd(), blk[j].getLeftEnd());
+				adjIdx++;
+			}
+			if (!chrom[i].isCirc()){
+				//  add right telomere location
+				if (blk[blk.length-1].isInverted()){
+					loc[blockIdMap.get(blk[blk.length-1].getName())][0] = adjIdx;
+				} else {
+					loc[blockIdMap.get(blk[blk.length-1].getName())][1] = adjIdx;
+				}
+				adj[adjIdx] = new Adjacency(blk[blk.length-1].getRightEnd());
+				adjIdx++;
+			}
+		}
+	}
+
+	/**
+	 * Returns true if the two permutations have equal content, false otherwise.
+	 * 
+	 * complexity ~ O(max(|X|,|Y|))
+	 * 
+	 * @param X 
+	 * @param Y
+	 * @return true if (X\Y) U (Y\X) == empty set, false otherwise 
+	 */
+	public static boolean equalContents(String X, String Y){
+		String[] blkX = X.split("[$, ]+");
+		String[] blkY = Y.split("[$, ]+");
+//		if (blkY.length != blkX.length)
+//			return false;
+		HashSet<String> set = new HashSet<String>();
+		for (int i = 0; i < blkX.length; i++){
+			blkX[i] = blkX[i].trim();
+			if (blkX[i].length() == 0) // empty contig
+				continue;
+			if (blkX[i].startsWith("-")){
+				blkX[i] = blkX[i].substring(1);
+			}
+			set.add(blkX[i]);
+		}
+		for (int j = 0; j < blkY.length; j++){
+			blkY[j] = blkY[j].trim();
+			if (blkY[j].length() == 0) // empty contig
+				continue;
+			if (blkY[j].startsWith("-")){
+				blkY[j] = blkY[j].substring(1);
+			}
+			if (set.remove(blkY[j])){
+				continue;
+			} else {
+				return false;
+			}
+		}
+		if (set.isEmpty())
+			return true;
+		else 
+			return false;
+	}
+	
+	
+}
diff --git a/src/org/gel/mauve/dcjx/Tail.java b/src/org/gel/mauve/dcjx/Tail.java
new file mode 100644
index 0000000..6ac8431
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/Tail.java
@@ -0,0 +1,18 @@
+package org.gel.mauve.dcjx;
+
+public class Tail implements BlockEnd {
+
+	private String name;
+	
+//	private Block block;
+	
+	public Tail(Block blk){
+//		block = blk;
+		name = blk.getName() + BlockEnd.TAIL_TAG;
+	}
+	
+	public String getName() {
+		return name;
+	}
+
+}
diff --git a/src/org/gel/mauve/dcjx/VestigialDCJ.java b/src/org/gel/mauve/dcjx/VestigialDCJ.java
new file mode 100644
index 0000000..79b74a5
--- /dev/null
+++ b/src/org/gel/mauve/dcjx/VestigialDCJ.java
@@ -0,0 +1,96 @@
+package org.gel.mauve.dcjx;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+
+/**
+ * 
+ * @author atritt
+ * @deprecated 
+ */
+public class VestigialDCJ {
+
+	public static void main(String [] args)
+	{
+		
+		if (args.length != 1){
+			System.err.println("Usage: java -jar DCJ.jar <perm_file>\n"+
+					           "  where perm_file contains one line per genome");
+			System.exit(-1);
+		}
+		
+		Scanner in = null;
+		try {
+			in = new Scanner(new FileReader(args[0]));
+			
+		} catch (IOException e){
+			e.printStackTrace();
+			System.exit(-1);
+		}
+	
+		String genomeX = in.nextLine().trim();
+		String genomeY = in.nextLine().trim(); 
+
+		// make some toys
+	
+		genomeX = "a,b,c,d,e,f,g,h*$";        
+		genomeY = "a,c,-d,-g,-f,-e,h,b$";
+		
+		System.out.println(genomeX +"\n" +genomeY);
+		
+		System.out.flush();
+		
+//		Map<String,Integer> blockIdMap = new HashMap<String,Integer>();
+//		DCJ.loadBlockIDMap(genomeX, blockIdMap);
+//		DCJ.loadBlockIDMap(genomeY, blockIdMap);
+		DCJ dcj = new DCJ(genomeX, genomeY);
+//		Permutation x = new Permutation(genomeX, blockIdMap, "genomeX");
+//		Permutation y = new Permutation(genomeY, blockIdMap, "genomeY");
+//		System.out.println("Computing DCJ distance between " + x.getName() + " and " + y.getName());
+//		x.printDesc(System.out);
+//		y.printDesc(System.out);
+		
+		int N = dcj.numBlocks(); 
+			//blockIdMap.size();
+		System.out.println("Found N = " + N + " blocks");
+		
+/*	
+		Adjacency[] adj = x.getAdjacencies();
+		System.out.print("Adjacencies in X: {");
+		for (int i = 0; i < adj.length; i++){
+			System.out.print(adj[i].toString());
+			if (i<adj.length-1)
+				System.out.print(",");
+			else 
+				System.out.print("}\n");
+		}
+		adj = y.getAdjacencies();
+		System.out.print("Adjacencies in Y: {");
+		for (int i = 0; i < adj.length; i++){
+			System.out.print(adj[i].toString());
+			if (i<adj.length-1)
+				System.out.print(",");
+			else 
+				System.out.print("}\n");
+		}
+*/
+		System.out.print("Creating Adjacency graph...");
+		AdjacencyGraph agXY = dcj.getAdjacencyGraph();
+		System.out.println("done!");
+		int C = agXY.numCycles();
+		int I = agXY.numOddPaths();
+		System.out.println("Found C = " + C + " cycles");
+		System.out.println("Found I = " + I + " odd pathes");
+		int dcjDist = N - (C + I/2);
+		System.out.println("dcj(X,Y) = " + dcjDist);
+		System.out.println("dcj(X,Y) = " + dcj.dcjDistance());
+	}
+	
+
+
+	
+}
diff --git a/src/org/gel/mauve/format/BaseFormat.java b/src/org/gel/mauve/format/BaseFormat.java
new file mode 100644
index 0000000..d1a7a22
--- /dev/null
+++ b/src/org/gel/mauve/format/BaseFormat.java
@@ -0,0 +1,129 @@
+package org.gel.mauve.format;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.NoSuchElementException;
+
+import org.biojava.bio.BioException;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+import org.biojavax.bio.BioEntry;
+import org.biojavax.bio.seq.RichSequence;
+import org.biojavax.bio.seq.RichSequenceIterator;
+import org.gel.mauve.SupportedFormat;
+
+public abstract class BaseFormat implements SupportedFormat
+{
+    public void validate(Sequence s, File source, int index) throws FileNotFoundException
+    {
+        if (!source.exists())
+            throw new FileNotFoundException("File " + source + " not found.");
+    }
+
+    public Sequence makeDelegate(Sequence s, File source, int index) throws FileNotFoundException
+    {
+    	if(s instanceof RichSequence)
+    		return new RichDelegatingSequence(s, this, source, index);
+        return new DelegatingSequence(s, this, source, index);
+    }
+
+    public Sequence readInnerSequence(File source, int index)
+    {
+    	try
+        {
+        	return SequenceIteratorCache.getSequence (
+        			this, source, index);
+        }
+        catch (NoSuchElementException e)
+        {
+            // This will only happen when we try to create a Delegating sequence
+            // with a too-large index.
+            throw new RuntimeException("Unexpected exception.", e);
+        }
+    }
+
+    /**
+     * @param file
+     *            A file, which is expected to exist.
+     * @return
+     * 
+     * Produces an iterator that creates DelegateSequences while reading a
+     * sequence file.
+     */
+    public SequenceIterator makeIterator(final File file)
+    {
+    	if(isRich())
+    	{
+            return new RichSequenceIterator()
+            {
+                SequenceIterator inner = readFile(file);
+                int index = -1;
+
+                public boolean hasNext()
+                {
+                    return inner.hasNext();
+                }
+
+                public Sequence nextSequence() throws NoSuchElementException, BioException
+                {
+                	Sequence s = null;
+                	if(inner instanceof RichSequenceIterator)
+                		s = ((RichSequenceIterator)inner).nextRichSequence();
+                	else
+                		s = inner.nextSequence();
+                    index++;
+                    try
+                    {
+                        return makeDelegate(s, file, index);
+                    }
+                    catch (FileNotFoundException e)
+                    {
+                        // The file has already been verified above, so there must
+                        // be a weird problem.
+                        throw new Error("Index: " + index, e);
+                    }
+                }
+                public RichSequence nextRichSequence() throws NoSuchElementException, BioException
+                {
+                	return (RichSequence)nextSequence();
+                }
+                
+                public BioEntry nextBioEntry() throws BioException
+                {
+                	return ((RichSequenceIterator)inner).nextBioEntry();
+                }
+            };
+    	}
+        return new SequenceIterator()
+        {
+            SequenceIterator inner = readFile(file);
+            int index = -1;
+
+            public boolean hasNext()
+            {
+                return inner.hasNext();
+            }
+
+            public Sequence nextSequence() throws NoSuchElementException, BioException
+            {
+            	Sequence s = null;
+            	if(inner instanceof RichSequenceIterator)
+            		s = ((RichSequenceIterator)inner).nextRichSequence();
+            	else
+            		s = inner.nextSequence();
+                index++;
+                try
+                {
+                    return makeDelegate(s, file, index);
+                }
+                catch (FileNotFoundException e)
+                {
+                    // The file has already been verified above, so there must
+                    // be a weird problem.
+                    throw new Error("Index: " + index, e);
+                }
+            }
+        };
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/DelegatingSequence.java b/src/org/gel/mauve/format/DelegatingSequence.java
new file mode 100644
index 0000000..689cbc7
--- /dev/null
+++ b/src/org/gel/mauve/format/DelegatingSequence.java
@@ -0,0 +1,429 @@
+package org.gel.mauve.format;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.biojava.bio.Annotation;
+import org.biojava.bio.BioException;
+import org.biojava.bio.SimpleAnnotation;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SimpleFeatureHolder;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.seq.Feature.Template;
+import org.biojava.bio.seq.impl.SimpleStrandedFeature;
+import org.biojava.bio.symbol.Alphabet;
+import org.biojava.bio.symbol.Edit;
+import org.biojava.bio.symbol.IllegalAlphabetException;
+import org.biojava.bio.symbol.PackedSymbolListFactory;
+import org.biojava.bio.symbol.Symbol;
+import org.biojava.bio.symbol.SymbolList;
+import org.biojava.ontology.OntoTools;
+import org.biojava.utils.ChangeListener;
+import org.biojava.utils.ChangeType;
+import org.biojava.utils.ChangeVetoException;
+import org.biojava.utils.Changeable;
+import org.biojava.utils.Unchangeable;
+import org.biojavax.bio.seq.RichFeature;
+import org.gel.mauve.FilterCacheSpec;
+import org.gel.mauve.SupportedFormat;
+
+class DelegatingSequence implements Sequence {
+	protected File source;
+
+	protected int sequenceIndex;
+
+	protected Annotation annotation;
+
+	protected Alphabet alphabet;
+
+	protected int length;
+
+	protected String name;
+
+	protected String urn;
+
+	protected int featureCount;
+
+	protected Map filterCache = new HashMap ();
+
+	protected SupportedFormat format;
+	
+	protected boolean circular;
+
+	// Delegate changeable stuff to unchangeable implementation
+	Changeable ch = new Unchangeable ();
+
+	SymbolList packedList = null; // a packed symbol list
+
+	public DelegatingSequence (Sequence s, SupportedFormat format, File source,
+			int index) throws FileNotFoundException {
+		format.validate (s, source, index);
+
+		this.format = format;
+		this.source = source;
+		this.sequenceIndex = index;
+
+		init (s);
+	}
+
+	protected void init (Sequence s) {
+		PackedSymbolListFactory pslFactory = new PackedSymbolListFactory ();
+		Symbol [] symArray = new Symbol [s.length ()];
+		int symI = 0;
+		for (Iterator symIter = s.iterator (); symIter.hasNext ();) {
+			symArray[symI++] = (Symbol) symIter.next ();
+		}
+		try {
+        	packedList = pslFactory.makeSymbolList(symArray, s.length(), s.getAlphabet());
+        }catch(IllegalAlphabetException iae)
+        {
+			iae.printStackTrace ();
+		}
+
+		annotation = new SimpleAnnotation (s.getAnnotation ());
+		alphabet = s.getAlphabet ();
+		length = s.length ();
+		name = format.getChromosomeName (s);
+		//name = s.getName ();
+		urn = s.getURN ();
+		featureCount = s.countFeatures ();
+
+		for (int i = 0; i < format.getFilterCacheSpecs ().length; i++) {
+			addToCache (s, format.getFilterCacheSpecs ()[i]);
+		}
+
+		completeInit (s);
+	}
+
+	protected void completeInit (Sequence s) {
+		// Added for subclass convenience.
+	}
+
+	// /////////////////////////////////////////////////////////////////
+	// Begin Sequence-specific implementation
+	public String getName () {
+		return name;
+	}
+
+	public String getURN () {
+		return urn;
+	}
+
+	// End Sequence-specific implementation
+	// /////////////////////////////////////////////////////////////////
+
+	// /////////////////////////////////////////////////////////////////
+	// Begin ChangeListener implementation.
+	/**
+	 * @deprecated
+	 */
+//	@Deprecated
+	public void addChangeListener (ChangeListener cl) {
+		ch.addChangeListener (cl);
+	}
+
+	public void addChangeListener (ChangeListener cl, ChangeType ct) {
+		ch.addChangeListener (cl, ct);
+	}
+
+	/**
+	 * @deprecated
+	 */
+//	@Deprecated
+	public void removeChangeListener (ChangeListener cl) {
+		ch.removeChangeListener (cl);
+	}
+
+	public void removeChangeListener (ChangeListener cl, ChangeType ct) {
+		ch.removeChangeListener (cl, ct);
+	}
+
+	public boolean isUnchanging (ChangeType ct) {
+		return ch.isUnchanging (ct);
+	}
+
+	// End ChangeListener implementation.
+	// /////////////////////////////////////////////////////////////////
+
+	// /////////////////////////////////////////////////////////////////
+	// Begin Annotatable implementation.
+	public Annotation getAnnotation () {
+		return annotation;
+	}
+
+	// End Annotatable implementation.
+	// /////////////////////////////////////////////////////////////////
+
+	// /////////////////////////////////////////////////////////////////
+	// Begin SymbolList implementation.
+	public Alphabet getAlphabet () {
+		return packedList.getAlphabet ();
+	}
+
+	public int length () {
+		return length;
+	}
+
+	public Symbol symbolAt (int index) throws IndexOutOfBoundsException {
+		return packedList.symbolAt (index);
+	}
+
+	public List toList () {
+		return packedList.toList ();
+	}
+
+	public Iterator iterator () {
+		return packedList.iterator ();
+	}
+
+	public SymbolList subList (int start, int end)
+			throws IndexOutOfBoundsException {
+		return new DelegatingSublist (start, end);
+	}
+
+	public String seqString () {
+		return packedList.seqString ();
+	}
+
+	public String subStr (int start, int end) throws IndexOutOfBoundsException {
+		return packedList.subStr (start, end);
+	}
+
+	public void edit (Edit edit) throws IllegalAlphabetException,
+			ChangeVetoException {
+		throw new ChangeVetoException ();
+	}
+
+    public int countFeatures () {
+		return featureCount;
+	}
+
+	public Iterator features () {
+		Sequence s = format.readInnerSequence (source, sequenceIndex);
+		return s.features ();
+	}
+
+	public FeatureHolder filter (FeatureFilter fc, boolean recurse) {
+		FeatureHolder fh;
+		// if this is of the form And(SomeFilter,OverlapsLocation)
+		// used by TranslatedSequencePanels, see if we have
+		// data corresponding to SomeFilter in one of the cache entries
+		if (fc instanceof FeatureFilter.And
+				&& ((FeatureFilter.And) fc).getChild2 () instanceof FeatureFilter.OverlapsLocation) {
+			fh = getCachedFilterResults (((FeatureFilter.And) fc).getChild1 ());
+		} else {
+			fh = format.readInnerSequence (source, sequenceIndex);
+		}
+		return fh.filter (fc, recurse);
+	}
+
+	private FeatureHolder getCachedFilterResults (FeatureFilter ff) {
+		if (!filterCache.containsKey (ff)) {
+			Sequence s = format.readInnerSequence (source, sequenceIndex);
+			addToCache (s, new FilterCacheSpec (ff));
+		}
+
+		return (FeatureHolder) filterCache.get (ff);
+	}
+
+	private void addToCache (Sequence s, FilterCacheSpec filterCacheSpec) {
+		FeatureHolder results = s.filter (filterCacheSpec.filter, true);
+
+		// Save them.
+		SimpleFeatureHolder sfh = new SimpleFeatureHolder ();
+		Iterator i = results.features ();
+		while (i.hasNext ()) {
+			Feature f = (Feature) i.next ();
+			Feature sf = null;
+			if (f instanceof StrandedFeature)
+				sf = makeThinFeature ((StrandedFeature) f, filterCacheSpec);
+			else
+				System.err.println ("found one that ain't stranded");
+			try {
+				sfh.addFeature (sf);
+			} catch (ChangeVetoException e) {
+				throw new Error (e);
+			}
+		}
+		filterCache.put (filterCacheSpec.filter, sfh);
+	}
+	
+	protected SimpleStrandedFeature makeThinFeature (StrandedFeature f, FilterCacheSpec spec) {
+		return makeThinFeature (this, f, spec);
+	}
+
+	/**
+	 * @param f
+	 * @return
+	 * @throws ChangeVetoException
+	 * 
+	 * Make a slimmed-down copy of the feature for caching.
+	 * 
+	 */
+	protected static SimpleStrandedFeature makeThinFeature (Sequence sequence, 
+			StrandedFeature f, FilterCacheSpec spec) {
+		Feature.Template template = f.makeTemplate ();
+		if (f.getAnnotation () != null && spec.getAnnotations () != null
+				&& spec.getAnnotations ().length > 0) {
+			if (spec.getAnnotations ()[0]
+					.equals (FilterCacheSpec.ALL_ANNOTATIONS)) {
+				// Don't replace any annotations.
+			} else {
+				Annotation a = new SimpleAnnotation ();
+				for (int i = 0; i < spec.getAnnotations ().length; i++) {
+					String property = spec.getAnnotations ()[i];
+
+					if (f.getAnnotation ().containsProperty (property)) {
+						try {
+							a.setProperty (property, f.getAnnotation ()
+									.getProperty (property));
+						} catch (ChangeVetoException e) {
+							// We don't expect an exception here.
+							throw new RuntimeException (e);
+						}
+					}
+				}
+				template.annotation = a;
+			}
+		} else {
+			// Strip out everything except location and type
+			template.annotation = Annotation.EMPTY_ANNOTATION;
+		}
+		template.source = null;
+		template.sourceTerm = OntoTools.ANY;
+        if(template instanceof RichFeature.Template)
+        {
+        	SimpleStrandedFeature sf = new SimpleStrandedFeature(sequence, sequence, 
+        			new RichStrandedFeatureTemplate((RichFeature.Template)template));
+        	return sf;
+        }else{
+        	SimpleStrandedFeature sf = new SimpleStrandedFeature(sequence, sequence,
+        			(StrandedFeature.Template)template);
+        	return sf;
+        }
+	}
+
+	public FeatureHolder filter (FeatureFilter fc) {
+		FeatureHolder fh;
+		// if this is of the form And(SomeFilter,OverlapsLocation)
+		// used by TranslatedSequencePanels, see if we have
+		// data corresponding to SomeFilter in one of the cache entries
+		if (fc instanceof FeatureFilter.And
+				&& ((FeatureFilter.And) fc).getChild2 () instanceof FeatureFilter.OverlapsLocation) {
+			fh = getCachedFilterResults (((FeatureFilter.And) fc).getChild1 ());
+		} else if (fc instanceof FeatureFilter.ByType) {
+			fh = getCachedFilterResults (fc);
+		} else {
+			fh = format.readInnerSequence (source, sequenceIndex);
+		}
+		return fh.filter (fc);
+	}
+
+	public Feature createFeature (Template ft) throws BioException,
+			ChangeVetoException {
+		throw new ChangeVetoException ();
+	}
+
+	public void removeFeature (Feature f) throws ChangeVetoException,
+			BioException {
+		throw new ChangeVetoException ();
+	}
+
+	public boolean containsFeature (Feature f) {
+		Sequence s = format.readInnerSequence (source, sequenceIndex);
+		return s.containsFeature (f);
+	}
+
+	public FeatureFilter getSchema () {
+		Sequence s = format.readInnerSequence (source, sequenceIndex);
+		return s.getSchema ();
+	}
+
+	private class DelegatingSublist implements SymbolList {
+		private int start;
+
+		private int end;
+
+		public DelegatingSublist (int start, int end) {
+			this.start = start;
+			this.end = end;
+		}
+
+		public Alphabet getAlphabet () {
+			return alphabet;
+		}
+
+		public int length () {
+			return end - start + 1;
+		}
+
+		public Symbol symbolAt (int index) throws IndexOutOfBoundsException {
+			return DelegatingSequence.this.symbolAt (index + start - 1);
+		}
+
+		public List toList () {
+			throw new Error ("Not implemented");
+		}
+
+		public Iterator iterator () {
+			throw new Error ("Not implemented");
+		}
+
+		public SymbolList subList (int start, int end)
+				throws IndexOutOfBoundsException {
+			return new DelegatingSublist (this.start + start - 1, this.start
+					+ end - 1);
+		}
+
+		public String seqString () {
+			return packedList.subStr (start, end);
+			//throw new Error ("Not implemented");
+		}
+
+		public String subStr (int start, int end)
+				throws IndexOutOfBoundsException {
+			throw new Error ("Not implemented");
+		}
+
+		public void edit (Edit edit) throws IndexOutOfBoundsException,
+				IllegalAlphabetException, ChangeVetoException {
+			throw new ChangeVetoException ();
+		}
+
+		/**
+		 * @deprecated
+		 */
+//		@Deprecated
+		public void addChangeListener (ChangeListener cl) {
+			ch.addChangeListener (cl);
+		}
+
+		public void addChangeListener (ChangeListener cl, ChangeType ct) {
+			ch.addChangeListener (cl, ct);
+		}
+
+		/**
+		 * @deprecated
+		 */
+		public void removeChangeListener (ChangeListener cl) {
+			ch.removeChangeListener (cl);
+		}
+
+		public void removeChangeListener (ChangeListener cl, ChangeType ct) {
+			ch.removeChangeListener (cl, ct);
+		}
+
+		public boolean isUnchanging (ChangeType ct) {
+			return true;
+		}
+
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/EmblFormat.java b/src/org/gel/mauve/format/EmblFormat.java
new file mode 100644
index 0000000..82b8724
--- /dev/null
+++ b/src/org/gel/mauve/format/EmblFormat.java
@@ -0,0 +1,26 @@
+package org.gel.mauve.format;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+
+import org.biojava.bio.seq.SequenceIterator;
+import org.biojava.bio.seq.io.SeqIOTools;
+import org.biojavax.bio.seq.RichSequence;
+
+class EmblFormat extends GenbankEmblFormat {
+	public SequenceIterator readFile (File file) {
+		BufferedReader reader;
+		try {
+			reader = new BufferedReader (new FileReader (file));
+		} catch (FileNotFoundException e) {
+			// This exception is not expected, because file is required to
+			// exist.
+			throw new RuntimeException (e);
+		}
+		return SeqIOTools.readEmbl (reader);
+	}
+
+    public boolean isRich(){ return false; }
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/FastaFormat.java b/src/org/gel/mauve/format/FastaFormat.java
new file mode 100644
index 0000000..0004e9a
--- /dev/null
+++ b/src/org/gel/mauve/format/FastaFormat.java
@@ -0,0 +1,43 @@
+package org.gel.mauve.format;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+import org.biojava.bio.seq.io.SeqIOTools;
+import org.biojavax.bio.seq.RichSequence;
+import org.gel.mauve.FilterCacheSpec;
+
+public class FastaFormat extends BaseFormat {
+	public SequenceIterator readFile (File file) {
+		BufferedReader reader = openFile (file);
+		return SeqIOTools.readFastaDNA (reader);
+	}
+
+	protected BufferedReader openFile (File file) {
+		try {
+			return new BufferedReader (new FileReader (file));
+		} catch (FileNotFoundException e) {
+			// This exception is not expected, because file is required to
+			// exist.
+			throw new RuntimeException (e);
+		}
+	}
+
+	public String getSequenceName (Sequence s) {
+		return s.getName ();
+	}
+
+	public String getChromosomeName (Sequence s) {
+		return s.getName ();
+	}
+
+	public FilterCacheSpec [] getFilterCacheSpecs () {
+		return new FilterCacheSpec [0];
+	}
+
+    public boolean isRich(){ return false; }
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/FileFinder.java b/src/org/gel/mauve/format/FileFinder.java
new file mode 100644
index 0000000..713b0ff
--- /dev/null
+++ b/src/org/gel/mauve/format/FileFinder.java
@@ -0,0 +1,73 @@
+package org.gel.mauve.format;
+
+import java.io.File;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.SupportedFormat;
+
+public class FileFinder {
+
+    private static String windowsPathHack(String p)
+	{
+		// only do this under Mac OS X, which has an inept java.io.File
+		if(System.getProperty("os.name").indexOf("indow") > 0)
+			return p;
+		// only operate on paths with backslashes and drive letter specs
+		if(p.length() < 3 || !((p.charAt(1) == ':' && p.charAt(2) == '\\') || p.startsWith("\\\\")))
+			return p;
+
+		// replace all backslashes with forward slash and ditch the drive specifier 
+		if(p.charAt(1) == ':' && p.charAt(2) == '\\')
+			p = p.substring(2,p.length());
+		int indie = p.indexOf('\\');
+		int previa = 0;
+		StringBuilder sb = new StringBuilder();
+		while(indie >= 0)
+		{
+			if(previa < indie)
+			{
+				sb.append("/");	
+				sb.append(p.substring(previa,indie));
+			}
+			previa = indie+1;
+			indie = p.indexOf('\\', indie+1);
+		}
+		if(previa < p.length())
+		{
+			sb.append("/");
+			sb.append(p.substring(previa, p.length()));
+		}
+		return sb.toString();
+	}
+    public static String findFile(BaseViewerModel model, String filename)
+    {    	
+        File f = new File(filename);
+        // first try to read the file as given in the XMFA
+        if (f.canRead())
+            return filename;
+
+        // try stripping quote characters
+        String nameSansQuotes = org.gel.mauve.format.SupportedFormatFactory.trimWhiteAndQuotes(filename);
+
+        f = new File(nameSansQuotes);
+        if (f.canRead())
+            return nameSansQuotes;
+
+        // normalize windows path names in case we're not running Windows
+        nameSansQuotes = windowsPathHack(nameSansQuotes);
+
+        // try the directory of the source alignment with the full path
+        filename = model.getSrc().getParent() + File.separatorChar + nameSansQuotes;
+        f = new File(filename);
+        if (f.canRead())
+            return filename;
+
+        // otherwise try the directory of the source alignment with just the filename
+    	String path = "";
+    	if( nameSansQuotes.length() > 0 )
+    		path = model.getSrc().getParent() + File.separatorChar + f.getName();
+        return path;
+    }
+
+}
diff --git a/src/org/gel/mauve/format/GenbankEmblFormat.java b/src/org/gel/mauve/format/GenbankEmblFormat.java
new file mode 100644
index 0000000..d707f13
--- /dev/null
+++ b/src/org/gel/mauve/format/GenbankEmblFormat.java
@@ -0,0 +1,248 @@
+package org.gel.mauve.format;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.util.StringTokenizer;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.biojava.bio.Annotation;
+import org.biojava.bio.gui.sequence.RectangularBeadRenderer;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.utils.ChangeVetoException;
+import org.biojavax.Note;
+import org.biojavax.RichObjectFactory;
+import org.biojavax.bio.seq.RichFeature;
+import org.biojavax.bio.seq.RichSequence;
+import org.biojavax.bio.taxa.NCBITaxon;
+import org.biojavax.ontology.ComparableTerm;
+import org.gel.mauve.FilterCacheSpec;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.gui.navigation.AnnotationContainsFilter;
+import org.gel.mauve.gui.sequence.ZiggyRectangularBeadRenderer;
+
+public abstract class GenbankEmblFormat extends BaseFormat {
+	private static FilterCacheSpec [] specs = new FilterCacheSpec [11];
+	static {
+		RectangularBeadRenderer renderer = null;
+		ZiggyRectangularBeadRenderer zrenderer;
+		try {
+			zrenderer = new ZiggyRectangularBeadRenderer (10.0, 0.0,
+					Color.BLACK, Color.WHITE, new BasicStroke ());
+			specs[0] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("CDS"),
+					new FeatureFilter.StrandFilter (StrandedFeature.POSITIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					zrenderer);
+
+			renderer = new RectangularBeadRenderer (10.0, 0.0, Color.BLACK,
+					Color.RED, new BasicStroke ());
+			renderer.setHeightScaling (false);
+			specs[1] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("rRNA"),
+					new FeatureFilter.StrandFilter (StrandedFeature.POSITIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+
+			renderer = new RectangularBeadRenderer (10.0, 0.0, Color.BLACK,
+					Color.GREEN, new BasicStroke ());
+			renderer.setHeightScaling (false);
+			specs[2] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("tRNA"),
+					new FeatureFilter.StrandFilter (StrandedFeature.POSITIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+
+			renderer = new RectangularBeadRenderer (10.0, 0.0, Color.BLACK,
+					Color.BLUE, new BasicStroke ());
+			renderer.setHeightScaling (false);
+			specs[3] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("misc_RNA"),
+					new FeatureFilter.StrandFilter (StrandedFeature.POSITIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+
+			zrenderer = new ZiggyRectangularBeadRenderer (10.0, 10.0,
+					Color.BLACK, Color.WHITE, new BasicStroke ());
+			specs[4] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("CDS"),
+					new FeatureFilter.StrandFilter (StrandedFeature.NEGATIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					zrenderer);
+
+			renderer = new RectangularBeadRenderer (10.0, 10.0, Color.BLACK,
+					Color.RED, new BasicStroke ());
+			renderer.setHeightScaling (false);
+			specs[5] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("rRNA"),
+					new FeatureFilter.StrandFilter (StrandedFeature.NEGATIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+
+			renderer = new RectangularBeadRenderer (10.0, 10.0, Color.BLACK,
+					Color.GREEN, new BasicStroke ());
+			renderer.setHeightScaling (false);
+			specs[6] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("tRNA"),
+					new FeatureFilter.StrandFilter (StrandedFeature.NEGATIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+
+			renderer = new RectangularBeadRenderer (10.0, 10.0, Color.BLACK,
+					Color.BLUE, new BasicStroke ());
+			renderer.setHeightScaling (false);
+			specs[7] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("misc_RNA"),
+					new FeatureFilter.StrandFilter (StrandedFeature.NEGATIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+
+			// for repeats
+			renderer = new RectangularBeadRenderer (6.0, 26.0, Color.BLACK,
+					Color.PINK, new BasicStroke ());
+			renderer.setHeightScaling (false);
+			specs[8] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("repeat_region"),
+					new FeatureFilter.StrandFilter (StrandedFeature.NEGATIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+			renderer.setHeightScaling (false);
+			renderer = new RectangularBeadRenderer (6.0, 20.0, Color.BLACK,
+					Color.PINK, new BasicStroke ());
+			specs[9] = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType ("repeat_region"),
+					new FeatureFilter.StrandFilter (StrandedFeature.POSITIVE)),
+					new String [] {"gene", "locus_tag", "product", "db_xref"},
+					renderer);
+			renderer.setHeightScaling (false);
+			specs[10] = new FilterCacheSpec (
+					new FeatureFilter.ByType ("source"),
+					new String [] {FilterCacheSpec.ALL_ANNOTATIONS});
+		} catch (ChangeVetoException e) {
+			e.printStackTrace ();
+		}
+	}
+
+	public String getChromosomeName (Sequence s) {
+		FeatureHolder fh = s.filter(new FeatureFilter.ByType("source"));
+        String name = null;
+        Annotation a = null;
+        if (fh.countFeatures() != 0)
+        {
+            Feature f2 = (Feature) fh.features().next();
+            a = f2.getAnnotation();
+        }
+        if (a != null) {
+            if (a.containsProperty("chromosome"))
+            {
+                name = (String) a.getProperty("chromosome");
+            }
+            else if (a.containsProperty("biojavax:chromosome"))
+            {
+            	name = (String) a.getProperty("biojavax:chromosome");
+            }
+            else if (a.containsProperty("plasmid"))
+            {
+            	name = (String) a.getProperty("plasmid");
+            }
+            else if (a.containsProperty("biojavax:plasmid"))
+            {
+            	name = (String) a.getProperty("biojavax:plasmid");
+            }
+		}
+        if (name != null) {
+        	name = name.trim();
+        	if (name.length() == 0)
+        		name = null;
+        }
+        if (name == null && s.getAnnotation() != null) {
+        	name = (String) s.getName();
+        }
+        if (name == null && 
+        		AnnotationContainsFilter.getKeyIgnoreCase ("definition",
+				s.getAnnotation()) != null) {
+			name = getChromNameFromDescription (s);
+		}
+		return name;
+	}
+
+	public String getChromNameFromDescription (Sequence seq) {
+		String desc = ((String) AnnotationContainsFilter.getValueIgnoreCase (
+				"definition", seq.getAnnotation ()));
+        if(desc == null) {
+            return null;
+        } else {
+            desc = desc.toLowerCase();
+        }
+
+		int ind = desc.indexOf ("contig");
+		if (ind > -1) {
+			ind = desc.lastIndexOf (" ", ind);
+			if (ind < 0)
+				ind = 0;
+			int ind2 = desc.indexOf (" ", ind + 1);
+			if (ind2 < 0)
+				ind2 = desc.length ();
+			desc = desc.substring (ind, ind2);
+		}
+		else if (desc.indexOf("chromosome") > 0)
+			desc = "chromosome";
+		else if (desc.indexOf("plasmid") > 0)
+			desc = "plasmid";
+		/*else {
+			StringTokenizer toke = new StringTokenizer (getSequenceName (seq));
+			while (toke.hasMoreTokens ()) {
+				String [] pieces = desc.split (toke.nextToken ().toLowerCase ());
+				desc = "";
+				for (int i = 0; i < pieces.length; i++)
+					desc += pieces[i].trim () + " ";
+				desc = desc.trim ();
+			}
+		}*/
+		return desc;
+	}
+
+	public String getSequenceName (Sequence s) {
+		FeatureHolder fh = s.filter (new FeatureFilter.ByType ("source"));
+		if (fh.countFeatures () != 0) {
+			Feature f2 = (Feature) fh.features ().next ();
+			Annotation a = f2.getAnnotation ();
+			String name = "";
+			String add = null;
+			if (a.containsProperty ("organism")) {
+				name += a.getProperty ("organism") + " ";
+			}
+			if (a.containsProperty ("serovar")) {
+				add = a.getProperty ("serovar").toString ();
+				if (name.indexOf (add) == -1)
+					name += add + " ";
+			}
+			if (a.containsProperty ("strain")) {
+				add = a.getProperty ("strain").toString ();
+				if (name.indexOf (add) == -1)
+					name += add + " ";
+			}
+			if (name != "")
+				return name.trim ();
+		}
+		// if a source feature didn't exist
+		// try getting the source from the headers
+		try {
+			Object source = s.getAnnotation ().getProperty ("SOURCE");
+			if (source != null) {
+				return (String) source;
+			}
+		} catch (Exception e) {
+		}
+		return null;
+	}
+
+	public FilterCacheSpec [] getFilterCacheSpecs () {
+		return specs;
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/GenbankFileFormat.java b/src/org/gel/mauve/format/GenbankFileFormat.java
new file mode 100644
index 0000000..9a283e0
--- /dev/null
+++ b/src/org/gel/mauve/format/GenbankFileFormat.java
@@ -0,0 +1,28 @@
+package org.gel.mauve.format;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+
+import org.biojava.bio.seq.SequenceIterator;
+import org.biojava.bio.seq.io.SeqIOTools;
+import org.biojavax.bio.seq.RichSequence;
+
+class GenbankFileFormat extends GenbankEmblFormat {
+	public SequenceIterator readFile (File file) {
+		BufferedReader reader;
+		try {
+			reader = new BufferedReader (new FileReader (file));
+		} catch (FileNotFoundException e) {
+			// This exception is not expected, because file is required to
+			// exist.
+			throw new RuntimeException (e);
+		}
+		
+		return SeqIOTools.readGenbank (reader);
+//		return RichSequence.IOTools.readGenbankDNA(reader, null);
+	}
+
+	public boolean isRich(){ return false; }
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/INSDseqFormat.java b/src/org/gel/mauve/format/INSDseqFormat.java
new file mode 100644
index 0000000..c221ca7
--- /dev/null
+++ b/src/org/gel/mauve/format/INSDseqFormat.java
@@ -0,0 +1,33 @@
+package org.gel.mauve.format;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+import org.biojavax.SimpleNamespace;
+import org.biojavax.bio.seq.RichSequence;
+import org.gel.mauve.FilterCacheSpec;
+
+public class INSDseqFormat extends GenbankEmblFormat {
+
+	public SequenceIterator readFile(File file) {
+        BufferedReader reader;
+        try
+        {
+            reader = new BufferedReader(new FileReader(file));
+        }
+        catch (FileNotFoundException e)
+        {
+            // This exception is not expected, because file is required to
+            // exist.
+            throw new RuntimeException(e);
+        }
+        SimpleNamespace namesp = new SimpleNamespace(file.getName());
+        return RichSequence.IOTools.readINSDseqDNA(reader, namesp);
+	}
+
+    public boolean isRich(){ return true; }
+}
diff --git a/src/org/gel/mauve/format/RawFastaBridgeFilterReader.java b/src/org/gel/mauve/format/RawFastaBridgeFilterReader.java
new file mode 100644
index 0000000..8ad332a
--- /dev/null
+++ b/src/org/gel/mauve/format/RawFastaBridgeFilterReader.java
@@ -0,0 +1,100 @@
+package org.gel.mauve.format;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+
+public class RawFastaBridgeFilterReader extends FilterReader {
+	private final static String HEADER = ">raw\n";
+
+	private int remainingHeader = HEADER.length ();
+
+	protected RawFastaBridgeFilterReader (Reader in) {
+		super (in);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.Reader#read()
+	 */
+	public int read () throws IOException {
+		if (remainingHeader > 0) {
+			char c = HEADER.charAt (HEADER.length () - remainingHeader);
+			remainingHeader--;
+			return c;
+		} else {
+			return super.read ();
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.Reader#read(char[], int, int)
+	 */
+	public int read (char [] cbuf, int off, int len) throws IOException {
+		if (remainingHeader > 0) {
+			int counter = 0;
+
+			while (remainingHeader > 0) {
+				cbuf[off] = (char) read ();
+				off++;
+				len--;
+				counter++;
+			}
+			return counter + super.read (cbuf, off, len);
+		} else {
+			return super.read (cbuf, off, len);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.Reader#skip(long)
+	 */
+	public long skip (long n) throws IOException {
+		if (remainingHeader > 0) {
+			if (n <= remainingHeader) {
+				remainingHeader = remainingHeader - (int) n;
+				return n;
+			} else {
+				int oldRemaining = remainingHeader;
+				remainingHeader = 0;
+				n = n - oldRemaining;
+				return super.skip (n) + oldRemaining;
+			}
+
+		} else {
+			return super.skip (n);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.Reader#reset()
+	 */
+	public void reset () throws IOException {
+		throw new IOException ("Reset not supported.");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.Reader#mark(int)
+	 */
+	public void mark (int readAheadLimit) throws IOException {
+		throw new IOException ("Mark not supported");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.Reader#markSupported()
+	 */
+	public boolean markSupported () {
+		return false;
+	}
+}
diff --git a/src/org/gel/mauve/format/RawFormat.java b/src/org/gel/mauve/format/RawFormat.java
new file mode 100644
index 0000000..2e44a33
--- /dev/null
+++ b/src/org/gel/mauve/format/RawFormat.java
@@ -0,0 +1,26 @@
+package org.gel.mauve.format;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+
+class RawFormat extends FastaFormat {
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.gel.mauve.format.FastaFormat#openFile(java.io.File)
+	 */
+	protected BufferedReader openFile (File file) {
+		try {
+			return new BufferedReader (new RawFastaBridgeFilterReader (
+					new FileReader (file)));
+		} catch (FileNotFoundException e) {
+			// This exception is not expected, because file is required to
+			// exist.
+			throw new RuntimeException (e);
+		}
+	}
+	
+	 public boolean isRich(){ return false; }
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/RichDelegatingSequence.java b/src/org/gel/mauve/format/RichDelegatingSequence.java
new file mode 100644
index 0000000..52b9173
--- /dev/null
+++ b/src/org/gel/mauve/format/RichDelegatingSequence.java
@@ -0,0 +1,171 @@
+package org.gel.mauve.format;
+
+import java.io.File;
+
+import java.io.FileNotFoundException;
+import java.util.Set;
+
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.symbol.SymbolList;
+import org.biojava.utils.ChangeVetoException;
+import org.biojavax.Comment;
+import org.biojavax.Namespace;
+import org.biojavax.RankedCrossRef;
+import org.biojavax.RankedDocRef;
+import org.biojavax.RichAnnotation;
+import org.biojavax.bio.BioEntryRelationship;
+import org.biojavax.bio.seq.RichSequence;
+import org.biojavax.bio.taxa.NCBITaxon;
+import org.gel.mauve.SupportedFormat;
+
+public class RichDelegatingSequence extends DelegatingSequence implements
+		RichSequence {
+	
+    public RichDelegatingSequence(Sequence s, SupportedFormat format, File source, int index) throws FileNotFoundException
+    {
+    	super(s,format,source,index);
+    	richInit(s);
+    }
+    
+    void richInit(Sequence s)
+    {
+    	RichSequence rs = (RichSequence)s;
+    	featureSet = rs.getFeatureSet();
+    	seqVersion = rs.getSeqVersion();
+    	intSymList = rs.getInternalSymbolList();
+    	accession = rs.getAccession();
+    	comments = rs.getComments();
+    	description = rs.getDescription();
+    	division = rs.getDivision();
+    	identifier = rs.getIdentifier();
+    	bioname = rs.getName();
+    	ns = rs.getNamespace();
+    	rankedDocRefs = rs.getRankedDocRefs();
+    	relationships = rs.getRelationships();
+    	taxon = rs.getTaxon();
+    	bioVersion = rs.getVersion();
+    	noteSet = rs.getNoteSet();
+    	rankedCrossRefs = rs.getRankedCrossRefs();
+    	circular = rs.getCircular();
+    }
+
+    Set featureSet = null;
+    Double seqVersion = null;
+    SymbolList intSymList = null;
+    
+    //  RichSequence methods
+	public Double getSeqVersion(){
+		return seqVersion;
+	}
+	public void setSeqVersion(Double version) throws ChangeVetoException{
+        throw new ChangeVetoException();
+	}
+	public Set getFeatureSet(){
+		return featureSet;
+	}
+	public void setFeatureSet(Set s) throws ChangeVetoException{
+        throw new ChangeVetoException();
+	}
+	public void setCircular(boolean circular) throws ChangeVetoException{
+        throw new ChangeVetoException();
+	}
+	public boolean getCircular(){
+		return circular;
+	}
+	public SymbolList getInternalSymbolList(){
+		return intSymList;
+	}
+	
+
+	// BioEntry methods
+	public void addComment(Comment comment) throws ChangeVetoException {
+        throw new ChangeVetoException();
+	}
+	public void addRankedDocRef(RankedDocRef docref) throws ChangeVetoException {        
+		throw new ChangeVetoException();
+	}
+	public void addRelationship(BioEntryRelationship relation) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	String accession = null;
+	Set comments = null;
+	String description = null;
+	String division = null;
+	String identifier = null;
+	String bioname = null;
+	Namespace ns = null;
+	Set rankedDocRefs = null;
+	Set relationships = null;
+	NCBITaxon taxon = null;
+	int bioVersion = 0;
+
+	public String getAccession(){return accession;}
+	public Set getComments(){return comments;}
+	public String getDescription(){return description;}
+	public String getDivision(){return division;}
+	public String getIdentifier(){return identifier;}
+	public String getName(){return bioname;}
+	public Namespace getNamespace(){return ns;}
+	public Set getRankedDocRefs(){return rankedDocRefs;}
+	public Set getRelationships(){return relationships;}
+	public NCBITaxon getTaxon(){return taxon;}
+	public int getVersion(){return bioVersion;}
+	public void removeComment(Comment comment) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	public void removeRankedDocRef(RankedDocRef docref) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	public void removeRelationship(BioEntryRelationship relation) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	public void setDescription(String description) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	public void setDivision(String division) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	public void setIdentifier(String identifier) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	public void setTaxon(NCBITaxon taxon) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+
+	// comparable methods
+	public int compareTo(Object o){
+		return -1;
+	}
+	
+	// RichAnnotatable methods
+	Set noteSet = null;
+	public Set getNoteSet(){ return noteSet; }
+	public void setNoteSet(Set notes) throws ChangeVetoException {
+		throw new ChangeVetoException();
+	}
+	
+	// RankedCrossRefable methods
+	Set rankedCrossRefs = null;
+	public Set getRankedCrossRefs()
+    {
+		return rankedCrossRefs;
+    }
+	public void setRankedCrossRefs(Set refs) throws ChangeVetoException
+    {
+		throw new ChangeVetoException();
+    }
+	public void addRankedCrossRef(RankedCrossRef crossref) throws ChangeVetoException
+    {		
+		throw new ChangeVetoException();
+    }
+	public void removeRankedCrossRef(RankedCrossRef crossref) throws ChangeVetoException
+    {		
+		throw new ChangeVetoException();
+    }
+
+	public RichAnnotation getRichAnnotation() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+}
diff --git a/src/org/gel/mauve/format/RichStrandedFeatureTemplate.java b/src/org/gel/mauve/format/RichStrandedFeatureTemplate.java
new file mode 100644
index 0000000..a17707a
--- /dev/null
+++ b/src/org/gel/mauve/format/RichStrandedFeatureTemplate.java
@@ -0,0 +1,28 @@
+/**
+ * 
+ */
+package org.gel.mauve.format;
+
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojavax.bio.seq.RichFeature;
+import org.biojavax.bio.seq.RichLocation;
+
+class RichStrandedFeatureTemplate extends StrandedFeature.Template
+{
+	static final long serialVersionUID = 243583828;
+	RichStrandedFeatureTemplate(RichFeature.Template t)
+	{
+		this.annotation = t.annotation;
+		this.location = t.location;
+		this.source = t.source;
+		this.sourceTerm = t.sourceTerm;
+		this.type = t.type;
+		this.typeTerm = t.typeTerm;
+		if( ((RichLocation)t.location).getStrand().intValue() == -1 )
+			strand = StrandedFeature.NEGATIVE;
+		else if( ((RichLocation)t.location).getStrand().intValue() == 1 )
+			strand = StrandedFeature.POSITIVE;
+		else
+			strand = StrandedFeature.UNKNOWN;
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/SequenceIteratorCache.java b/src/org/gel/mauve/format/SequenceIteratorCache.java
new file mode 100644
index 0000000..f84f809
--- /dev/null
+++ b/src/org/gel/mauve/format/SequenceIteratorCache.java
@@ -0,0 +1,116 @@
+package org.gel.mauve.format;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.EventListener;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javax.swing.Timer;
+
+import org.biojava.bio.BioException;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+import org.biojavax.bio.seq.RichSequenceIterator;
+
+/**
+ * Acts as a cache to allow for faster access to files with multiple contigs without
+ * creating a permanent memory commitment.  Necessary due to biojava's interaction with
+ * the BaseFormat class.
+ * 
+ * @author Anna I Rissman
+ *
+ */
+public class SequenceIteratorCache {
+	
+	/**
+	 * contains file names mapped to arrays of data required for cache
+	 */
+	protected static Hashtable cache = new Hashtable ();
+	
+	/**
+	 * Integers that represent array indexes of different required information.
+	 * Useful for accessing info in the arrays returned from the cache.
+	 */
+	public final static int ITERATOR_INDEX = 0;
+	public final static int INDEX_INDEX = 1;
+	
+	/**
+	 * Represents how much time will pass between attempts to clean up cache
+	 */
+	public final static int CLEAN_DELAY = 120000;
+	/**
+	 * Allows for clean up when a sequence iterator has no more sequences
+	 *  left to iterate through
+	 */
+	public final static Timer CLEAN_UP_TIMER = new Timer (CLEAN_DELAY, null);
+	
+	/**
+	 * Initializes the clean up timer by adding a listener and starting the timer
+	 */
+	static {
+		CLEAN_UP_TIMER.addActionListener (new ActionListener () {
+			public void actionPerformed (ActionEvent e) {
+				cleanCache ();
+			}
+		});
+		CLEAN_UP_TIMER.start();
+	}
+	
+	/**
+	 * Returns an iterator already iterated to the appropriate index.
+	 * 
+	 * @param format	The format the file to parse is in
+	 * @param source	The file containing the information from which to
+	 * 					 construct the iterator
+	 * @param index		The index of the desired sequence.
+	 * @return			A SequenceIterator that will give the desired Sequence with one
+	 * 					 nextSequence () call.
+	 */
+	public static Sequence getSequence (BaseFormat format, final File source, int index) {
+		Object [] array = null;
+		if (cache.containsKey (source)) {
+			array = (Object []) cache.get (source);
+			if (((Integer) array [INDEX_INDEX]).intValue () > index)
+				array = null;
+		}
+		if (array == null) {
+			array = new Object [2];
+			array [ITERATOR_INDEX] = format.readFile(source);
+			array [INDEX_INDEX] = new Integer (0);
+			cache.put (source, array);
+		}
+		try {
+			for (int i = ((Integer) array [INDEX_INDEX]).intValue (); i < index; i++)
+				((SequenceIterator) array [ITERATOR_INDEX]).nextSequence ();
+			array [INDEX_INDEX] = new Integer (index + 1);
+			if(array [ITERATOR_INDEX] instanceof RichSequenceIterator)
+				return ((RichSequenceIterator) array [ITERATOR_INDEX]).nextRichSequence();
+			return ((SequenceIterator) array [ITERATOR_INDEX]).nextSequence();
+		}
+		catch (Exception e) {
+			e.printStackTrace ();
+            throw new Error("Unexpected exception.", e);
+		}
+	}
+	
+	/**
+	 * Looks through cache hashtable for any iterators that have no more sequences
+	 * and therefore are no longer useful and removes them from the cache.
+	 */
+	public static void cleanCache () {
+		synchronized (cache) {
+			Iterator keys = cache.keySet ().iterator();
+			while (keys.hasNext()) {
+				Object key = keys.next();
+				if (!((SequenceIterator) ((Object []) cache.get(key))
+						[ITERATOR_INDEX]).hasNext()) {
+					keys.remove();
+//					System.out.println ("removed! " + cache.get(key));
+				}
+			}
+		}
+	}
+	
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/SupportedFormatFactory.java b/src/org/gel/mauve/format/SupportedFormatFactory.java
new file mode 100644
index 0000000..9c90b00
--- /dev/null
+++ b/src/org/gel/mauve/format/SupportedFormatFactory.java
@@ -0,0 +1,83 @@
+package org.gel.mauve.format;
+
+import java.io.File;
+
+import org.gel.mauve.SupportedFormat;
+
+public class SupportedFormatFactory
+{
+    final static SupportedFormat GENBANK = new GenbankFileFormat();
+    final static SupportedFormat EMBL = new EmblFormat();
+    final static SupportedFormat FASTA = new FastaFormat();
+    final static SupportedFormat RAW = new RawFormat();
+    final static SupportedFormat INSDSEQ = new INSDseqFormat();
+
+    public static SupportedFormat guessFormatFromFilename(String filename)
+    {
+        File f = new File(trimWhiteAndQuotes(filename));
+        String name = f.getName().toLowerCase();
+
+        if (name.endsWith(".gbk"))
+        {
+            return SupportedFormatFactory.GENBANK;
+        }
+        else if (name.endsWith(".raw"))
+        {
+            return SupportedFormatFactory.RAW;
+        }
+        else if (name.endsWith(".seq"))
+        {
+            return SupportedFormatFactory.GENBANK;
+        }
+        else if (name.endsWith(".embl"))
+        {
+            return SupportedFormatFactory.EMBL;
+        }
+        else if (name.endsWith(".xml"))
+        {
+            return SupportedFormatFactory.INSDSEQ;
+        }
+        else
+        {
+            return SupportedFormatFactory.FASTA;
+        }
+    }
+
+    public static SupportedFormat formatNameToFormat(String name)
+    {
+        if (name.equals("GenBank"))
+        {
+            return SupportedFormatFactory.GENBANK;
+        }
+        else if (name.equals("FastA"))
+        {
+            return SupportedFormatFactory.FASTA;
+        }
+        else if (name.equals("Raw"))
+        {
+            return SupportedFormatFactory.RAW;
+        }
+        else if (name.equals("EMBL"))
+        {
+            return SupportedFormatFactory.EMBL;
+        }
+        else if (name.equals("INSDseq"))
+        {
+            return SupportedFormatFactory.INSDSEQ;
+        }
+
+        throw new RuntimeException("Unexpected format: " + name);
+    }
+
+    public static String trimWhiteAndQuotes(String path)
+    {
+        // try stripping quote characters
+        String nameSansQuotes = new String(path);
+        nameSansQuotes = nameSansQuotes.trim();
+        if(nameSansQuotes.length() > 1 &&
+        		nameSansQuotes.charAt(0) == '"' && 
+        		nameSansQuotes.charAt(nameSansQuotes.length()-1) == '"')
+            nameSansQuotes = nameSansQuotes.substring(1, nameSansQuotes.length()-1);
+        return nameSansQuotes;
+    }
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/format/TestMain.java b/src/org/gel/mauve/format/TestMain.java
new file mode 100644
index 0000000..8c11020
--- /dev/null
+++ b/src/org/gel/mauve/format/TestMain.java
@@ -0,0 +1,12 @@
+package org.gel.mauve.format;
+
+import javax.imageio.ImageIO;
+
+public class TestMain {
+	public static void main (String [] args) {
+		String [] fmts = ImageIO.getReaderFormatNames ();
+		for (int i = 0; i < fmts.length; i++) {
+			System.out.println (fmts[i]);
+		}
+	}
+}
diff --git a/src/org/gel/mauve/gaggle/SampleGoose.java b/src/org/gel/mauve/gaggle/SampleGoose.java
new file mode 100644
index 0000000..78f58a0
--- /dev/null
+++ b/src/org/gel/mauve/gaggle/SampleGoose.java
@@ -0,0 +1,696 @@
+package org.gel.mauve.gaggle;
+// SampleGoose.java
+//------------------------------------------------------------------------------
+// $Revision: 503 $   
+// $Date: 2005/04/03 19:15:04 $
+//-------------------------------------------------------------------------------------
+/*
+ * Copyright (C) 2006 by Institute for Systems Biology,
+ * Seattle, Washington, USA.  All rights reserved.
+ *
+ * This source code is distributed under the GNU Lesser
+ * General Public License, the text of which is available at:
+ *   http://www.gnu.org/copyleft/lesser.html
+ */
+
+// todo - this is the canonical goose - remove warnings
+
+
+import java.rmi.*;
+
+import org.systemsbiology.gaggle.core.datatypes.DataMatrix;
+import org.systemsbiology.gaggle.core.Boss;
+import org.systemsbiology.gaggle.core.Goose;
+import org.systemsbiology.gaggle.core.datatypes.*;
+import org.systemsbiology.gaggle.core.datatypes.Interaction;
+import org.systemsbiology.gaggle.util.MiscUtil;
+import org.systemsbiology.gaggle.geese.common.RmiGaggleConnector;
+import org.systemsbiology.gaggle.geese.common.GooseShutdownHook;
+import org.systemsbiology.gaggle.geese.common.GaggleConnectionListener;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+/**
+ * A goose useful for testing and diagnostics and learning how to write a goose
+ * that uses the new (2007-04) API. 
+ */
+public class SampleGoose extends JFrame implements Goose, GaggleConnectionListener,
+        WindowListener {
+    String myGaggleName = "Sample";
+    String[] activeGooseNames;
+    Boss boss;
+    RmiGaggleConnector connector = new RmiGaggleConnector(this);
+    protected JScrollPane scrollPane;
+    protected JTextArea textArea;
+    JComboBox gooseChooser;
+    String targetGoose = "Boss";
+
+    JButton connectButton;
+    JButton disconnectButton;
+
+    //-------------------------------------------------------------------------------------
+    public SampleGoose() {
+        super("Sample");
+        addWindowListener(this);
+        new GooseShutdownHook(connector);
+
+
+
+        try {
+            connectToGaggle();
+        }
+        catch (Exception ex0) {
+            System.err.println("SampleGoose failed to connect to gaggle: " + ex0.getMessage());
+        }
+        add(createGui());
+
+        connector.addListener(this);
+
+        setSize(500, 500);
+        MiscUtil.placeInCenter(this);
+        setVisible(true);
+
+    }
+
+    //-------------------------------------------------------------------------------------
+    JPanel createGui() {
+        ToolTipManager.sharedInstance().setInitialDelay(0);
+        setLayout(new BorderLayout());
+        JPanel mainPanel = new JPanel();
+        mainPanel.setLayout(new BorderLayout());
+        JPanel controlPanel = new JPanel();
+        JToolBar toolbar = new JToolBar();
+        controlPanel.add(toolbar);
+        toolbar.setFloatable(false);
+
+        mainPanel.add(controlPanel, BorderLayout.NORTH);
+        MiscUtil.setApplicationIcon(this);
+
+
+
+        gooseChooser = new JComboBox(new String[]{"Boss"});
+        gooseChooser.setPrototypeDisplayValue("a very very long goose name");
+        gooseChooser.setToolTipText("Specify goose for broadcast");
+        toolbar.add(gooseChooser);
+
+
+        gooseChooser.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                JComboBox cb = (JComboBox) e.getSource();
+                int gooseChooserIndex = cb.getSelectedIndex();
+                System.out.println("choose goose index: " + gooseChooserIndex);
+                targetGoose = (String) cb.getSelectedItem();
+                System.out.println("target: " + targetGoose);
+            }
+        });
+
+        JButton showGooseButton = new JButton("S");
+        JButton hideGooseButton = new JButton("H");
+
+        // broadcast small & simple versions of each data type
+        JButton broadcastListButton = new JButton("B");
+        JButton broadcastMatrixButton = new JButton("M");
+        JButton broadcastNetworkButton = new JButton("N");
+        JButton broadcastHashButton = new JButton("T");
+        JButton broadcastClusterButton = new JButton("C");
+
+        showGooseButton.setToolTipText("Show selected goose");
+        hideGooseButton.setToolTipText("Hide selected goose");
+        broadcastListButton.setToolTipText("Broadcast sample name list");
+        broadcastMatrixButton.setToolTipText("Broadcast sample matrix");
+        broadcastNetworkButton.setToolTipText("Broadcast sample network");
+        broadcastHashButton.setToolTipText("Broadcast sample Tuple (lists of data and metadata)");
+        broadcastClusterButton.setToolTipText("Broadcast cluster: selected row and column names");
+
+        showGooseButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    boss.show(targetGoose);
+                }
+                catch (Exception ex2) {
+                    ex2.printStackTrace();
+                }
+            }
+        });
+
+        hideGooseButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    boss.hide(targetGoose);
+                }
+                catch (Exception ex2) {
+                    ex2.printStackTrace();
+                }
+            }
+        });
+
+        broadcastListButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    broadcastSampleList();
+                }
+                catch (Exception ex2) {
+                    ex2.printStackTrace();
+                }
+            }
+        });
+
+        broadcastMatrixButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                broadcastSampleMatrix();
+            }
+        });
+
+        broadcastNetworkButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    broadcastSampleNetwork();
+                }
+                catch (Exception ex2) {
+                    ex2.printStackTrace();
+                }
+            }
+        });
+
+        broadcastHashButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    broadcastSampleTuple();
+                }
+                catch (Exception ex2) {
+                    ex2.printStackTrace();
+                }
+            }
+        });
+
+        broadcastClusterButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    broadcastSampleCluster();
+                }
+                catch (Exception ex2) {
+                    ex2.printStackTrace();
+                }
+            }
+        });
+
+        toolbar.add(showGooseButton);
+        toolbar.add(hideGooseButton);
+        toolbar.add(broadcastListButton);
+        toolbar.add(broadcastMatrixButton);
+        toolbar.add(broadcastNetworkButton);
+        toolbar.add(broadcastHashButton);
+        toolbar.add(broadcastClusterButton);
+
+        JPanel searchPanel = new JPanel();
+        textArea = new JTextArea();
+        JScrollPane scrollPane = new JScrollPane(textArea);
+        mainPanel.setBorder(createBorder());
+        mainPanel.add(scrollPane, BorderLayout.CENTER);
+
+        JPanel buttonPanel = new JPanel();
+
+        connectButton = new JButton("Connect");
+        connectButton.setToolTipText("Connect to Boss");
+        connectButton.setEnabled(!connector.isConnected());
+        connectButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent actionEvent) {
+                try {
+                    connector.connectToGaggle();
+                    connectButton.setEnabled(false);
+                    disconnectButton.setEnabled(true);
+                } catch (Exception e) {
+                    JOptionPane.showMessageDialog(SampleGoose.this,
+                            "Couldn't connect to boss, is the boss running?");
+                    e.printStackTrace();
+                }
+            }
+        });
+
+
+        disconnectButton = new JButton("Disconnect");
+        disconnectButton.setToolTipText("Disconnect from Boss");
+        disconnectButton.setEnabled(connector.isConnected());
+        disconnectButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent actionEvent) {
+                connector.disconnectFromGaggle(false);
+                connectButton.setEnabled(true);
+                disconnectButton.setEnabled(false);
+            }
+        });
+
+
+        JButton clearButton = new JButton("Clear");
+        clearButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                textArea.setText("");
+            }
+        });
+
+
+        JButton exitButton = new JButton("Quit");
+        exitButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                doExit();
+            }
+        });
+
+        buttonPanel.add(connectButton);
+        buttonPanel.add(disconnectButton);
+        buttonPanel.add(clearButton);
+        buttonPanel.add(exitButton);
+
+        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+        MiscUtil.updateGooseChooser(gooseChooser, myGaggleName, activeGooseNames);
+
+        return mainPanel;
+
+    } // createGui
+
+
+    //-----------------------------------------------------------------------------------
+    private Border createBorder() {
+        int right = 10;
+        int left = 10;
+        int top = 10;
+        int bottom = 10;
+        return new EmptyBorder(top, left, bottom, right);
+    }
+
+    //-------------------------------------------------------------------------------
+    public void connectToGaggle() {
+        try {
+            connector.connectToGaggle();
+        }
+        catch (Exception ex0) {
+            System.err.println("failed to connect to gaggle: " + ex0.getMessage());
+            ex0.printStackTrace();
+        }
+        boss = connector.getBoss();
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void handleNameList(String source, Namelist nameList) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" >>> handleNameList, name = " + nameList.getName() +
+                ", length " + nameList.getNames().length + ", source = " + source + "\n");
+        sb.append("  species: " + nameList.getSpecies() + "\n");
+
+        int max = 5;
+        if (nameList.getNames().length < max)
+            max = nameList.getNames().length;
+        for (int i = 0; i < max; i++)
+            sb.append("   " + nameList.getNames()[i] + "\n");
+
+        sb.append("\n\n");
+        textArea.append(sb.toString());
+        textArea.setCaretPosition(textArea.getText().length());
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void handleMatrix(String source, DataMatrix matrix) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" >>> handleMatrix: " + matrix.getRowCount() + " x " + matrix.getColumnCount() + "\n");
+        sb.append("  species: " + matrix.getSpecies() + "\n");
+        sb.append("matrix name = " + matrix.getName() + ", source = " + source + "\n");
+        sb.append("\n\n");
+        textArea.append(sb.toString());
+        textArea.setCaretPosition(textArea.getText().length());
+
+    }
+//----------------------------------------------------------------------------------------
+
+    public void handleTuple(String source, GaggleTuple gaggleTuple) { // todo - extract this to common code for other diag/sample geese
+        // todo  - figure out a better way to show a small subset of a gaggleTuple
+        // (right now we just show the whole thing)
+        System.out.println("in SampleGoose.handleTuple()");
+        StringBuilder sb = new StringBuilder();
+        sb.append(" >>> handleTuple: " + gaggleTuple.getName() + "\n");
+        sb.append("  species: " + gaggleTuple.getSpecies() + "\n");
+        sb.append("  source: " + source + "\n\n");
+
+        int datalength = (gaggleTuple.getData().getSingleList().size() > 5) ? 5
+                : gaggleTuple.getData().getSingleList().size();
+        
+        int metadatalength = (gaggleTuple.getMetadata().getSingleList().size() > 5) ? 5
+                : gaggleTuple.getMetadata().getSingleList().size();
+
+        sb.append("First few rows of metadata: \n");
+        sb.append(gaggleTuple.getMetadata().toString());
+        sb.append("\n");
+
+        /*
+        if (gaggleTuple.getMetadata().getSingleList().size() == 0) {
+            sb.append(gaggleTuple.getMetadata().toString());
+            sb.append("\n");
+        } else {
+            String[] msegs = gaggleTuple.getMetadata().toString().split("\n");
+            for (int i = 0; i < metadatalength; i++) {
+                sb.append(msegs[i]);
+                sb.append("\n");
+            }
+
+        }
+        */
+
+
+        sb.append("\n\nFirst few rows of data: \n");
+        sb.append(gaggleTuple.getData().toString());
+        sb.append("\n");
+        /*
+        if (gaggleTuple.getData().getSingleList().size() == 0) {
+            sb.append(gaggleTuple.getData().toString());
+            sb.append("\n");
+        } else {
+            String[] dsegs = gaggleTuple.getData().toString().split("\n");
+            for (int i = 0; i < datalength; i++) {
+                //Tuple tuple = (Tuple)gaggleTuple.getData().getSingleAt(i).getValue();
+                //sb.append(tuple.toString());
+                sb.append(dsegs[i]);
+                sb.append("\n");
+            }
+            
+        }
+        */
+
+        sb.append("\n\n");
+        textArea.append(sb.toString());
+        textArea.setCaretPosition(textArea.getText().length());
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void handleCluster(
+            String source, Cluster cluster) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" >>> handleCluster: name =" + cluster.getName() + "\n");
+        sb.append("  species: " + cluster.getSpecies() + "\n");
+        sb.append("  source: " + source + "\n");
+        sb.append("  rows: " + cluster.getRowNames().length + "\n");
+        sb.append("  cols: " + cluster.getColumnNames().length + "\n");
+
+        sb.append("\n\n");
+        textArea.append(sb.toString());
+        textArea.setCaretPosition(textArea.getText().length());
+
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void handleNetwork(String source, Network network) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" >>> handleNetwork, name =  " + network.getName() + "\n");
+        sb.append("  source: " + source + "\n");
+        sb.append("  species: " + network.getSpecies() + "\n");
+        sb.append("  nodes: " + network.nodeCount() + "\n");
+        sb.append("  edges: " + network.edgeCount() + "\n");
+        if (network.getMetadata() == null) {
+            sb.append("  no metadata");
+        } else {
+            sb.append("  metadata found, # of singles: ");
+            sb.append(network.getMetadata().getSingleList().size());
+        }
+
+        sb.append("\n\n");
+        textArea.append(sb.toString());
+        textArea.setCaretPosition(textArea.getText().length());
+    }
+
+    //----------------------------------------------------------------------------------------
+    protected void broadcastSampleList() {
+        String[] nameList = {"YFL036W", "YFL037W", "YLR212C", "YLR213C",
+                "YML085C", "YML086C", "YML123C", "YML124C"};
+
+        String species = "Saccharomyces cerevisiae";
+        Namelist gaggleNameList = new Namelist();
+        gaggleNameList.setSpecies(species);
+        gaggleNameList.setNames(nameList);
+        try {
+            boss.broadcastNamelist(myGaggleName, targetGoose, gaggleNameList);
+        }
+        catch (RemoteException rex) {
+            System.err.println("SampleGoose: " + "rmi error calling boss.broadcast (nameList)");
+            rex.printStackTrace();
+        }
+
+    } // broadcastSampleList
+
+    //----------------------------------------------------------------------------------------
+    protected void broadcastSampleCluster() {
+        String[] rowNames = {"YFL036W", "YLR212C", "YML085C", "YML123C"};
+        String[] columnNames = {"T000", "T120", "T240"};
+
+        String species = "Saccharomyces cerevisiae";
+        String clusterName = "Sample Cluster";
+
+        try {
+            boss.broadcastCluster(myGaggleName, targetGoose, new Cluster(clusterName, species, rowNames, columnNames));
+        }
+        catch (RemoteException rex) {
+            System.err.println("SampleGoose: " + "rmi error calling boss.broadcast (cluster)");
+            rex.printStackTrace();
+        }
+
+    } // broadcastSampleCluster
+
+    //----------------------------------------------------------------------------------------
+    protected void broadcastSampleMatrix() {
+        org.systemsbiology.gaggle.core.datatypes.DataMatrix matrix = new org.systemsbiology.gaggle.core.datatypes.DataMatrix();
+
+        matrix.setFullName("Demo Yeast created on the fly, meaningless data");
+        matrix.setShortName("Demo Yeast");
+
+        String[] columnTitles = {"T000", "T060", "T120", "T240"};
+        String[] rowTitles = {"YFL036W", "YFL037W", "YLR212C", "YLR213C",
+                "YML085C", "YML086C", "YML123C", "YML124C"};
+        int dataRows = rowTitles.length;
+        int dataColumns = columnTitles.length;
+        matrix.setSize(dataRows, dataColumns);
+
+        matrix.setSpecies("Saccharomyces cerevisiae");
+        matrix.setRowTitlesTitle("GENE");
+        matrix.setColumnTitles(columnTitles);
+        matrix.setRowTitles(rowTitles);
+
+        for (int r = 0; r < dataRows; r++)
+            for (int c = 0; c < dataColumns; c++)
+                matrix.set(r, c, (r * 0.38) + c * 0.09);
+
+        matrix.setName("a sample matrix");
+        try {
+            boss.broadcastMatrix(myGaggleName, targetGoose, matrix);
+        }
+        catch (RemoteException rex) {
+            System.err.println("SampleGoose: " + "rmi error calling boss.broadcast (matrix)");
+            rex.printStackTrace();
+        }
+
+    } // broadcastSampleMatrix
+
+    //----------------------------------------------------------------------------------------
+    protected void broadcastSampleNetwork() {
+        Interaction i0 = new Interaction("YFL036W", "YFL037W", "GeneCluster");
+        Interaction i1 = new Interaction("YFL037W", "YLR212C", "GeneFusion");
+        Interaction i2 = new Interaction("YFL037W", "YML085C", "GeneFusion");
+        Interaction i3 = new Interaction("YFL037W", "YML124C", "GeneFusion");
+        Interaction i4 = new Interaction("YLR212C", "YLR213C", "GeneCluster");
+        Interaction i5 = new Interaction("YLR212C", "YML085C", "GeneFusion");
+        Interaction i6 = new Interaction("YLR212C", "YML124C", "GeneFusion");
+        Interaction i7 = new Interaction("YML123C", "YML124C", "GeneCluster");
+        Interaction i8 = new Interaction("YML085C", "YML086C", "GeneCluster");
+        Interaction i9 = new Interaction("YML085C", "YML124C", "GeneFusion");
+
+        org.systemsbiology.gaggle.core.datatypes.Network network = new org.systemsbiology.gaggle.core.datatypes.Network();
+
+        network.add(i0);
+        network.add(i1);
+        network.add(i2);
+        network.add(i3);
+        network.add(i4);
+        network.add(i5);
+        network.add(i6);
+        network.add(i7);
+        network.add(i8);
+        network.add(i9);
+
+        String species = "Saccharomyces cerevisiae";
+        String[] nodeNames = {"YFL036W", "YFL037W", "YLR212C", "YLR213C",
+                "YML085C", "YML086C", "YML123C", "YML124C"};
+        for (int i = 0; i < nodeNames.length; i++) {
+            network.addNodeAttribute(nodeNames[i], "moleculeType", "DNA");
+            network.addNodeAttribute(nodeNames[i], "species", species);
+        }
+
+        network.addEdgeAttribute("YFL036W (GeneCluster) YFL037W", "score", new Double(0.5));
+        network.addEdgeAttribute("YFL037W (GeneFusion) YLR212C", "score", new Double(0.4));
+        network.addEdgeAttribute("YFL037W (GeneFusion) YML085C", "score", new Double(0.3));
+        network.addEdgeAttribute("YFL037W (GeneFusion) YML124C", "score", new Double(0.2));
+        network.addEdgeAttribute("YLR212C (GeneCluster) YLR213C", "score", new Double(0.1));
+        network.addEdgeAttribute("YLR212C (GeneFusion) YML085C", "score", new Double(0.8));
+        network.addEdgeAttribute("YLR212C (GeneFusion) YML124C", "score", new Double(0.75));
+        network.addEdgeAttribute("YML123C (GeneCluster) YML124C", "score", new Double(0.55));
+        network.addEdgeAttribute("YML085C (GeneCluster) YML086C", "score", new Double(0.45));
+        network.addEdgeAttribute("YML085C (GeneFusion) YML124C", "score", new Double(0.35));
+        network.setSpecies(species);
+        network.setName("a sample network");
+
+        try {
+            boss.broadcastNetwork(myGaggleName, targetGoose, network);
+        }
+        catch (RemoteException rex) {
+            System.err.println("SampleGoose: " + "rmi error calling boss.broadcast (network)");
+            rex.printStackTrace();
+        }
+
+    } // broadcastSimpleNetwork
+
+    //----------------------------------------------------------------------------------------
+    protected void broadcastSampleTuple() {
+        // todo change this to broadcast vng names (and change species accordingly)
+        System.out.println("in SampleGoose.broadcastSampleTuple()");
+
+        double[] values = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8};
+        String[] rowNames = {"YFL036W", "YFL037W", "YLR212C", "YLR213C",
+                "YML085C", "YML086C", "YML123C", "YML124C"};
+
+        GaggleTuple gaggleTuple = new GaggleTuple();
+        gaggleTuple.setName("a tuple list holding a frame of a movie");
+        gaggleTuple.setSpecies("escargot");
+
+        // todo - think of convenience methods that would allow doing this in one line:
+        Single condition = new Single("condition", "a condition name");
+        gaggleTuple.getMetadata().addSingle(condition);
+        //
+
+        for (int i = 0; i < rowNames.length; i++) {
+            Tuple tuple = new Tuple();
+            tuple.addSingle(new Single(null, rowNames[i]));
+            tuple.addSingle(new Single(null, "log10 ratios"));
+            tuple.addSingle(new Single(null, values[i]));
+            gaggleTuple.getData().addSingle(new Single(tuple));
+        }
+
+
+
+        try {
+            boss.broadcastTuple(myGaggleName, targetGoose, gaggleTuple);
+        }
+        catch (RemoteException rex) {
+            System.err.println("SampleGoose: " + "rmi error calling boss.broadcastTuple");
+            rex.printStackTrace();
+        }
+
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void clearSelections() {
+        System.out.println("clearSelections");
+    }
+
+    //----------------------------------------------------------------------------------------
+    public int getSelectionCount() {
+        return 0;
+    }
+
+    //----------------------------------------------------------------------------------------
+    public String getName() {
+        return myGaggleName;
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void setName(String newName) {
+        myGaggleName = newName;
+        setTitle(myGaggleName);
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void setGeometry(int x, int y, int width, int height) {
+        System.out.println("setGeometry");
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void doBroadcastList() {
+
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void doHide() {
+        setVisible(false);
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void doShow() {
+        setAlwaysOnTop(true);
+        setVisible(true);
+        setAlwaysOnTop(false);
+
+    }
+
+    //----------------------------------------------------------------------------------------
+    public void doExit() {
+        connector.disconnectFromGaggle(true);
+        System.exit(0);
+    }
+
+    public void update(String[] activeGooseNames) {
+        this.activeGooseNames = activeGooseNames;
+
+        MiscUtil.updateGooseChooser(gooseChooser, myGaggleName, activeGooseNames);
+
+        if (textArea != null) {
+            StringBuffer sb = new StringBuffer();
+            sb.append(" >>> GOT AN UPDATE EVENT. New goose names are: \n");
+            for (String gooseName : activeGooseNames) {
+                sb.append(gooseName);
+                sb.append("\n");
+            }
+
+            sb.append("\n\n");
+            textArea.append(sb.toString());
+            textArea.setCaretPosition(textArea.getText().length());
+        }
+    }
+
+
+    public void setConnected(boolean connected, Boss boss) {
+        this.boss = boss;
+        System.out.println("SampleGoose: received connection status: " + connected);
+        if (connectButton != null)
+            connectButton.setEnabled(!connected);
+        if (disconnectButton !=  null)
+            disconnectButton.setEnabled(connected);
+    }
+
+
+    public void windowOpened(WindowEvent e) {
+    }
+
+    public void windowClosing(WindowEvent e) {
+        doExit();
+    }
+
+    public void windowClosed(WindowEvent e) {
+    }
+
+    public void windowIconified(WindowEvent e) {
+    }
+
+    public void windowDeiconified(WindowEvent e) {
+    }
+
+    public void windowActivated(WindowEvent e) {
+    }
+
+    public void windowDeactivated(WindowEvent e) {
+    }
+
+    public static void main(String[] args) throws Exception {
+        new SampleGoose();
+    } // main
+//-------------------------------------------------------------------------------------
+} // SampleGoose
diff --git a/src/org/gel/mauve/gui/AlignFrame.java b/src/org/gel/mauve/gui/AlignFrame.java
new file mode 100644
index 0000000..991c940
--- /dev/null
+++ b/src/org/gel/mauve/gui/AlignFrame.java
@@ -0,0 +1,717 @@
+/** 
+ * AlignFrame.java
+ *
+ * Description:	
+ * @author			koadman
+ * @version			
+ */
+
+package org.gel.mauve.gui;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Point;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.JList;
+
+import org.gel.mauve.MyConsole;
+import org.gel.mauve.contigs.ContigOrderer;
+import org.gel.mauve.gui.dnd.DnDList;
+
+/**
+ * A dialog box implementing a graphical interface to the command-line
+ * mauveAligner tool. Allows the user to manipulate various alignment options.
+ * Originally created with Metrowerks java gui designer.
+ */
+public class AlignFrame extends java.awt.Panel implements AlignmentProcessListener
+{
+    // Use for post-alignment file loading
+    protected String read_filename;
+    
+    // used when in GUI mode
+    protected java.awt.Frame frame;
+
+    // member declarations
+    JPanel parameterPanel = new JPanel();
+    JCheckBox defaultSeedCheckBox = new JCheckBox();
+    JCheckBox determineLCBsCheckBox = new JCheckBox();
+    protected JSlider seedLengthSlider = new JSlider();
+    JLabel seedLengthLabel = new JLabel();
+    JCheckBox recursiveCheckBox = new JCheckBox();
+    JLabel minLcbWeightLabel = new JLabel();
+    protected JTextField minLcbWeightText = new JTextField();
+    JCheckBox collinearCheckBox = new JCheckBox();
+
+    public JButton alignButton = new JButton();
+    public JButton cancelButton = new JButton();
+
+    protected JPanel sequencesPanel = new JPanel();
+    protected JButton addButton = new JButton();
+    protected JButton removeButton = new JButton();
+    protected JTextField outputFileText = new JTextField();
+    protected JButton outputButton = new JButton();
+    protected JList sequenceList = null;
+    protected JLabel outputLabel = new JLabel();
+    JLabel sequencesLabel = new JLabel();
+
+    JPanel parentPanel = new JPanel();
+    JTabbedPane alignmentOptionPane = new JTabbedPane();
+    /** < contains the various parameter panels */
+
+    protected JFileChooser fc = null;
+    protected JScrollPane listScrollPane = new JScrollPane();
+    protected DefaultListModel sequenceListModel = new DefaultListModel();
+    Dimension d;
+    int top_inset = 30;
+
+    protected Mauve mauve;
+    protected AlignWorker worker;
+    
+    public AlignFrame(Mauve mauve)
+    {
+    	this(mauve, true);
+    }
+    public AlignFrame(Mauve mauve, boolean gui)
+    {
+    	if(gui){
+		fc = new JFileChooser();
+    		frame  = new java.awt.Frame ();
+    		frame.setResizable(false);
+    	}
+   	 fc = new JFileChooser() {
+	    	public void updateUI () {
+	    		if (frame != null)
+	    			super.updateUI();
+	    	}
+	    	public boolean isTraversable(File f) {
+	    		return true;
+	    	}
+	    };
+	    fc.setMultiSelectionEnabled(true);
+        this.mauve = mauve;
+    }
+    public void initComponents()
+    {
+
+        // the following code sets the frame's initial state
+        parameterPanel.setSize(new java.awt.Dimension(350, 150));
+        parameterPanel.setLocation(new java.awt.Point(0, 210));
+        parameterPanel.setVisible(true);
+        parameterPanel.setLayout(null);
+        defaultSeedCheckBox.setVisible(true);
+        defaultSeedCheckBox.setSize(new java.awt.Dimension(180, 20));
+        defaultSeedCheckBox.setText("Default seed weight");
+        defaultSeedCheckBox.setSelected(true);
+        defaultSeedCheckBox.setLocation(new java.awt.Point(10, 10));
+        defaultSeedCheckBox.setToolTipText("Selecting this will cause Mauve to choose the minimum seed size automatically.");
+        determineLCBsCheckBox.setVisible(true);
+        determineLCBsCheckBox.setSize(new java.awt.Dimension(140, 20));
+        determineLCBsCheckBox.setText("Determine LCBs");
+        determineLCBsCheckBox.setSelected(true);
+        determineLCBsCheckBox.setLocation(new java.awt.Point(10, 90));
+        determineLCBsCheckBox.setToolTipText("Selecting this will cause Mauve to determine Locally Collinear Blocks.  Without this option, all multi-MUMs will be displayed.");
+        seedLengthSlider.setSize(new java.awt.Dimension(130, 50));
+        seedLengthSlider.setLocation(new java.awt.Point(200, 30));
+        seedLengthSlider.setVisible(true);
+        seedLengthSlider.setMajorTickSpacing(4);
+        seedLengthSlider.setMinorTickSpacing(2);
+        seedLengthSlider.setMinimum(3);
+        seedLengthSlider.setMaximum(21);
+        seedLengthSlider.setPaintLabels(true);
+        seedLengthSlider.setPaintTicks(true);
+        seedLengthSlider.setSnapToTicks(true);
+        seedLengthSlider.setValue(15);
+        seedLengthSlider.setEnabled(false);
+        seedLengthSlider.setToolTipText("This sets the minimum size of multi-MUMs found during the first pass of match detection");
+        seedLengthLabel.setSize(new java.awt.Dimension(160, 20));
+        seedLengthLabel.setLocation(new java.awt.Point(210, 10));
+        seedLengthLabel.setVisible(true);
+        seedLengthLabel.setText("Match Seed Weight:");
+        seedLengthLabel.setEnabled(false);
+        recursiveCheckBox.setVisible(true);
+        recursiveCheckBox.setSize(new java.awt.Dimension(140, 20));
+        recursiveCheckBox.setText("Full Alignment");
+        recursiveCheckBox.setSelected(true);
+        recursiveCheckBox.setLocation(new java.awt.Point(10, 145));
+        recursiveCheckBox.setToolTipText("This enables recursive anchor search and gapped alignment using MUSCLE");
+        collinearCheckBox.setVisible(true);
+        collinearCheckBox.setSize(new java.awt.Dimension(220, 20));
+        collinearCheckBox.setText("Assume collinear genomes");
+        collinearCheckBox.setSelected(false);
+        collinearCheckBox.setLocation(new java.awt.Point(10, 110));
+        collinearCheckBox.setToolTipText("Set this when the input sequences do not have rearrangements");
+        minLcbWeightLabel.setText("Min LCB weight:");
+        minLcbWeightLabel.setHorizontalAlignment(JLabel.RIGHT);
+        d = minLcbWeightLabel.getPreferredSize();
+        minLcbWeightLabel.setSize(new Dimension( d.width, 20 ));
+        minLcbWeightLabel.setLocation(new java.awt.Point(265 - d.width, 90));
+        minLcbWeightLabel.setVisible(true);
+        minLcbWeightText.setVisible(true);
+        minLcbWeightText.setSize(new java.awt.Dimension(60, 20));
+        minLcbWeightText.setLocation(new java.awt.Point(270, 90));
+        minLcbWeightText.setText("default");
+        minLcbWeightText.setToolTipText("LCBs below this weight will be removed from the alignment");
+        alignButton.setVisible(true);
+        alignButton.setSize(new java.awt.Dimension(100, 30));
+        alignButton.setText("Align...");
+        alignButton.setLocation(new java.awt.Point(230, 320+top_inset));
+        cancelButton.setVisible(true);
+        cancelButton.setEnabled(false);
+        cancelButton.setSize(new java.awt.Dimension(165, 30));
+        cancelButton.setText("Cancel alignment");
+        cancelButton.setLocation(new java.awt.Point(60, 320+top_inset));
+        sequencesPanel.setSize(new java.awt.Dimension(350, 210));
+        sequencesPanel.setLocation(new java.awt.Point(0, 0));
+        sequencesPanel.setVisible(true);
+        sequencesPanel.setLayout(null);
+        addButton.setVisible(true);
+        addButton.setSize(new java.awt.Dimension(150, 20));
+        addButton.setText("Add Sequence...");
+        addButton.setLocation(new java.awt.Point(10, 150));
+        removeButton.setVisible(true);
+        removeButton.setSize(new java.awt.Dimension(175, 20));
+        removeButton.setText("Remove Sequence");
+        removeButton.setLocation(new java.awt.Point(165, 150));
+        outputFileText.setVisible(true);
+        outputFileText.setSize(new java.awt.Dimension(220, 20));
+        outputFileText.setLocation(new java.awt.Point(85, 180));
+        outputFileText.setToolTipText("The path and base file name for output files");
+        outputButton.setVisible(true);
+        outputButton.setSize(new java.awt.Dimension(20, 20));
+        outputButton.setText("...");
+        outputButton.setLocation(new java.awt.Point(310, 180));
+        outputButton.setToolTipText("Set the output file location");
+        if(frame!=null){
+        	sequenceList = new DnDList();
+        }else{
+        	sequenceList = new JList();
+        }
+        sequenceList.setModel(sequenceListModel);
+        sequenceList.setVisible(true);
+        sequenceList.setSize(new java.awt.Dimension(320, 110));
+        //		sequenceList.setLocation(new java.awt.Point(10, 30));
+        listScrollPane.getViewport().setView(sequenceList);
+        listScrollPane.setSize(new java.awt.Dimension(320, 110));
+        listScrollPane.setLocation(new java.awt.Point(10, 30));
+        outputLabel.setSize(new java.awt.Dimension(75, 20));
+        outputLabel.setLocation(new java.awt.Point(10, 180));
+        outputLabel.setVisible(true);
+        outputLabel.setText("Output:");
+        sequencesLabel.setSize(new java.awt.Dimension(180, 20));
+        sequencesLabel.setLocation(new java.awt.Point(10, 10));
+        sequencesLabel.setVisible(true);
+        sequencesLabel.setText("Sequences to align:");
+        setLocation(new java.awt.Point(0, 0));
+        setLayout(null);
+        
+        // if we're not on Mac or Windows then use the current dir as default
+        String osname = System.getProperty("os.name");
+        if(osname.indexOf("indow")==-1&&osname.indexOf("Mac")==-1)
+        	fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
+        
+        parameterPanel.add(defaultSeedCheckBox);
+        parameterPanel.add(determineLCBsCheckBox);
+        parameterPanel.add(collinearCheckBox);
+        parameterPanel.add(seedLengthSlider);
+        parameterPanel.add(seedLengthLabel);
+        parameterPanel.add(recursiveCheckBox);
+        parameterPanel.add(minLcbWeightLabel);
+        parameterPanel.add(minLcbWeightText);
+        sequencesPanel.add(addButton);
+        sequencesPanel.add(removeButton);
+        sequencesPanel.add(outputFileText);
+        sequencesPanel.add(outputButton);
+        sequencesPanel.add(listScrollPane);
+        sequencesPanel.add(outputLabel);
+        sequencesPanel.add(sequencesLabel);
+        //		add(parameterPanel);
+        //		add(sequencesPanel);
+
+        parentPanel.setLocation(new java.awt.Point(0, 0));
+        parentPanel.setVisible(true);
+        parentPanel.setLayout(null);
+
+        alignmentOptionPane.addTab("Files", sequencesPanel);
+        alignmentOptionPane.addTab("Parameters", parameterPanel);
+        //		alignmentOptionPane.setSelectedIndex( 0 );        
+        alignmentOptionPane.setVisible(true);
+
+        parentPanel.add(alignmentOptionPane);
+        parentPanel.add(alignButton);
+        parentPanel.add(cancelButton);
+
+        add(parentPanel);
+
+        int frame_width = 400;
+        int frame_height = 383;
+
+        if(frame != null){
+			frame.setIconImage(MauveFrame.mauve_icon.getImage());
+			frame.setTitle("Align sequences...");
+			frame.setLocation(new java.awt.Point(0, 0));
+			frame.setLayout(null);
+			frame.add(this);
+			frame.setSize(new java.awt.Dimension(frame_width, frame_height));
+        }
+        setSize(new java.awt.Dimension(frame_width, frame_height));
+        parentPanel.setSize(new java.awt.Dimension(frame_width, frame_height));
+        alignmentOptionPane.setSize(new java.awt.Dimension(frame_width, 310));
+        alignmentOptionPane.setLocation(new java.awt.Point(0, top_inset));
+
+        // event handling
+
+        defaultSeedCheckBox.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                defaultSeedCheckBoxActionPerformed(e);
+            }
+        });
+
+        determineLCBsCheckBox.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                determineLCBsCheckBoxActionPerformed(e);
+            }
+        });
+
+        collinearCheckBox.addActionListener(new java.awt.event.ActionListener()
+                {
+                    public void actionPerformed(java.awt.event.ActionEvent e)
+                    {
+                        collinearCheckBoxActionPerformed(e);
+                    }
+                });
+
+        addButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                addButtonActionPerformed(e);
+            }
+        });
+
+        removeButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                removeButtonActionPerformed(e);
+            }
+        });
+
+        outputButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                outputButtonActionPerformed(e);
+            }
+        });
+
+        if (frame != null) {
+        	frame.addWindowListener(new java.awt.event.WindowAdapter()
+	        {
+	            public void windowClosing(java.awt.event.WindowEvent e)
+	            {
+	                thisWindowClosing(e);
+	            }
+	            public void windowClosed(java.awt.event.WindowEvent e)
+	            {
+	                thisWindowClosed(e);
+	            }
+	        });
+        }
+
+        // delete the file in case the user chooses a different file name
+        alignButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                alignButtonActionPerformed(e);
+            }
+        });
+        cancelButton.addActionListener(new java.awt.event.ActionListener()
+                {
+                    public void actionPerformed(java.awt.event.ActionEvent e)
+                    {
+                        cancelButtonActionPerformed(e);
+                    }
+                });
+    }
+    
+    File getDefaultFile() throws IOException
+    {
+        return File.createTempFile("mauve", ".mln");
+    }
+
+    /*
+     * Build a platform-dependent path to the aligner binary
+     * @param name	The name of the aligner binary
+     */
+    public static String getBinaryPath(String name)
+    {
+        String os_type = System.getProperty("os.name");
+        String os_arch = System.getProperty("os.arch");
+
+        //MyConsole.out().println("OS name is: " + os_type + " arch: " + os_arch);
+        if (os_type.startsWith("Windows"))
+        {
+        	if(os_arch.indexOf("64") >= 0)
+        		return "win64\\" + name;
+        	else
+        	{
+        		// check for a win32 folder to support running in the dev environment
+        		File f = new File("win32\\" + name);
+        		if(f.exists())
+        			return "win32\\" + name;
+        		return name;
+        	}
+        }
+        else if (os_type.startsWith("Mac"))
+        {
+//            String mauve_path = System.getProperty("user.dir");
+//            mauve_path += "/Mauve.app/Contents/MacOS/" + name;
+            String mauve_path = System.getProperty("java.library.path") + "/" + name;
+        	File f = new File(mauve_path);
+        	if( f.exists()){
+        		return mauve_path;
+        	} else {
+        		mauve_path = System.getProperty("user.dir") + "/osx/" + name;
+        		f = new File(mauve_path);
+        		if (f.exists())
+        			return mauve_path;
+        	}
+    		return name;
+        }
+        else
+        {
+        	String mauvedir = System.getProperty("mauveDir");
+        	if(mauvedir == null)	mauvedir = "./";
+        	String pname = mauvedir + name;
+       		pname = mauvedir + "linux-x64/" + name;
+        	System.out.println("trying path " + pname);
+        	File f = new File(pname);
+        	if( f.exists())
+        		return pname;
+        	else
+        		return name;
+        }
+    }
+    
+    /**
+     * Prints an aligner command in an easily readable format.
+     * 
+     * @param cmd the command to print
+     * @param out the output stream to print to
+     */
+	public static void printCommand(String[] cmd, PrintStream out){
+		StringBuilder sb = new StringBuilder();
+		sb.append("  "+cmd[0]+"\n");
+		for (int i = 1; i < cmd.length; i++){
+			sb.append("    "+cmd[i]+"\n");
+		}
+		out.print(sb.toString());
+	}
+
+    /**
+     * Read the user's genome alignment parameters and start the alignment using
+     * the command line mauveAligner tool. This function translates information
+     * entered into the GUI aligner front-end into a command line for
+     * mauveAligner.
+     */
+    public void alignButtonActionPerformed(java.awt.event.ActionEvent evt)
+    {
+    	File outfile = new File(this.getOutput());
+    	// if outfile not selected, query until selected.
+    	while(this.getOutput().length()==0 || outfile.isDirectory())
+    	{
+        	fc.setDialogTitle("Save alignment file");
+        	fc.setSelectedFile(new File( "" ));
+            int returnVal = fc.showSaveDialog(this);
+
+            if (returnVal == JFileChooser.APPROVE_OPTION)
+            {
+            	outfile = fc.getSelectedFile();
+                outputFileText.setText(outfile.getPath());
+            }else
+            	return;
+    	}
+    	
+        alignButton.setEnabled(false);
+        
+        String[] mauve_cmd = makeAlignerCommand();
+
+        // No command means there was an error.
+        if (mauve_cmd == null)
+        {
+            alignButton.setEnabled(true);
+            return;
+        }
+        MyConsole.showConsole();
+      //  printCommand(mauve_cmd);
+    	worker = new AlignWorker(this, mauve_cmd, true);
+        System.out.println("Running alignment.\nExecuting ");
+        AlignFrame.printCommand(worker.mauve_cmd, System.out);
+        worker.start();
+        cancelButton.setEnabled(true);
+    }
+    
+    public void completeAlignment(int retcode)
+    {
+        alignButton.setEnabled(true);
+        cancelButton.setEnabled(false);
+        
+        if (retcode == 0)
+        {
+            File readFile = new File(read_filename);
+            if(!readFile.exists() || readFile.length() == 0)
+            {
+            	if(frame!=null){
+            		JOptionPane.showMessageDialog(null, "The aligner failed to produce an alignment.  The sequences may not contain any homologous regions.", "An error occurred", JOptionPane.ERROR_MESSAGE);
+            	}else{
+            		System.err.println("The aligner failed to produce an alignment.  The sequences may not contain any homologous regions.");
+            	}
+            }
+            System.out.println("Alignment complete!");
+            mauve.loadFile(new File(read_filename));
+            setVisible(false);
+        }
+        else if(!worker.getKilled())
+        {
+            JOptionPane.showMessageDialog(null, "mauveAligner exited with an error code.  Check the log window for details", "An error occurred", JOptionPane.ERROR_MESSAGE);
+        }
+        worker = null;
+    }
+    public void cancelButtonActionPerformed(java.awt.event.ActionEvent evt)
+    {
+    	worker.interrupt();
+    	alignButton.setEnabled(true);
+    	cancelButton.setEnabled(false);
+    }
+    
+    protected String[] makeAlignerCommand()
+    {
+        return new String[0];
+    }
+    
+    protected void printCommand(String[] mauve_cmd)
+    {
+        // Make a readable version of command.
+  /*      String mauve_str = new String();
+        for (int cmdI = 0; cmdI < mauve_cmd.length; cmdI++)
+        {
+            mauve_str += mauve_cmd[cmdI] + " ";
+        } */
+        MyConsole.out().println("Executing: ");
+//        MyConsole.out().println(mauve_str);
+        AlignFrame.printCommand(mauve_cmd, MyConsole.out());
+    }
+    
+    private boolean mShown = false;
+
+    public void addNotify()
+    {
+        super.addNotify();
+
+        if (mShown)
+            return;
+
+        // move components to account for insets
+        Insets insets = getInsets();
+        Component[] components = getComponents();
+        for (int i = 0; i < components.length; i++)
+        {
+            Point location = components[i].getLocation();
+            location.move(location.x, location.y + insets.top);
+            components[i].setLocation(location);
+        }
+
+        mShown = true;
+    }
+
+    // Close the window when the close box is clicked
+    void thisWindowClosing(java.awt.event.WindowEvent e)
+    {
+    	if(cancelButton.isEnabled())
+    	{
+    		// ask the user whether they would like to cancel the alignment
+            int choice = JOptionPane.showConfirmDialog(null, "An alignment is in progress.  Closing this window will terminate the alignment.  Would you like to proceed?\n", "Alignment in progress", JOptionPane.YES_NO_OPTION);
+    		if(choice == JOptionPane.NO_OPTION)
+    			return;
+    	}
+        frame.setVisible(false);
+        frame.dispose();
+    }
+    void thisWindowClosed(java.awt.event.WindowEvent e)
+    {
+    	if(cancelButton.isEnabled())
+    		worker.interrupt();
+    }
+
+    public void defaultSeedCheckBoxActionPerformed(java.awt.event.ActionEvent e)
+    {
+        if (defaultSeedCheckBox.isSelected())
+        {
+            seedLengthSlider.setEnabled(false);
+            seedLengthLabel.setEnabled(false);
+        }
+        else
+        {
+            seedLengthSlider.setEnabled(true);
+            seedLengthLabel.setEnabled(true);
+        }
+    }
+
+    public void determineLCBsCheckBoxActionPerformed(java.awt.event.ActionEvent e)
+    {
+        if (determineLCBsCheckBox.isSelected())
+        {
+            recursiveCheckBox.setEnabled(true);
+            if (!collinearCheckBox.isSelected())
+            {
+	            minLcbWeightLabel.setEnabled(true);
+	            minLcbWeightText.setEnabled(true);
+            }
+            collinearCheckBox.setEnabled(true);
+        }
+        else
+        {
+            recursiveCheckBox.setEnabled(false);
+            minLcbWeightLabel.setEnabled(false);
+            minLcbWeightText.setEnabled(false);
+            collinearCheckBox.setEnabled(false);
+        }
+    }
+
+    public void collinearCheckBoxActionPerformed(java.awt.event.ActionEvent e)
+    {
+        if (collinearCheckBox.isSelected())
+        {
+            minLcbWeightLabel.setEnabled(false);
+            minLcbWeightText.setEnabled(false);
+        }
+        else
+        {
+        	if( determineLCBsCheckBox.isSelected() ){
+	            minLcbWeightLabel.setEnabled(true);
+	            minLcbWeightText.setEnabled(true);
+        	}
+        }
+    }
+
+    public void addButtonActionPerformed(java.awt.event.ActionEvent e)
+    {
+    	fc.setDialogTitle("Select a genome sequence");
+    	fc.setMultiSelectionEnabled(true);
+        int returnVal = fc.showOpenDialog(this);
+
+        if (returnVal == JFileChooser.APPROVE_OPTION)
+        {
+        	File[] sfs = fc.getSelectedFiles();
+        	for(File seq_file : sfs){
+	            int sel_index = sequenceList.getSelectedIndex();
+	            if (sel_index >= 0)
+	                sequenceListModel.add(sel_index, seq_file.getPath());
+	            else
+	                sequenceListModel.addElement(seq_file.getPath());
+        	}
+        }
+    }
+
+    public void removeButtonActionPerformed(java.awt.event.ActionEvent e)
+    {
+        int[] selection = sequenceList.getSelectedIndices();
+        for (int selI = selection.length; selI > 0; selI--)
+        {
+            sequenceListModel.removeElementAt(selection[selI - 1]);
+        }
+    }
+
+    public void outputButtonActionPerformed(java.awt.event.ActionEvent e)
+    {
+    	fc.setDialogTitle("Save alignment file");
+    	fc.setName("");
+    	fc.setSelectedFile(new File( outputFileText.getText() ));
+        int returnVal = fc.showSaveDialog(this);
+
+        if (returnVal == JFileChooser.APPROVE_OPTION)
+        {
+            File seq_file = fc.getSelectedFile();
+            outputFileText.setText(seq_file.getPath());
+        }
+    }
+
+    public boolean getRecursive()
+    {
+        return recursiveCheckBox.isSelected();
+    }
+
+    public boolean getCollinear()
+    {
+        return collinearCheckBox.isSelected();
+    }
+
+    public int getSeedWeight()
+    {
+        if (!defaultSeedCheckBox.isSelected())
+            return seedLengthSlider.getValue();
+        else
+            return -1;
+    }
+
+    public boolean isLCBSearchEnabled()
+    {
+        return determineLCBsCheckBox.isSelected();
+    }
+
+    public int getMinLcbWeight()
+    {
+        try
+        {
+            return Integer.parseInt(minLcbWeightText.getText());
+        }
+        catch (NumberFormatException nfe)
+        {
+            return -1;
+        }
+    }
+
+    public String getOutput()
+    {
+        return outputFileText.getText();
+    }
+
+    public void setOutput(String filename)
+    {
+        outputFileText.setText(filename);
+        outputFileText.setCaretPosition (filename.length ());
+    }
+
+    public String[] getSequences()
+    {
+        String[] seqs = new String[sequenceListModel.getSize()];
+        for (int seqI = 0; seqI < seqs.length; seqI++)
+            seqs[seqI] = (String) (sequenceListModel.get(seqI));
+        return seqs;
+    }
+	public void setVisible (boolean show) {
+		super.setVisible (show);
+		if (show && frame != null)
+			frame.setVisible(true);
+	}
+}
diff --git a/src/org/gel/mauve/gui/AlignWorker.java b/src/org/gel/mauve/gui/AlignWorker.java
new file mode 100644
index 0000000..05c7c6c
--- /dev/null
+++ b/src/org/gel/mauve/gui/AlignWorker.java
@@ -0,0 +1,139 @@
+package org.gel.mauve.gui;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+
+import org.gel.mauve.MyConsole;
+
+
+public class AlignWorker extends SwingWorker
+{
+    String[] mauve_cmd;
+    AlignmentProcessListener align_listener;
+    int retcode = -1;
+    private Process align_proc = null;
+    private boolean killed = false;
+    
+    private PrintStream out;
+    private PrintStream err;
+    
+    public AlignWorker (AlignmentProcessListener align_listener, String[] mauve_cmd)
+    {
+    	this(align_listener,mauve_cmd,true);
+    }
+    
+    /**
+     * 
+     * @param align_listener
+     * @param mauve_cmd
+     * @param verbose true if redirect progressiveMauve output, false if suppress
+     */
+    public AlignWorker (AlignmentProcessListener align_listener, String[] mauve_cmd, boolean verbose){
+    	this.mauve_cmd = mauve_cmd;
+        this.align_listener = align_listener;
+        if (verbose) {
+        	this.out = MyConsole.out();
+        	this.err = MyConsole.err();
+        } else {
+        	this.out = null;
+        	this.err = null;
+        }
+    }
+   
+    public Object construct()
+    {
+        try
+        {
+            align_proc = Runtime.getRuntime().exec(mauve_cmd);
+
+       //     OutStreamPrinter outP = new OutStreamPrinter(align_proc.getInputStream(), MyConsole.out());
+        //    OutStreamPrinter errP = new OutStreamPrinter(align_proc.getErrorStream(), MyConsole.err());
+            OutStreamPrinter outP = new OutStreamPrinter(align_proc.getInputStream(), out);
+            OutStreamPrinter errP = new OutStreamPrinter(align_proc.getErrorStream(), err);
+            
+            errP.start();
+            outP.start();
+            
+            try
+            {
+                retcode = align_proc.waitFor();
+            }
+            catch (InterruptedException e)
+            {
+            	if(!killed)
+            		MyConsole.err().println("Interrupted.");
+            }
+
+        	if(!killed)
+        	{
+	            if (retcode == 0)
+	            {
+	                MyConsole.out().println("Completed without error.");
+	            }
+	            else
+	            {
+	                MyConsole.err().println("Exited with error code: " + retcode);
+	            }
+        	}
+        }
+        catch (IOException e)
+        {
+        	if(!killed)
+        	{
+	            MyConsole.err().println("Error running aligner.");
+	            e.printStackTrace(MyConsole.err());
+        	}
+        }
+        return new Integer(retcode);
+    }
+    
+    public void finished()
+    {
+        align_listener.completeAlignment(retcode);
+    }
+    public void interrupt() {
+    	killed = true;
+    	align_proc.destroy();
+    	align_proc = null;
+    	super.interrupt();
+    }
+    public boolean getKilled()
+    {
+    	return killed;
+    }
+}
+
+
+class OutStreamPrinter extends Thread
+{
+    InputStream in;
+    PrintStream ps;
+   
+    OutStreamPrinter(InputStream in, PrintStream ps)
+    {
+        this.in = in;
+        this.ps = ps;
+    }
+    
+    public void run()
+    {
+        try
+        {
+            InputStreamReader isr = new InputStreamReader(in);
+            BufferedReader br = new BufferedReader(isr);
+            String line=null;
+            while ( (line = br.readLine()) != null)
+            {
+            	if(ps!=null)
+            		ps.println(line);
+            }
+        } 
+    	catch (IOException ioe)
+    	{	
+    	    ioe.printStackTrace(MyConsole.err());  
+    	}
+    }
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/AlignmentProcessListener.java b/src/org/gel/mauve/gui/AlignmentProcessListener.java
new file mode 100644
index 0000000..666dd5f
--- /dev/null
+++ b/src/org/gel/mauve/gui/AlignmentProcessListener.java
@@ -0,0 +1,7 @@
+package org.gel.mauve.gui;
+
+import java.util.EventListener;
+
+public interface AlignmentProcessListener extends EventListener {
+    public void completeAlignment(int retcode);
+}
diff --git a/src/org/gel/mauve/gui/AnalysisDisplayWindow.java b/src/org/gel/mauve/gui/AnalysisDisplayWindow.java
new file mode 100644
index 0000000..913286e
--- /dev/null
+++ b/src/org/gel/mauve/gui/AnalysisDisplayWindow.java
@@ -0,0 +1,278 @@
+package org.gel.mauve.gui;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+// these are needed to grab the users display screen size
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.PrintStream;
+import java.util.HashMap;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JScrollPane;
+import javax.swing.border.BevelBorder;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableModel;
+
+//import org.gel.mauve.assembly.ScoreAssembly.ChangeCards;
+
+public class AnalysisDisplayWindow extends JFrame {
+	
+	private static int xMax = Toolkit.getDefaultToolkit().getScreenSize().width;
+	
+	private int contentCount;
+	
+	private JPanel topBar;
+	
+	private JButton saveBtn;
+	
+	private JPanel content;
+	
+	private JPanel botBar;
+	
+	private JFrame frame;
+	
+	private Font font;
+	
+	private HashMap<String,JComponent> components;
+	
+	private String name;
+	
+	private int width;
+	
+	private int height;
+	
+	private CardLayout cardMngr;
+	
+	private ContentManager cc;
+	
+	public AnalysisDisplayWindow(String name, int width, int height) {
+		contentCount = 0;
+		font = new Font ("monospaced", Font.PLAIN, 12);
+		frame = new JFrame(name);
+		
+		setLayout(new BorderLayout());
+		this.name = name;
+		this.width = width;
+		this.height = height;
+		frame.setSize(this.width, this.height);
+		frame.setLocation(xMax-this.width, 0);
+		components = new HashMap<String,JComponent>();
+		cc = new ContentManager();
+		topBar = new JPanel();
+		BoxLayout tmp = new BoxLayout(topBar, BoxLayout.X_AXIS);
+		topBar.setLayout(tmp);
+		saveBtn = new JButton("Save");
+		topBar.add(saveBtn);
+		topBar.setSize(100, 100);
+		saveBtn.addActionListener(cc);
+		GridLayout tmp1 = new GridLayout(1,0);
+		tmp1.setHgap(10);
+		botBar = new JPanel(tmp1,true);
+		content = new JPanel(new BorderLayout());
+		topBar.setBorder(BorderFactory.createEmptyBorder(2,1,4,1));
+		botBar.setBorder(BorderFactory.createEmptyBorder(4,4,4,4));
+		cardMngr = new CardLayout();
+		content.setLayout(cardMngr);
+		content.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
+		JPanel tempPanel = new JPanel(new BorderLayout());
+		tempPanel.add(topBar, BorderLayout.NORTH);
+		tempPanel.add(botBar,BorderLayout.SOUTH);
+		tempPanel.add(content, BorderLayout.CENTER);
+		tempPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
+		frame.getContentPane().add(tempPanel, BorderLayout.CENTER);
+		
+	}
+	
+	public void showWindow(){
+		if (components.size() == 0)
+			throw new RuntimeException("Can't call showWindow() unless there are Panels to show");
+		
+		if (botBar.getComponentCount() <= 1){
+			botBar.setVisible(false);
+		} else {
+			botBar.setVisible(true);
+		}
+		if (!frame.isVisible()) {
+			frame.setLocation(xMax-this.width, 0);
+			frame.setSize(width, height);	
+		}
+		frame.setVisible(true);
+	}
+	
+	public void closeWindow(){
+		frame.setVisible(false);
+	}
+
+	/**
+	 * Adds a text pane to this display window.
+	 * 
+	 * @param cmd
+	 * @param desc
+	 * @param setTop
+	 * @return the text area that gets displayed in the created pane
+	 */
+	public JTextArea addContentPanel(String cmd, String desc, boolean setTop){
+		// create button, add to bottom bar, and add a listener
+		JButton butn = new JButton(cmd);
+		botBar.add(butn);
+		butn.addActionListener(cc);
+		JTextArea ta = new JTextArea(25, 25); 
+		components.put(cmd, ta);
+		ta.setFont(font);
+		ta.setEditable(false);
+		cc.addCard(cmd, desc);
+		if (setTop){
+			cardMngr.show(content, desc);
+		}
+		JScrollPane jsp = new JScrollPane(ta);
+		content.add(desc, jsp);
+		contentCount++;
+		return ta;
+	}
+	
+	/**
+	 * Adds a table pane to this display window 
+	 * 
+	 * @param cmd
+	 * @param desc
+	 * @param setTop
+	 * @return the table that gets displayed in the created pane
+	 */
+	public DefaultTableModel addContentTable(String cmd, String desc, boolean setTop){
+		DefaultTableModel data = new DefaultTableModel();
+		JButton butn = new JButton(cmd);
+		botBar.add(butn);
+		butn.addActionListener(cc);
+		cc.addCard(cmd, desc);
+		if (setTop){
+			cardMngr.show(content, desc);
+		}
+		JTable table = new JTable(data);
+		components.put(cmd, table);
+		JScrollPane jsp = new JScrollPane(table);
+		table.setFillsViewportHeight(true);
+		table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
+		content.add(desc, jsp);
+		return data;
+	}
+	
+	
+	private class ContentManager implements ActionListener {
+		
+		HashMap<String,String> btmBarMap;
+		
+		HashMap<String,String> topBarMap;
+		
+		String lastCmd;
+		
+		JPanel savePanel;
+		
+		
+		public ContentManager(){
+			savePanel = new JPanel();
+			savePanel.setSize(200, 100);
+			btmBarMap = new HashMap<String,String>();
+			topBarMap = new HashMap<String,String>();
+		}
+		
+		public void addCard(String cmd, String action){
+			if (lastCmd == null)
+				lastCmd = cmd;
+			btmBarMap.put(cmd, action);
+		}
+		
+		public void actionPerformed (ActionEvent e) {
+			String cmd = e.getActionCommand();
+			if (cmd.equalsIgnoreCase("Save")){
+				try {
+					printText();
+				} catch (Exception exc){
+					
+				}
+			} else if (btmBarMap.containsKey(cmd)){
+				String action = btmBarMap.get(cmd);
+				lastCmd = cmd;
+				cardMngr.show(content, action);
+			} else {
+				System.err.println("Illegal Action!");
+			}
+		}
+		
+		private void printText() throws Exception{
+			File file = null;
+			int option = -1;
+			boolean selectFile = true;
+
+			JFileChooser fc = new JFileChooser(System.getProperty("user.home"));
+			while(selectFile){
+				selectFile = false;
+				option = fc.showSaveDialog(frame);
+				if (option == JFileChooser.APPROVE_OPTION){
+					file = fc.getSelectedFile();
+					if (file.exists()){
+						int result = JOptionPane.showConfirmDialog(frame,
+										"The file " + file + " already exists.  Overwrite?", 
+										"File exists", JOptionPane.YES_NO_OPTION);
+						selectFile = result == JOptionPane.NO_OPTION;
+					} else {
+						file.createNewFile();
+						selectFile = false;
+					}
+				} 
+			}
+			if (option != JFileChooser.CANCEL_OPTION){
+				System.err.println("Writing "+frame.getTitle()+" - "+lastCmd+ " to " + file.getAbsolutePath());
+
+				PrintStream out = (new PrintStream(file));
+				JComponent toPrint = components.get(lastCmd);
+				if (toPrint instanceof JTextArea){
+					out.print(((JTextArea)toPrint).getText());
+				} else if (toPrint instanceof JTable) {
+					DefaultTableModel data = (DefaultTableModel) 
+											((JTable) toPrint).getModel();
+					int numCol = data.getColumnCount();
+					int numRow = data.getRowCount();
+					out.print(data.getColumnName(0));
+					for (int colI = 1 ; colI < numCol; colI++){
+						out.print("\t"+data.getColumnName(colI));
+					}
+					out.print("\n");
+					for (int rowI = 0; rowI < numRow; rowI++){
+						out.print(data.getValueAt(rowI, 0).toString());
+						for (int colI = 0 ; colI < numCol; colI++){
+							out.print("\t"+data.getValueAt(rowI, colI).
+															toString());
+						}
+						out.print("\n");
+					}
+				}
+				out.flush();
+				out.close();
+			}
+				
+		
+		}
+		
+		public String getLastCommand(){
+			return lastCmd;
+		}
+		
+	}
+	
+	
+	
+}
diff --git a/src/org/gel/mauve/gui/ConsoleDialog.java b/src/org/gel/mauve/gui/ConsoleDialog.java
new file mode 100644
index 0000000..c1b26b3
--- /dev/null
+++ b/src/org/gel/mauve/gui/ConsoleDialog.java
@@ -0,0 +1,35 @@
+package org.gel.mauve.gui;
+
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.HeadlessException;
+import java.awt.Toolkit;
+
+import javax.swing.JDialog;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+public class ConsoleDialog extends JDialog {
+	JTextArea text;
+
+	public ConsoleDialog (Frame owner) throws HeadlessException {
+		super (owner, "Mauve Console");
+
+		initComponents ();
+	}
+
+	private void initComponents () {
+		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+		setSize (400, 600);
+		setLocation(dim.width - 400, 0);
+		text = new JTextArea ();
+		JScrollPane sp = new JScrollPane (text);
+		getContentPane ().add (sp);
+	}
+
+	public void appendText (String s) {
+		text.append (s);
+		setVisible (true);
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/ExportFrame.java b/src/org/gel/mauve/gui/ExportFrame.java
new file mode 100644
index 0000000..ed6c70c
--- /dev/null
+++ b/src/org/gel/mauve/gui/ExportFrame.java
@@ -0,0 +1,464 @@
+package org.gel.mauve.gui;
+
+import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.RenderingHints;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriter;
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
+import javax.imageio.stream.ImageOutputStream;
+import javax.swing.AbstractButton;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFormattedTextField;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.event.UndoableEditListener;
+
+import org.gel.mauve.MyConsole;
+
+
+public class ExportFrame extends JFrame
+{
+    private final static DecimalFormat FORMAT = new DecimalFormat("##########");
+    
+    private RearrangementPanel rrpanel;
+    private JTextField outputFile = new JTextField();
+    private JFileChooser fc = new JFileChooser();
+    private JComboBox formatSelector = new JComboBox();
+
+    private double scale = 1.0;
+    private JFormattedTextField widthBox = new JFormattedTextField(FORMAT);
+    private JFormattedTextField heightBox = new JFormattedTextField(FORMAT);
+    private boolean scaleChanging = false;
+
+    private float jpegQuality = 0.75f;
+    private ButtonGroup qualityGroup = new ButtonGroup();
+    
+    public ExportFrame(RearrangementPanel rrpanel)
+    {
+        this.rrpanel = rrpanel;
+        setSize(300,300);
+        
+        getContentPane().setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+
+        setTitle("Mauve Image Export");
+
+        c.insets = new Insets(2,2,2,2);
+
+        // Format label.
+        c.gridx = 0;
+        c.gridy = 0;
+        c.gridwidth = 1;
+        c.anchor = GridBagConstraints.EAST;
+        c.fill = GridBagConstraints.NONE;
+        getContentPane().add(new JLabel("Format:"), c);
+        
+        // Format selector.
+        c.gridx = 1;
+        c.gridy = 0;
+        c.gridwidth = 2;
+        c.anchor = GridBagConstraints.WEST;
+        c.fill = GridBagConstraints.HORIZONTAL;
+        formatSelector.addItem("JPEG");
+        formatSelector.addItem("PNG");
+        formatSelector.addActionListener(new ActionListener()
+                {
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        String format = (String) formatSelector.getSelectedItem();
+                        
+                        if (format.equals("JPEG"))
+                        {
+                            Enumeration buttons = ExportFrame.this.qualityGroup.getElements();
+                            while (buttons.hasMoreElements())
+                            {
+                                AbstractButton b = (AbstractButton) buttons.nextElement();
+                                b.setEnabled(true);
+                            }
+                        }
+                        else
+                        {
+                            Enumeration buttons = ExportFrame.this.qualityGroup.getElements();
+                            while (buttons.hasMoreElements())
+                            {
+                                AbstractButton b = (AbstractButton) buttons.nextElement();
+                                b.setEnabled(false);
+                            }
+                        }
+                    }
+                }
+        );	
+        getContentPane().add(formatSelector, c);
+
+        // Image size label.
+        c.gridx = 0;
+        c.gridy = 1;
+        c.gridwidth = 1;
+        c.weighty = 1;
+        c.anchor = GridBagConstraints.EAST;
+        c.fill = GridBagConstraints.NONE;
+        getContentPane().add(new JLabel("Image size:"), c);
+        
+        // Image size boxes.
+        JPanel scalePanel = new JPanel();
+        scalePanel.setLayout(new GridBagLayout());
+        GridBagConstraints c2 = new GridBagConstraints();
+        
+        // Width label
+        c2.gridx = 0;
+        c2.gridy = 0;
+        scalePanel.add(new JLabel("Width:"), c2);
+        
+        // Width box
+        c2.gridx = 1;
+        c2.weightx = 1;
+        c2.insets = new Insets(0,0,0,4);
+        c2.fill = GridBagConstraints.HORIZONTAL;
+        scalePanel.add(widthBox,c2);
+        widthBox.setValue(new Integer(rrpanel.getWidth()));
+        widthBox.getDocument().addUndoableEditListener(new UndoableEditListener()
+                {
+                    public void undoableEditHappened(UndoableEditEvent evt)
+                    {
+                        try
+                        {
+                            Number n = FORMAT.parse(widthBox.getText());
+                            scaleWidth(n.intValue());
+                        }
+                        catch (ParseException e)
+                        {
+                            // Invalid value.
+                            return;
+                        }
+                    }
+                }
+        );
+
+        // Height label
+        c2.gridx = 2;
+        c2.weightx = 0;
+        c2.fill = GridBagConstraints.NONE;
+        c2.insets = new Insets(0,0,0,0);
+        scalePanel.add(new JLabel("Height:"),c2);
+
+        // Height box
+        c2.gridx = 3;
+        c2.weightx = 1;
+        c2.fill = GridBagConstraints.HORIZONTAL;
+        scalePanel.add(heightBox,c2);
+        heightBox.setEditable(false);
+        heightBox.setValue(new Integer(rrpanel.getHeight()));
+        
+        // Adding scale panel.
+        c.gridx = 1;
+        c.gridy = 1;
+        c.gridwidth = 2;
+        c.anchor = GridBagConstraints.WEST;
+        c.fill = GridBagConstraints.HORIZONTAL;
+        getContentPane().add(scalePanel, c);
+
+        // Quality label.
+        c.gridx = 0;
+        c.gridy = 2;
+        c.gridwidth = 1;
+        c.weighty = 0;
+        c.anchor = GridBagConstraints.EAST;
+        c.fill = GridBagConstraints.NONE;
+
+        getContentPane().add(new JLabel("Quality:"), c);
+        
+        // Quality options
+
+        c.gridx = 1;
+        c.gridy = 2;
+        c.gridwidth = 2;
+        c.anchor = GridBagConstraints.WEST;
+        c.fill = GridBagConstraints.NONE;
+        
+        
+        JRadioButton lowButton = new JRadioButton("Low", false);
+        lowButton.addActionListener(new ActionListener()
+                {
+
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        jpegQuality = 0.25f;
+                    }
+            
+                }
+        );
+        JRadioButton mediumButton = new JRadioButton("Medium", false);
+        mediumButton.addActionListener(new ActionListener()
+                {
+
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        jpegQuality = 0.5f;
+                    }
+            
+                }
+        );
+        JRadioButton highButton = new JRadioButton("High", true);
+        highButton.addActionListener(new ActionListener()
+                {
+
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        jpegQuality = 0.75f;
+                    }
+            
+                }
+        );
+        JRadioButton maximumButton = new JRadioButton("Maximum", false);
+        maximumButton.addActionListener(new ActionListener()
+                {
+
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        jpegQuality = 1;
+                    }
+            
+                }
+        );
+
+        qualityGroup.add(lowButton);
+        qualityGroup.add(mediumButton);
+        qualityGroup.add(highButton);
+        qualityGroup.add(maximumButton);
+        
+        getContentPane().add(lowButton, c);
+        c.gridy = 3;
+        getContentPane().add(mediumButton,c);
+        c.gridy = 4;
+        getContentPane().add(highButton,c);
+        c.gridy = 5;
+        getContentPane().add(maximumButton,c);
+        
+        // File label.
+        c.gridx = 0;
+        c.gridy = 6;
+        c.gridwidth = 1;
+        c.fill = GridBagConstraints.NONE;
+        c.anchor = GridBagConstraints.SOUTHEAST;
+        c.weighty = 0;
+        getContentPane().add(new JLabel("Output file:"), c);
+        
+        // File text box
+        c.gridx = 1;
+        c.gridy = 6;
+        c.gridwidth = 1;
+        c.weighty = 1;
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.anchor = GridBagConstraints.SOUTHWEST;
+        c.weightx = 1;
+        getContentPane().add(outputFile, c);
+        
+        // File browse button.
+        JButton fileButton = new JButton("Browse...");
+        fileButton.addActionListener(new ActionListener()
+                {
+
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        int ret = fc.showDialog(ExportFrame.this, "Select");
+                        if (ret == JFileChooser.APPROVE_OPTION)
+                        {
+                            File f = fc.getSelectedFile();
+                            outputFile.setText(f.getAbsolutePath());
+                        }
+                    }
+                }
+        );
+        c.gridx = 2;
+        c.gridy = 6;
+        c.gridwidth = 1;
+        c.fill = GridBagConstraints.NONE;
+        c.anchor = GridBagConstraints.SOUTHWEST;
+        c.weightx = 0;
+        getContentPane().add(fileButton, c);
+        
+        // Export button.
+        JPanel buttonPanel = new JPanel();
+        
+        JButton exportButton = new JButton("Export");
+        exportButton.addActionListener(new ActionListener()
+                {
+        
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        doExport();
+                    }
+            
+                }
+        );
+        
+        buttonPanel.add(exportButton);
+        
+        JButton cancelButton = new JButton("Cancel");
+        cancelButton.addActionListener(new ActionListener()
+                {
+        
+                    public void actionPerformed(ActionEvent e)
+                    {
+                        setVisible(false);
+                    }
+                }
+        );
+        
+        buttonPanel.add(cancelButton);
+        
+        c.gridx = 0;
+        c.gridy = 7;
+        c.gridwidth = 3;
+        c.weighty = 0;
+        c.fill = GridBagConstraints.NONE;
+        c.anchor = GridBagConstraints.SOUTHEAST;
+
+        getContentPane().add(buttonPanel, c);
+    }
+    
+    private void doExport()
+    {
+        String format = (String) formatSelector.getSelectedItem();
+    	File f = getFileWithExtension(outputFile.getText(), format);
+        export(f, scale, format);
+    }
+    
+    private File getFileWithExtension(String fileName, String format)
+    {
+    	String lastFour = "";
+    	String lastFive = "";
+    	if(fileName.length() >= 4)
+    		lastFour = fileName.substring(fileName.length()-4);
+    	if(fileName.length() >= 5)
+    		lastFive = fileName.substring(fileName.length()-5);
+    	if(format.equalsIgnoreCase("JPEG"))
+    	{
+    		if(!lastFive.equalsIgnoreCase(".jpeg") && 
+    				!lastFour.equalsIgnoreCase(".jpg") )
+    			fileName += ".jpg";
+    	}else if(format.equalsIgnoreCase("PNG"))
+    	{
+    		if(	!lastFour.equalsIgnoreCase(".png") )
+    			fileName += ".png";
+    	}
+
+    	return new File(fileName);
+    }
+    private void export(File outputFile, double scale, String formatName)
+    {
+        if (outputFile.exists())
+        {
+            int result = JOptionPane.showConfirmDialog(this, "The file " + outputFile + " already exists.  Overwrite?", "File exists", JOptionPane.YES_NO_OPTION);
+            if (result == JOptionPane.NO_OPTION)
+            {
+                return;
+            }
+        }
+        
+        BufferedImage img = new BufferedImage((int) (rrpanel.getWidth() * scale), (int) (rrpanel.getHeight() * scale), BufferedImage.TYPE_INT_RGB);
+        
+        Graphics2D g2 = (Graphics2D) img.getGraphics();
+        
+        g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+        g2.scale(scale, scale);
+        
+        g2.setRenderingHint(MauveRenderingHints.KEY_SIMILARITY_DENSITY, new Double(1.0 / scale));
+        rrpanel.paint(g2);
+
+        
+        
+        if (formatName.equals("JPEG"))
+        {
+            try
+            {
+                writeJpeg(outputFile, img);
+            }
+            catch (IOException e)
+            {
+    			JOptionPane.showMessageDialog(this, "Error writing file.", "Error Writing File", JOptionPane.ERROR_MESSAGE);
+                e.printStackTrace();
+                return;
+            }        
+        }
+        else
+        {
+            try
+            {
+                ImageIO.write(img, formatName, outputFile);
+            }
+            catch (IOException e)
+            {
+    			JOptionPane.showMessageDialog(this, "The selected file could not be written.  Please choose another.", "Error Writing File", JOptionPane.ERROR_MESSAGE);
+    			MyConsole.err().println(e);
+    			return;
+            }
+        }
+        
+        setVisible(false);
+    }
+    
+    
+    /**
+     * @param outputFile
+     * @param img
+     * @throws IOException
+     */
+    private void writeJpeg(File outputFile, BufferedImage img) throws IOException
+    {
+        Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
+        if (!iter.hasNext())
+        {
+            throw new RuntimeException("Couldn't find jpeg image writer.");
+        }
+        
+        ImageWriter writer = (ImageWriter)iter.next();
+
+        // Prepare output file
+        ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile);
+        writer.setOutput(ios);
+
+        // Set the compression quality
+        JPEGImageWriteParam param = new JPEGImageWriteParam(Locale.getDefault());
+        param.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT) ;
+        param.setCompressionQuality(jpegQuality);
+
+        // Write the image
+        writer.write(null, new IIOImage(img, null, null), param);
+
+        // Cleanup
+        ios.flush();
+        writer.dispose();
+        ios.close();
+    }
+
+    private void scaleWidth(int width)
+    {
+        scale = width / (double) rrpanel.getWidth(); 
+        heightBox.setValue(new Integer((int) (rrpanel.getHeight() * scale)));
+    }
+    
+}
diff --git a/src/org/gel/mauve/gui/ExportMenu.java b/src/org/gel/mauve/gui/ExportMenu.java
new file mode 100644
index 0000000..1496e81
--- /dev/null
+++ b/src/org/gel/mauve/gui/ExportMenu.java
@@ -0,0 +1,166 @@
+package org.gel.mauve.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import javax.swing.JFileChooser;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.KeyStroke;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.analysis.OneToOneOrthologExporter;
+import org.gel.mauve.analysis.PermutationExporter;
+import org.gel.mauve.analysis.SnpExporter;
+
+public class ExportMenu extends JMenu implements ActionListener {
+
+    JMenuItem jMenuFileExportImage = new JMenuItem();
+	JMenuItem jMenuFileExportSnps = new JMenuItem();
+	JMenuItem jMenuFileExportSpas = new JMenuItem();
+	JMenuItem jMenuFileExportOrthologs = new JMenuItem();
+	JMenuItem jMenuFileExportPermutation = new JMenuItem();
+	JMenuItem jMenuFileExportGaps = new JMenuItem();
+
+	BaseViewerModel model;
+	RearrangementPanel rrpanel;
+    public ExportMenu()
+    {
+        jMenuFileExportImage.setToolTipText("Export graphics to file...");
+        jMenuFileExportImage.setVisible(true);
+        jMenuFileExportImage.setText("Export Image...");
+        jMenuFileExportImage.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK));
+        jMenuFileExportImage.setMnemonic('E');
+        jMenuFileExportImage.setActionCommand("ExportImage");
+        jMenuFileExportImage.addActionListener(this);
+
+        jMenuFileExportSnps.setToolTipText("Export a tabular listing of polymorphic sites and their locations site");
+    	jMenuFileExportSnps.setVisible(true);
+    	jMenuFileExportSnps.setText("Export SNPs");
+    	jMenuFileExportSnps.setMnemonic('p');
+    	jMenuFileExportSnps.setActionCommand("ExportSNPs");
+        jMenuFileExportSnps.addActionListener(this);
+
+    	jMenuFileExportSpas.setToolTipText("Export a listing of segmental presence/absence patterns -- large indels");
+    	jMenuFileExportSpas.setVisible(true);
+    	jMenuFileExportSpas.setText("Export Islands");
+    	jMenuFileExportSpas.setMnemonic('i');
+    	jMenuFileExportSpas.setActionCommand("ExportIslands");
+    	jMenuFileExportSpas.addActionListener(this);
+
+    	jMenuFileExportOrthologs.setToolTipText("Export annotated 1-to-1 orthologs");
+    	jMenuFileExportOrthologs.setVisible(true);
+    	jMenuFileExportOrthologs.setText("Export Orthologs");
+    	jMenuFileExportOrthologs.setMnemonic('i');
+    	jMenuFileExportOrthologs.setActionCommand("ExportOrthologs");
+    	jMenuFileExportOrthologs.addActionListener(this);
+
+    	jMenuFileExportPermutation.setToolTipText("Export a signed gene-order permutation matrix");
+    	jMenuFileExportPermutation.setVisible(true);
+    	jMenuFileExportPermutation.setText("Export Permutation");
+    	jMenuFileExportPermutation.setMnemonic('P');
+    	jMenuFileExportPermutation.setActionCommand("ExportPermutation");
+    	jMenuFileExportPermutation.addActionListener(this);
+    	
+    	jMenuFileExportGaps.setToolTipText("Export locations of gaps in alignment.");
+    	jMenuFileExportGaps.setVisible(true);
+    	jMenuFileExportGaps.setText("Export Gaps");
+    	jMenuFileExportGaps.setMnemonic('i');
+    	jMenuFileExportGaps.setActionCommand("ExportGaps");
+    	jMenuFileExportGaps.addActionListener(this);
+
+        jMenuFileExportImage.setEnabled(false);
+		jMenuFileExportSnps.setEnabled(false);
+		jMenuFileExportSpas.setEnabled(false);
+		jMenuFileExportPermutation.setEnabled(false);
+		jMenuFileExportOrthologs.setEnabled(false);
+		jMenuFileExportGaps.setEnabled(false);
+		
+        add(jMenuFileExportImage);
+        add(jMenuFileExportSnps);
+//        add(jMenuFileExportSpas);
+        add(jMenuFileExportPermutation);
+        add(jMenuFileExportOrthologs);
+        add(jMenuFileExportGaps);
+
+    }
+    
+    public void setTarget(BaseViewerModel model, RearrangementPanel rrpanel)
+    {
+    	this.model = model;
+    	this.rrpanel = rrpanel;
+    	if(model instanceof XmfaViewerModel)
+    	{
+    		if(((XmfaViewerModel)model).getBackboneList()!=null)
+    		{
+    			jMenuFileExportOrthologs.setEnabled(true);
+    			jMenuFileExportSpas.setEnabled(true);
+    			jMenuFileExportSnps.setEnabled(true);
+    			jMenuFileExportGaps.setEnabled(true);
+    		}
+       		jMenuFileExportPermutation.setEnabled(true);
+    	}else{
+			jMenuFileExportSnps.setEnabled(false);
+			jMenuFileExportSpas.setEnabled(false);
+       		jMenuFileExportPermutation.setEnabled(false);
+			jMenuFileExportOrthologs.setEnabled(false);
+    	}
+        jMenuFileExportImage.setEnabled(true);
+    }
+    
+	public void actionPerformed(ActionEvent e) {
+        if (e.getActionCommand().equals("ExportImage"))
+        {
+            ExportFrame exportFrame = new ExportFrame(rrpanel);
+            exportFrame.setVisible(true);
+        }
+        if (e.getActionCommand().equals("ExportOrthologs"))
+        {
+        	XmfaViewerModel xvm = (XmfaViewerModel)model;
+        	OneToOneOrthologExporter.ExportFrame pef = new OneToOneOrthologExporter.ExportFrame(xvm);
+        }
+        if (e.getActionCommand().equals("ExportSNPs"))
+        {
+        	JFileChooser fc = new JFileChooser();
+        	fc.setDialogTitle("Export SNP file to...");
+            if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION)
+            {
+            	try{
+            	BufferedWriter bw = new BufferedWriter( new FileWriter(fc.getSelectedFile()));
+            	XmfaViewerModel xvm = (XmfaViewerModel)model;
+    			SnpExporter.export(xvm, xvm.getXmfa(), bw);
+    			bw.flush();
+    			bw.close();
+            	}catch(IOException ioe){ioe.printStackTrace();}
+            }
+        }
+        if (e.getActionCommand().equals("ExportPermutation"))
+        {
+        	XmfaViewerModel xvm = (XmfaViewerModel)model;
+        	PermutationExporter.ExportFrame pef = new PermutationExporter.ExportFrame(xvm);
+        }
+        if (e.getActionCommand().equals("ExportGaps")){
+        	JFileChooser fc = new JFileChooser();
+        	fc.setDialogTitle("Export Gap file to...");
+        	if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION){
+        		try {
+        			BufferedWriter bw = new BufferedWriter(new FileWriter(fc.getSelectedFile()));
+        			XmfaViewerModel xvm = (XmfaViewerModel) model;
+        			SnpExporter.exportGaps(xvm, xvm.getXmfa(), bw);
+        			bw.flush();
+        			bw.close();
+        		} catch (IOException ioe) {
+        			ioe.printStackTrace();
+        		}
+        	}
+        }
+		
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/FillLayout.java b/src/org/gel/mauve/gui/FillLayout.java
new file mode 100644
index 0000000..d17ad89
--- /dev/null
+++ b/src/org/gel/mauve/gui/FillLayout.java
@@ -0,0 +1,85 @@
+package org.gel.mauve.gui;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+
+public class FillLayout implements LayoutManager {
+
+	public void addLayoutComponent (String name, Component comp) {
+	}
+
+	public void removeLayoutComponent (Component comp) {
+	}
+
+	public Dimension preferredLayoutSize (Container parent) {
+		synchronized (parent.getTreeLock ()) {
+			// Find the maximum preferred width and height.
+
+			Insets insets = parent.getInsets ();
+			int ncomponents = parent.getComponentCount ();
+
+			int w = 0;
+			int h = 0;
+
+			for (int i = 0; i < ncomponents; i++) {
+				Component comp = parent.getComponent (i);
+				Dimension d = comp.getPreferredSize ();
+				if (w < d.width) {
+					w = d.width;
+				}
+				if (h < d.height) {
+					h = d.height;
+				}
+			}
+			return new Dimension (insets.left + insets.right + w, insets.top
+					+ insets.bottom + h);
+		}
+	}
+
+	public Dimension minimumLayoutSize (Container parent) {
+		synchronized (parent.getTreeLock ()) {
+			// Find the maximum preferred width and height.
+
+			Insets insets = parent.getInsets ();
+			int ncomponents = parent.getComponentCount ();
+
+			int w = 0;
+			int h = 0;
+
+			for (int i = 0; i < ncomponents; i++) {
+				Component comp = parent.getComponent (i);
+				Dimension d = comp.getMinimumSize ();
+				if (w > d.width) {
+					w = d.width;
+				}
+				if (h < d.height) {
+					h = d.height;
+				}
+			}
+			return new Dimension (insets.left + insets.right + w, insets.top
+					+ insets.bottom + h);
+		}
+	}
+
+	public void layoutContainer (Container parent) {
+		synchronized (parent.getTreeLock ()) {
+			Insets insets = parent.getInsets ();
+			int ncomponents = parent.getComponentCount ();
+
+			if (ncomponents == 0) {
+				return;
+			}
+
+			int w = parent.getWidth () - (insets.left + insets.right);
+			int h = parent.getHeight () - (insets.top + insets.bottom);
+
+			for (int i = 0; i < ncomponents; i++) {
+				parent.getComponent (i).setBounds (insets.left, insets.top, w,
+						h);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/FrameLoader.java b/src/org/gel/mauve/gui/FrameLoader.java
new file mode 100644
index 0000000..845733a
--- /dev/null
+++ b/src/org/gel/mauve/gui/FrameLoader.java
@@ -0,0 +1,102 @@
+package org.gel.mauve.gui;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.MauveFormatException;
+import org.gel.mauve.ModelBuilder;
+
+public class FrameLoader implements Runnable {
+	private MauveFrame frame;
+
+	private File file;
+
+	private URL url;
+
+	private String sequenceID;
+
+	private long start;
+
+	private long end;
+
+	private String auth_token;
+	
+	private String contig;
+
+	public FrameLoader (MauveFrame frame, URL url) {
+		this.frame = frame;
+		this.url = url;
+	}
+
+	public FrameLoader (MauveFrame frame, File file) {
+		this.frame = frame;
+		this.file = file;
+	}
+
+	public FrameLoader (MauveFrame frame, URL url, String sequenceID,
+			long start, long end, String auth_token, String contig) {
+		this.frame = frame;
+		this.url = url;
+		this.sequenceID = sequenceID;
+		this.start = start;
+		this.end = end;
+		this.auth_token = auth_token;
+		this.contig = contig;
+		
+	}
+
+	public void run () {
+		if (url != null) {
+			loadURL ();
+		} else {
+			loadFile ();
+		}
+	}
+
+	private void loadURL () {
+		try {
+			final BaseViewerModel model = ModelBuilder.buildModel (url,
+					auth_token, frame);
+
+			if (sequenceID != null) {
+				model.setFocus (sequenceID, start, end, contig);
+			}
+
+			SwingUtilities.invokeLater (new Runnable () {
+				public void run () {
+					frame.setModel (model);
+				}
+			});
+		} catch (IOException e) {
+			// TODO A better error.
+			e.printStackTrace ();
+		} catch (MauveFormatException e) {
+			// TODO A better error.
+			e.printStackTrace ();
+		}
+
+	}
+
+	private void loadFile () {
+		try {
+			final BaseViewerModel model = ModelBuilder.buildModel (file, frame);
+			SwingUtilities.invokeLater (new Runnable () {
+				public void run () {
+					frame.setModel (model);
+				}
+			});
+		} catch (IOException e) {
+			JOptionPane.showMessageDialog(null, "An error occurred when reading the alignment file.\n" + e.getMessage(), "An error occurred", JOptionPane.ERROR_MESSAGE);
+			e.printStackTrace ();
+		} catch (MauveFormatException e) {
+			JOptionPane.showMessageDialog(null, "An error occurred when reading the alignment file.\n" + e.getMessage(), "An error occurred", JOptionPane.ERROR_MESSAGE);
+			e.printStackTrace ();
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/GenomeCellRenderer.java b/src/org/gel/mauve/gui/GenomeCellRenderer.java
new file mode 100644
index 0000000..5e69588
--- /dev/null
+++ b/src/org/gel/mauve/gui/GenomeCellRenderer.java
@@ -0,0 +1,26 @@
+package org.gel.mauve.gui;
+
+import java.awt.Component;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+
+import org.gel.mauve.Genome;
+
+public class GenomeCellRenderer {
+
+	public static ListCellRenderer getListCellRenderer () {
+		return new DefaultListCellRenderer () {
+
+			public Component getListCellRendererComponent (JList list,
+					Object val, int index, boolean sel, boolean focus) {
+				if (val instanceof Genome)
+					val = ((Genome) val).getDisplayName ();
+				return super.getListCellRendererComponent (list, val, index,
+						sel, focus);
+			}
+
+		};
+	}
+}
diff --git a/src/org/gel/mauve/gui/HintMessageEvent.java b/src/org/gel/mauve/gui/HintMessageEvent.java
new file mode 100644
index 0000000..13feb38
--- /dev/null
+++ b/src/org/gel/mauve/gui/HintMessageEvent.java
@@ -0,0 +1,17 @@
+package org.gel.mauve.gui;
+
+import java.util.EventObject;
+
+public class HintMessageEvent extends EventObject{
+	static final long serialVersionUID = 2342334;
+	String message;
+	public HintMessageEvent( RearrangementPanel source, String message )
+	{
+		super(source);
+		this.message = message;
+	}
+	String getMessage()
+	{
+		return message;
+	}
+}
diff --git a/src/org/gel/mauve/gui/HintMessageListener.java b/src/org/gel/mauve/gui/HintMessageListener.java
new file mode 100644
index 0000000..74a794b
--- /dev/null
+++ b/src/org/gel/mauve/gui/HintMessageListener.java
@@ -0,0 +1,7 @@
+package org.gel.mauve.gui;
+
+import java.util.EventListener;
+
+public interface HintMessageListener extends EventListener {
+	public void messageChanged(HintMessageEvent hme);
+}
diff --git a/src/org/gel/mauve/gui/LCBStatusBar.java b/src/org/gel/mauve/gui/LCBStatusBar.java
new file mode 100644
index 0000000..f8408dd
--- /dev/null
+++ b/src/org/gel/mauve/gui/LCBStatusBar.java
@@ -0,0 +1,195 @@
+package org.gel.mauve.gui;
+
+import java.awt.Dimension;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.HighlightListener;
+import org.gel.mauve.LCB;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.Match;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.XmfaViewerModel;
+
+
+/**
+ * This class displays statistics about LCBs in a status bar
+ */
+public class LCBStatusBar extends JPanel implements HighlightListener, HintMessageListener
+{
+    JLabel hint_bar = new JLabel(" ");
+    JLabel LCB_length_status = new JLabel(" ");
+    JLabel LCB_weight_status = new JLabel(" ");
+    JLabel segment_name = new JLabel(" ");
+    JLabel segment_loc = new JLabel(" ");
+    private BaseViewerModel model;
+
+    LCBStatusBar()
+    {
+        // set tooltip text for components
+        //			hint_bar.setTooltipText("");
+        LCB_length_status.setToolTipText("The length (in nucleotides) of a collinear segment");
+        LCB_weight_status.setToolTipText("The alignment weight of a collinear segment");
+        segment_name.setToolTipText("The chromosome or contig name");
+        segment_loc.setToolTipText("The current coordinate within the chromosome or contig");
+
+        // layout the components
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+        setMaximumSize(new Dimension(Short.MAX_VALUE, 15));
+        setBorder(BorderFactory.createEtchedBorder());
+
+        add(hint_bar);
+        add(Box.createHorizontalGlue());
+
+        segment_name.setBorder(BorderFactory.createLoweredBevelBorder());
+        Dimension pref_dim = segment_name.getPreferredSize();
+        pref_dim.setSize(150, pref_dim.getHeight());
+        segment_name.setPreferredSize(pref_dim);
+        add(segment_name);
+
+        add(Box.createHorizontalStrut(2));
+
+        segment_loc.setBorder(BorderFactory.createLoweredBevelBorder());
+        pref_dim = segment_loc.getPreferredSize();
+        pref_dim.setSize(150, pref_dim.getHeight());
+        segment_loc.setPreferredSize(pref_dim);
+        add(segment_loc);
+
+        add(Box.createHorizontalStrut(2));
+
+        LCB_length_status.setBorder(BorderFactory.createLoweredBevelBorder());
+        pref_dim = LCB_length_status.getPreferredSize();
+        pref_dim.setSize(150, pref_dim.getHeight());
+        LCB_length_status.setPreferredSize(pref_dim);
+        add(LCB_length_status);
+
+        add(Box.createHorizontalStrut(2));
+
+        LCB_weight_status.setBorder(BorderFactory.createLoweredBevelBorder());
+        pref_dim = LCB_weight_status.getPreferredSize();
+        pref_dim.setSize(150, pref_dim.getHeight());
+        LCB_weight_status.setPreferredSize(pref_dim);
+        add(LCB_weight_status);
+
+        add(Box.createHorizontalStrut(1));
+    }
+    
+    public void setModel(BaseViewerModel model)
+    {
+        this.model = model;
+    }
+
+    private void setWeightStatus(long weight)
+    {
+        LCB_weight_status.setText(" LCB weight: " + weight);
+    }
+
+    private void clearWeightStatus()
+    {
+        LCB_weight_status.setText(" ");
+    }
+
+    private void setLengthStatus(long length)
+    {
+        LCB_length_status.setText(" LCB length: " + length);
+    }
+
+    private void clearLengthStatus()
+    {
+        LCB_length_status.setText(" ");
+    }
+
+    private void setLocationStatus(String name, long coordinate)
+    {
+        segment_name.setText(name);
+        segment_loc.setText(new Long(coordinate).toString());
+    }
+
+    private void clearLocationStatus()
+    {
+        segment_name.setText(" ");
+        segment_loc.setText(" ");
+    }
+
+    public void messageChanged(HintMessageEvent hme)
+    {
+    	hint_bar.setText(hme.getMessage());
+    }
+
+    public void setHint(String hint)
+    {
+        hint_bar.setText(hint);
+    }
+
+    public void clearHint()
+    {
+        hint_bar.setText("");
+    }
+
+    /** clears all text shown in the toolbar */
+    public void clear()
+    {
+        clearHint();
+        clearLocationStatus();
+        clearLengthStatus();
+        clearWeightStatus();
+    }
+
+    public void highlightChanged(ModelEvent evt)
+    {
+        clearWeightStatus();
+        clearLengthStatus();
+        clearLocationStatus();
+        
+        if (model != null)
+        {
+            Genome highlight = model.getHighlightGenome();
+            
+            if (!(model instanceof XmfaViewerModel) && (model instanceof LcbViewerModel))
+            {
+                LcbViewerModel lm = (LcbViewerModel) model;
+                Match lastMatch = lm.lastMatchHighlight();
+                if (lastMatch != null)
+                {
+                    LCB cur_lcb = lm.getVisibleLcb(lastMatch.lcb);
+                    setWeightStatus(cur_lcb.weight);
+                    long length = cur_lcb.getRightEnd(highlight)- cur_lcb.getLeftEnd(highlight);
+                    setLengthStatus(length);
+                }
+            }
+
+            if (model instanceof LcbViewerModel)
+            {
+                List lcbs = ((LcbViewerModel) model).getLCBRange(highlight, model.getHighlightCoordinate(), model.getHighlightCoordinate());
+
+                if (lcbs != null)
+                {
+                    Iterator i = lcbs.iterator();
+                    if (i.hasNext())
+                    {
+                        LCB cur_lcb = (LCB) i.next();
+                        setWeightStatus(cur_lcb.weight);
+                        setLengthStatus(cur_lcb.getLength(highlight));
+                    }
+                }
+            }
+            
+            Chromosome ch = highlight.getChromosomeAt(model.getHighlightCoordinate());
+            if (ch != null)
+            {
+                setLocationStatus(ch.getName(), ch.relativeLocation(model.getHighlightCoordinate()));
+            }
+        }
+
+        repaint();
+    }
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/LcbLinePanel.java b/src/org/gel/mauve/gui/LcbLinePanel.java
new file mode 100644
index 0000000..aff742d
--- /dev/null
+++ b/src/org/gel/mauve/gui/LcbLinePanel.java
@@ -0,0 +1,283 @@
+package org.gel.mauve.gui;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.Stroke;
+
+import javax.swing.JComponent;
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.LCB;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.ModelListener;
+import org.gel.mauve.gui.sequence.RRSequencePanel;
+import org.gel.mauve.gui.sequence.SeqPanel;
+
+/**
+ * This class is intended to overlay a RearrangementPanel with lines that
+ * connect each locally collinear block. It reads the LCB boundaries and the
+ * current view range from various classes contained by a RearrangementPanel. A
+ * very, very dirty hack.
+ */
+class LcbLinePanel extends JComponent implements ModelListener
+{
+    boolean[] highlighted; // The LCBs that should be highlighted
+
+    RearrangementPanel rrpanel;
+
+    int menubar_height;
+
+    boolean draw_strikethrough = true;
+    
+    boolean hidden = false;
+    
+    boolean getHidden(){ return hidden; }
+    void setHidden(boolean hidden)
+    {
+    	if(this.hidden != hidden)
+    		setVisible(!hidden);
+    	this.hidden = hidden; 
+    }
+
+    LcbViewerModel model;
+
+    LcbLinePanel(RearrangementPanel rrpanel, LcbViewerModel model)
+    {
+        this.rrpanel = rrpanel;
+        this.model = model;
+        this.menubar_height = 0;
+        highlighted = new boolean[model.getVisibleLcbCount()];
+        for (int hI = 0; hI < highlighted.length; hI++)
+        {
+            highlighted[hI] = true;
+        }
+        model.addModelListener(this);
+    }
+
+    public void paintComponent(Graphics g)
+    {
+        paint(g);
+    }
+    
+    int nextVisibleGenome(LCB lcb, int fromSeq)
+    {
+    	int seqJ = fromSeq;
+    	for (; seqJ < model.getSequenceCount(); seqJ++)
+        {
+    		Genome lg = model.getGenomeByViewingIndex(seqJ);
+    		if(lcb.getLeftEnd(lg) != 0 && lg.getVisible())
+    			break;
+        }
+    	return seqJ;
+    }
+
+    /**
+     * Extract the pixel coordinates of visible LCBs and draw lines that connect
+     * them.
+     */
+    public void paint(Graphics g1d)
+    {
+        Graphics2D g2d = (Graphics2D) g1d;
+        g2d.setColor(Color.black);
+        Rectangle bounds = getBounds();
+        
+        int[] boxTops = new int[model.getSequenceCount()];
+        int[] boxHeights = new int[model.getSequenceCount()];
+        Rectangle boxBounds[] = new Rectangle[model.getSequenceCount()];
+        Genome genomes[] = new Genome[model.getSequenceCount()];
+        SeqPanel seqpanels[] = new SeqPanel[model.getSequenceCount()];
+        RRSequencePanel rrseqpanels[] = new RRSequencePanel[model.getSequenceCount()];
+        int clipx = 0;
+        for (int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+        {
+            genomes[seqI] = model.getGenomeByViewingIndex(seqI);
+            seqpanels[seqI] = rrpanel.getNewPanel(seqI);
+            rrseqpanels[seqI] = seqpanels[seqI].getSequencePanel();
+
+            boxTops[seqI] = rrseqpanels[seqI].boxTop() + 1;
+            boxHeights[seqI] = rrseqpanels[seqI].boxHeight() + 1;
+
+            boxBounds[seqI] = rrseqpanels[seqI].getBounds();
+            boxBounds[seqI].x += seqpanels[seqI].getBounds().x;
+            boxBounds[seqI].y += seqpanels[seqI].getBounds().y;
+            clipx = seqpanels[seqI].getControlPanel().getWidth();
+        }
+        g2d.clipRect(clipx, 0, this.getWidth(), this.getHeight());
+
+        for (int lcbI = 0; lcbI < model.getVisibleLcbCount(); lcbI++)
+        {
+            
+            if (!highlighted[lcbI])
+                continue;
+
+            LCB lcb = model.getVisibleLcb(lcbI);
+
+            int firstVisible = nextVisibleGenome(lcb,0);
+            int seqJ = model.getSequenceCount();
+            for (int seqI = firstVisible; seqI + 1 < model.getSequenceCount(); seqI = seqJ)
+            {
+            	Genome g = model.getGenomeByViewingIndex(seqI);
+            	seqJ = nextVisibleGenome(lcb,seqI+1);
+            	if(seqJ == model.getSequenceCount())
+            		continue;
+
+            	Genome upperGenome = model.getGenomeByViewingIndex(seqI);
+	            Genome lowerGenome = model.getGenomeByViewingIndex(seqJ);
+	            
+	            RRSequencePanel upperPanel = rrseqpanels[seqI];
+	            RRSequencePanel lowerPanel = rrseqpanels[seqJ];
+	
+	            int upperBoxTop = boxTops[seqI];
+	            int upperBoxHeight = boxHeights[seqI];
+	
+	            int lowerBoxTop = boxTops[seqJ];
+	            int lowerBoxHeight = boxHeights[seqJ];
+	
+	            // Here's what needs to be fixed.
+	
+	            // Translate bounds, since RRSequencePanels are contained in
+	            // SeqPanels.
+	            Rectangle upperBounds = boxBounds[seqI];
+	            Rectangle lowerBounds = boxBounds[seqJ];
+
+                Color base_color = Color.BLACK;
+                if (lcb.color != null)
+                {
+                	base_color = lcb.color;
+                }
+                g2d.setColor(base_color);
+                int upperMidpoint = upperPanel.sequenceCoordinateToCenterPixel(lcb.midpoint(upperGenome));
+                int lowerMidpoint = lowerPanel.sequenceCoordinateToCenterPixel(lcb.midpoint(lowerGenome));
+
+                upperMidpoint += upperBounds.x;
+                lowerMidpoint += lowerBounds.x;
+                // draw the connecting bars if at least one of the LCBs is
+                // visible
+                if (barVisible(bounds, upperMidpoint, lowerMidpoint))
+                {
+                    
+
+                    if (draw_strikethrough)
+                    {
+                        if (seqI == firstVisible)
+                        {
+                            if (!lcb.getReverse(upperGenome))
+                            {
+                                g2d.drawLine(upperMidpoint, upperBounds.y + upperBoxTop + upperBoxHeight / 2, upperMidpoint, upperBounds.y + upperBoxTop + upperBoxHeight);
+                            }
+                        }
+                        else
+                        {
+                            int y_offset = lcb.getReverse(lowerGenome) ? 0 : upperBoxHeight / 2;
+                            g2d.drawLine(upperMidpoint, upperBounds.y + upperBoxTop + y_offset, upperMidpoint, upperBounds.y + upperBoxTop + y_offset + upperBoxHeight / 2);
+                        }
+                    }
+                                        
+                    if (draw_strikethrough)
+                    {
+                        // If there are more genomes below the lower genome, the strikethrough line extends beyond the bottom of the LCB
+                    	int seqK = nextVisibleGenome(lcb,seqJ+1);
+                    	if(seqK < model.getSequenceCount())
+                        {
+                            int y_offset = lcb.getReverse(lowerGenome) ? 0 : lowerBoxHeight / 2;
+                            g2d.drawLine(lowerMidpoint, lowerBounds.y + lowerBoxTop + y_offset, lowerMidpoint, lowerBounds.y + lowerBoxTop + y_offset + lowerBoxHeight / 2);
+                        }
+                        else if (lcb.getReverse(lowerGenome))
+                        {
+                            g2d.drawLine(lowerMidpoint, lowerBounds.y + lowerBoxTop, lowerMidpoint, lowerBounds.y + lowerBoxTop + lowerBoxHeight / 2);
+                        }
+                    }
+                    g2d.drawLine(upperMidpoint, upperBounds.y + upperBoxTop + upperBoxHeight, lowerMidpoint, lowerBounds.y + lowerBoxTop);
+                }
+            }
+        }
+    }
+
+    /**
+     * @param bounds
+     * @param start
+     * @param end
+     * @return
+     */
+    private boolean barVisible(Rectangle bounds, int start, int end)
+    {
+        return (start > 0 && start < bounds.width) || (end > 0 && end < bounds.width);
+    }
+
+    public void colorChanged(ModelEvent event)
+    {
+        // Color scheme doesn't affect lcb lines.
+    }
+
+    public void weightChanged(ModelEvent event)
+    {
+        repaint();
+    }
+
+    public void drawingSettingsChanged(ModelEvent event)
+    {
+        // Box fill etc doesn't affect lines.
+    }
+
+    public void modeChanged(ModelEvent event)
+    {
+        // Mode doesn't affect lines
+    }
+    
+    public void viewableRangeChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void viewableRangeChangeStart(ModelEvent event)
+    {
+    	if(!getHidden())
+    		setVisible(false);
+    }
+
+    public void viewableRangeChangeEnd(ModelEvent event)
+    {
+    	if(!getHidden())
+    		setVisible(true);
+    }
+
+    public void modelReloadStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void modelReloadEnd(ModelEvent event)
+    {
+        repaint();
+    }
+
+    public void genomesReordered(ModelEvent event)
+    {
+        repaint();
+    }
+    
+    public void referenceChanged(ModelEvent event)
+    {
+        repaint();
+    }
+
+    public void genomeVisibilityChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void printingStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void printingEnd(ModelEvent event)
+    {
+        // Ignored.
+    }
+    public void attributesChanged(ModelEvent event) {}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/Mauve.java b/src/org/gel/mauve/gui/Mauve.java
new file mode 100644
index 0000000..5c76567
--- /dev/null
+++ b/src/org/gel/mauve/gui/Mauve.java
@@ -0,0 +1,379 @@
+/**
+ * Mauve.java
+ * 
+ * Title: Mauve Description: Viewer for multiple genome alignments and
+ * annotation
+ * 
+ * @author Aaron Darling
+ * @author Paul Infield-Harm
+ * @version
+ */
+
+package org.gel.mauve.gui;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Vector;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.gel.mauve.MyConsole;
+import org.gel.mauve.assembly.ScoreAssembly;
+import org.gel.mauve.contigs.ContigOrderer;
+import org.gel.mauve.remote.RemoteControlImpl;
+
+public class Mauve {
+	static Properties props = new Properties ();
+	static String about_message = "";
+
+	private MauveFrame availableFrame;
+	protected Vector<MauveFrame> frames; // List of open frames
+	protected boolean check_updates = true;
+
+	protected Mauve () {
+	}
+
+	// Main entry point
+	static public void main (String [] args) {
+		if (args.length > 1) {
+			String firstArg = args[0];
+			if (firstArg.equalsIgnoreCase("ScoreAssembly"))
+				ScoreAssembly.main(trimFirstArg(args));
+			else if (firstArg.equalsIgnoreCase("OneToOneOrthologExporter"))
+				System.exit(-1);
+			else if (firstArg.equalsIgnoreCase("ContigOrderer")){			
+				ContigOrderer.main(trimFirstArg(args));
+			}
+		} else {
+			mainHook (args, new Mauve ());
+		}
+	}
+	
+	private static String[] trimFirstArg(String[] args){
+		String[] tmp = new String[args.length-1];
+		System.arraycopy(args, 1, tmp, 0, tmp.length);
+		return tmp;
+	}
+ 	
+	public static void mainHook (String args [], final Mauve mv) {
+		if (args.length >= 1) {
+			final String filename = args[0];
+			javax.swing.SwingUtilities.invokeLater (new Runnable () {
+				public void run () {
+					mv.init (filename);
+				}
+			});
+		} else {
+			javax.swing.SwingUtilities.invokeLater (new Runnable () {
+				public void run () {
+					mv.init ();
+				}
+			});
+		}
+	}
+	
+	public MauveFrame getFrame(){
+		return availableFrame;
+	}
+
+	public void init (String filename) {
+		init ();
+		if (filename.indexOf ("://") == -1) {
+			loadFile (new File (filename));
+		} else
+			// this looks like a URL, try to load it
+			try {
+				loadURL (new URL (filename));
+			} catch (MalformedURLException mue) {
+				mue.printStackTrace ();
+			}
+	}
+    
+    private String release_version = "unknown";
+    private String build_number = "0";
+	
+	public synchronized void init () {
+		// On OS X the aqua look and feel is default, but we can't develop for
+		// so
+		// many different looks and feels. Set it to Metal.
+		try {
+			javax.swing.UIManager
+					.setLookAndFeel ("javax.swing.plaf.metal.MetalLookAndFeel");
+//			javax.swing.UIManager.setLookAndFeel(
+//				    javax.swing.UIManager.getSystemLookAndFeelClassName()
+//				);
+		} catch (InstantiationException e) {
+		} catch (ClassNotFoundException e) {
+		} catch (javax.swing.UnsupportedLookAndFeelException e) {
+		} catch (IllegalAccessException e) {
+		}
+		if (!hasRequiredJVM ())
+			return;
+		if (!hasEnoughHeapSpace())
+			return;
+		if (!System.getProperties ().containsKey ("mauve.force.console")) {
+			MyConsole.setUseSwing (true);
+		}
+		if (System.getProperties ().containsKey ("mauve.enable.remote")) {
+			RemoteControlImpl.startRemote (this);
+		}
+
+		// read in the version properties file
+		try {
+			InputStream props_stream = MauveFrame.class
+					.getResourceAsStream ("/version.properties");
+
+			if (props_stream != null) {
+				props.load (props_stream);
+				release_version = props.getProperty ("release.version");
+				build_number = props.getProperty ("build.number");
+			}
+		} catch (IOException ioe) {
+			MyConsole.err ().println ("Couldn't read version.properties file.");
+			ioe.printStackTrace (MyConsole.err ());
+		}
+
+        about_message = "<html><center>Mauve version " + release_version + " build " + build_number + " (c) 2003-2015 <br>Aaron Darling, Paul Infield-Harm, Anna Rissman, and Andrew Tritt<br>" + "<a href=\"http://darlinglab.org/mauve\">http://darlinglab.org/mauve</a><br>" + "<p>Mauve is free, open-source software.  See COPYING for details.</center></p><p>CITATION:<br>Mauve: Multiple Alignment of Conserved Genomic Sequence With Rearrangements.<br>Aaron C. E. Darling, Bob Mau, Frederick R.  [...]
+
+        // check for updates in a separate thread so as not to
+        // stall the GUI if the network is down
+        new UpdateCheck().start();
+
+		frames = new Vector ();
+		availableFrame = makeNewFrame ();
+		new SplashScreen ("/images/mauve_logo.png", about_message, null, 3000);
+		// mauve_splash.dispose();
+		// mauve_splash.setVisible(false);
+	}
+
+	/**
+	 * Checks the Mauve web server for a newer version of this software
+	 */
+	class UpdateCheck extends Thread {
+		public void run() {
+		if (!check_updates)
+			return;
+		try {
+			String os_type = System.getProperty ("os.name");
+			String build_date = props.getProperty ("build.timestamp");
+
+			if (build_date == null) {
+				MyConsole
+						.err ()
+						.println (
+								"Couldn't check version, build.timestamp property not found.");
+				return;
+			}
+			long local_version = Long.parseLong (build_date);
+
+			// default to checking the linux version since it's
+			// probably the least standardized OS currently
+			String os_suffix = "linux";
+			if (os_type.startsWith ("Windows"))
+				os_suffix = "windows";
+			else if (os_type.startsWith ("Mac"))
+				os_suffix = "mac";
+			URL hurl = new URL (
+					"http://darlinglab.org/mauve/downloads/latest."
+							+ os_suffix);
+			BufferedReader latest_in = new BufferedReader (
+					new InputStreamReader (hurl.openStream ()));
+			long latest_version = Long.parseLong (latest_in.readLine ());
+
+			if (local_version < latest_version) {
+				// check the OS type since linux can't do an auto-update
+				if (!os_suffix.equals ("windows")) {
+					JOptionPane
+							.showMessageDialog (
+									null,
+									"<html>An updated version of Mauve is available.<br>See the Mauve web site at <a href=\"http://darlinglab.org/mauve\">http://darlinglab.org/mauve</a> for more details...",
+									"Updated Mauve available",
+									JOptionPane.INFORMATION_MESSAGE);
+					return;
+				}
+
+                int dl_val = JOptionPane.showConfirmDialog(null, "An updated version of Mauve is available.\nWould you like to download and install it?", "Updated Mauve available", JOptionPane.YES_NO_OPTION);
+                if (dl_val == 0)
+                {
+                    //
+                    // download the installer to a temp file
+                    //
+                    MyConsole.out().println("Downloading installer...\n");
+                    String dl_location = "http://darlinglab.org/mauve/downloads/mauve_installer_";
+                    dl_location += Long.toString(latest_version) + ".exe";
+                    URL dlurl = new URL(dl_location);
+                    InputStream is = dlurl.openStream();
+                    File ins_file = File.createTempFile("mauve_ins", ".exe");
+                    FileOutputStream output_file = new FileOutputStream(ins_file);
+                    byte[] buf = new byte[1024 * 1024];
+                    int read_chars = is.read(buf);
+                    while (read_chars >= 0)
+                    {
+                        output_file.write(buf, 0, read_chars);
+                        read_chars = is.read(buf);
+                    }
+                    // close the output file
+                    output_file.close();
+                    //
+                    // run the installer
+                    //
+                    String[] ins_cmd = new String[1];
+                    ins_cmd[0] = ins_file.getPath();
+                    Runtime.getRuntime().exec(ins_cmd);
+                    // delete the installer
+                    ins_file.delete();
+                    // exit this
+                    System.exit(0);
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            MyConsole.err().println("Error checking for updates.");
+	        }
+        }
+    }
+	
+	private boolean hasRequiredJVM () {
+		String jvm_version = System.getProperty ("java.version");
+		int minor_version = Integer.parseInt (jvm_version.substring (2, 3));
+		if (jvm_version.charAt (0) == '1') {
+			if (minor_version < 5) {
+				MyConsole
+						.err ()
+						.println (
+								"Sorry, Mauve requires at least Java version 1.5 to operate correctly");
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	/*
+	 * We require at least 384 Mb of Heap for visualization
+	 */
+	private boolean hasEnoughHeapSpace() {
+		final long required_mb = 384;	/**< Set this to the minimum required megabytes to run Mauve smoothly */
+		long maxmem = java.lang.Runtime.getRuntime().maxMemory();
+		if( maxmem < required_mb*1024*1024 )
+		{
+			StringBuilder errorMessage = new StringBuilder();
+			errorMessage.append("Sorry, Mauve requires at least " + required_mb + "Mb of memory to operate correctly.\n");			
+			if(System.getProperty("os.name").indexOf("indows") > 0)
+			{
+				errorMessage.append("If Mauve was launched by double-clicking Mauve.jar, please relaunch \nMauve by clicking the shortcut in the Windows menu instead, as that \nwill set the memory requirements appropriately.\n\n");
+			}			
+			errorMessage.append("If Mauve was launched via the command-line, please use the -Xmx java \nargument to increase the memory heap allocation size.\n");
+			errorMessage.append("For example, to launch Mauve with 500MB: java -Xmx500m -jar Mauve.jar\n");
+			JOptionPane.showMessageDialog(null, errorMessage.toString(), "Error launching Mauve", JOptionPane.ERROR_MESSAGE);
+			return false;
+		}
+		return true;
+	}
+
+	public synchronized void setFocus (String alignID, String sequenceID,
+			long start, long end, String auth_token, String contig) {
+		URL alignURL;
+		try {
+			alignURL = new URL (alignID);
+		} catch (MalformedURLException e) {
+			e.printStackTrace ();
+			return;
+		}
+
+		Iterator it = frames.iterator ();
+		while (it.hasNext ()) {
+			MauveFrame frame = (MauveFrame) it.next ();
+			if (frame.model != null
+					&& alignURL.equals (frame.model.getSourceURL ())) {
+				frame.model.setFocus (sequenceID, start, end, contig);
+				frame.toFront ();
+				return;
+			}
+		}
+
+		// Load model and then zoom it.
+		MauveFrame frame = getNewFrame ();
+		Thread t = new Thread (new FrameLoader (frame, alignURL, sequenceID,
+				start, end, auth_token, contig));
+		t.start ();
+
+	}
+
+	/**
+	 * Load a file into a RearrangementPanel. Create a new frame if this frame
+	 * is already displaying a genome alignment
+	 */
+	public void loadFile (File rr_file) {
+		MauveFrame frame = getNewFrame ();
+		Thread t = new Thread (new FrameLoader (frame, rr_file));
+		t.start ();
+	}
+
+	/**
+	 * Load a file into a RearrangementPanel. Create a new frame if this frame
+	 * is already displaying a genome alignment
+	 */
+	public void loadURL (URL url) {
+		MauveFrame frame = getNewFrame ();
+		Thread t = new Thread (new FrameLoader (frame, url));
+		t.start ();
+	}
+
+	synchronized public void closeFrame (MauveFrame frame) {
+		if (frames.size () > 1 || frame.rrpanel == null) {
+			frame.setVisible (false);
+			frames.remove (frame);
+			frame.dispose ();
+		} else {
+			frame.reset ();
+			availableFrame = frame;
+		}
+	}
+	    synchronized public void frameClosed()
+    {
+	    if (frames.size() == 0)
+	    {
+	    	// invoke after the current thread has notified all listeners
+	    	SwingUtilities.invokeLater(new Runnable()
+                {
+                    public void run(){
+            	        System.exit(0); // no more frames left. exit.
+                    }
+                });
+		}
+	}
+
+	synchronized protected MauveFrame getNewFrame () {
+		MauveFrame frame;
+		if (availableFrame != null) {
+			frame = availableFrame;
+			availableFrame = null;
+		} else {
+			frame = makeNewFrame ();
+		}
+		return frame;
+	}
+	
+	protected MauveFrame makeNewFrame () {
+		MauveFrame frame = new MauveFrame (this);
+		frames.add (frame);
+		return frame;
+	}
+	 /**
+     *  The string with the version number
+     * @return The version number
+     */
+    public String getVersion(){
+    	return release_version;
+	}
+}
diff --git a/src/org/gel/mauve/gui/MauveAlignFrame.java b/src/org/gel/mauve/gui/MauveAlignFrame.java
new file mode 100644
index 0000000..7492c2f
--- /dev/null
+++ b/src/org/gel/mauve/gui/MauveAlignFrame.java
@@ -0,0 +1,320 @@
+package org.gel.mauve.gui;
+
+import java.awt.Dimension;
+import java.io.File;
+import java.io.IOException;
+import java.util.Vector;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+
+import org.gel.mauve.MyConsole;
+
+public class MauveAlignFrame extends AlignFrame {
+
+	// member declarations
+	JLabel islandSizeLabel = new JLabel ();
+
+	JTextField islandSizeText = new JTextField ();
+
+	JLabel backboneGapSizeLabel = new JLabel ();
+
+	JTextField backboneGapSizeText = new JTextField ();
+
+	JLabel backboneSizeLabel = new JLabel ();
+
+	JTextField backboneSizeText = new JTextField ();
+
+	JCheckBox extendLcbsCheckBox = new JCheckBox ();
+	JComboBox alignerChoice = new JComboBox (new String [] {"Muscle 3.6"});
+
+	JLabel alignerChoiceLabel = new JLabel ("Aligner:", JLabel.RIGHT);
+
+	Dimension d;
+
+	public MauveAlignFrame (Mauve mauve) {
+		super (mauve);
+	}
+
+	public void initComponents () {
+		super.initComponents ();
+
+		// set locations for components in AlignFrame superclass
+		defaultSeedCheckBox.setLocation (new java.awt.Point (10, 10));
+		determineLCBsCheckBox.setLocation (new java.awt.Point (10, 90));
+		seedLengthSlider.setLocation (new java.awt.Point (200, 30));
+		seedLengthLabel.setLocation (new java.awt.Point (210, 10));
+		recursiveCheckBox.setLocation (new java.awt.Point (10, 145));
+		collinearCheckBox.setLocation (new java.awt.Point (10, 110));
+		d = minLcbWeightLabel.getPreferredSize ();
+		minLcbWeightLabel.setLocation (new java.awt.Point (265 - d.width, 90));
+		minLcbWeightText.setLocation (new java.awt.Point (270, 90));
+
+		// initialize mauveAligner specific components
+		extendLcbsCheckBox.setVisible (true);
+		extendLcbsCheckBox.setSize (new java.awt.Dimension (120, 20));
+		extendLcbsCheckBox.setText ("Extend LCBs");
+		extendLcbsCheckBox.setSelected (true);
+		extendLcbsCheckBox.setLocation (new java.awt.Point (10, 165));
+		extendLcbsCheckBox
+				.setToolTipText ("Disabling this may speed up alignment at the expense of sensitivity to some rearrangements.");
+		d = alignerChoice.getPreferredSize ();
+		alignerChoice.setSize (d);
+		alignerChoice.setLocation (new java.awt.Point (330 - d.width, 145));
+		alignerChoice.setVisible (true);
+		d = alignerChoiceLabel.getPreferredSize ();
+		alignerChoiceLabel.setSize (new Dimension (d.width, 20));
+		alignerChoiceLabel.setLocation (alignerChoice.getLocation ().x
+				- d.width - 10, 145);
+		alignerChoiceLabel.setVisible (true);
+		islandSizeLabel.setSize (new java.awt.Dimension (155, 20));
+		islandSizeLabel.setLocation (new java.awt.Point (105, 190));
+		islandSizeLabel.setVisible (true);
+		islandSizeLabel.setText ("Minimum Island Size:");
+		islandSizeText.setVisible (true);
+		islandSizeText.setSize (new java.awt.Dimension (60, 20));
+		islandSizeText.setLocation (new java.awt.Point (270, 190));
+		islandSizeText.setText ("50");
+		backboneGapSizeLabel.setSize (new java.awt.Dimension (215, 20));
+		backboneGapSizeLabel.setLocation (new java.awt.Point (55, 220));
+		backboneGapSizeLabel.setVisible (true);
+		backboneGapSizeLabel.setText ("Maximum Backbone Gap Size:");
+		backboneGapSizeText.setVisible (true);
+		backboneGapSizeText.setSize (new java.awt.Dimension (60, 20));
+		backboneGapSizeText.setLocation (new java.awt.Point (270, 220));
+		backboneGapSizeText.setText ("50");
+		backboneGapSizeText
+				.setToolTipText ("Segments of backbone may not contain gaps larger than the given size");
+		backboneSizeLabel.setSize (new java.awt.Dimension (175, 20));
+		backboneSizeLabel.setLocation (new java.awt.Point (85, 250));
+		backboneSizeLabel.setVisible (true);
+		backboneSizeLabel.setText ("Minimum Backbone Size:");
+		backboneSizeText.setVisible (true);
+		backboneSizeText.setSize (new java.awt.Dimension (60, 20));
+		backboneSizeText.setLocation (new java.awt.Point (270, 250));
+		backboneSizeText.setText ("50");
+		backboneSizeText
+				.setToolTipText ("Segments of backbone must contain at least this many nucleotides without a gap larger than the minimum backbone gap size");
+//		alignButton.setLocation (new java.awt.Point (250, 320));
+
+		parameterPanel.add (extendLcbsCheckBox);
+		parameterPanel.add (alignerChoice);
+		parameterPanel.add (alignerChoiceLabel);
+		parameterPanel.add (islandSizeLabel);
+		parameterPanel.add (islandSizeText);
+		parameterPanel.add (backboneGapSizeLabel);
+		parameterPanel.add (backboneGapSizeText);
+		parameterPanel.add (backboneSizeLabel);
+		parameterPanel.add (backboneSizeText);
+
+		// event handling
+		determineLCBsCheckBox
+				.addActionListener (new java.awt.event.ActionListener () {
+					public void actionPerformed (java.awt.event.ActionEvent e) {
+						determineLCBsCheckBoxActionPerformed (e);
+					}
+				});
+
+		recursiveCheckBox
+				.addActionListener (new java.awt.event.ActionListener () {
+					public void actionPerformed (java.awt.event.ActionEvent e) {
+						recursiveCheckBoxActionPerformed (e);
+					}
+				});
+	}
+    
+    
+    protected String[] makeAlignerCommand()
+    {
+        Vector cmd_vec = new Vector();
+        read_filename = null;
+        String cur_cmd;
+        boolean detect_lcbs = true;
+
+        String pname = getBinaryPath("mauveAligner");
+        cmd_vec.addElement(pname);
+
+		if (getSeedWeight () > 0) {
+			cur_cmd = "--seed-size=";
+			cur_cmd += Integer.toString (getSeedWeight ());
+			cmd_vec.addElement (cur_cmd);
+		}
+
+        // get a good output file name
+        String output_file = getOutput();
+        cur_cmd = "--output=";
+        cur_cmd += output_file;
+        cmd_vec.addElement(cur_cmd);
+
+		read_filename = output_file;
+
+		detect_lcbs = isLCBSearchEnabled ();
+		if (detect_lcbs) {
+
+			if (!getRecursive ()) {
+				cmd_vec.addElement ("--no-recursion");
+			}
+
+			if (!getExtendLcbs ()) {
+				cmd_vec.addElement ("--no-lcb-extension");
+			}
+
+			if (getCollinear ()) {
+				cmd_vec.addElement ("--collinear");
+			}
+
+			if (getMinLcbWeight () != -1) {
+				cur_cmd = "--weight=";
+				cur_cmd += Integer.toString (getMinLcbWeight ());
+				cmd_vec.addElement (cur_cmd);
+			}
+
+			if (getRecursive ()) {
+				if (alignerChoice.getSelectedIndex () == 1) {
+					cur_cmd = "--gapped-aligner=clustal";
+					cmd_vec.addElement (cur_cmd);
+				}
+				if (getIslandSize () != -1) {
+					cur_cmd = "--island-size=";
+					cur_cmd += Integer.toString (getIslandSize ());
+					cmd_vec.addElement (cur_cmd);
+
+					cur_cmd = "--island-output=" + output_file + ".islands";
+					cmd_vec.addElement (cur_cmd);
+				}
+
+				if (getBackboneSize () != -1 && getBackboneGapSize () != -1) {
+					cur_cmd = "--backbone-size=";
+					cur_cmd += Integer.toString (getBackboneSize ());
+					cmd_vec.addElement (cur_cmd);
+
+					cur_cmd = "--max-backbone-gap=";
+					cur_cmd += Integer.toString (getBackboneGapSize ());
+					cmd_vec.addElement (cur_cmd);
+
+					cur_cmd = "--backbone-output=" + output_file + ".backbone";
+					cmd_vec.addElement (cur_cmd);
+				}
+				// make an identity matrix file name
+				cur_cmd = "--id-matrix=" + output_file + ".id_matrix";
+				cmd_vec.addElement (cur_cmd);
+
+				// make an alignment file name
+				cur_cmd = "--output-alignment=" + output_file + ".alignment";
+				cmd_vec.addElement (cur_cmd);
+
+				read_filename = output_file + ".alignment";
+			}
+
+			// make a guide tree file name
+			cur_cmd = "--output-guide-tree=" + output_file + ".guide_tree";
+			cmd_vec.addElement (cur_cmd);
+		} else if (!detect_lcbs) {
+			cur_cmd = "--mums";
+			cmd_vec.addElement (cur_cmd);
+			cur_cmd = "--eliminate-inclusions";
+			cmd_vec.addElement (cur_cmd);
+		}
+
+		String [] sequences = getSequences ();
+		for (int seqI = 0; seqI < sequences.length; seqI++) {
+			cmd_vec.addElement (sequences[seqI]);
+			if (sequences.length > 1) {
+				cur_cmd = sequences[seqI] + ".sslist";
+				cmd_vec.addElement (cur_cmd);
+	            File sml_file = new File(cur_cmd);
+	            if(sml_file.exists())  sml_file.delete();
+			}
+		}
+
+		String [] mauve_cmd = new String [cmd_vec.size ()];
+		mauve_cmd = (String []) (cmd_vec.toArray (mauve_cmd));
+
+		return mauve_cmd;
+	}
+
+	public void determineLCBsCheckBoxActionPerformed (
+			java.awt.event.ActionEvent e) {
+		super.determineLCBsCheckBoxActionPerformed (e);
+		if (determineLCBsCheckBox.isSelected ()) {
+			recursiveCheckBox.setEnabled (true);
+			if (recursiveCheckBox.isSelected ()) {
+				islandSizeLabel.setEnabled (true);
+				islandSizeText.setEnabled (true);
+				backboneGapSizeLabel.setEnabled (true);
+				backboneGapSizeText.setEnabled (true);
+				backboneSizeLabel.setEnabled (true);
+				backboneSizeText.setEnabled (true);
+				extendLcbsCheckBox.setEnabled (true);
+				alignerChoice.setEnabled (true);
+				alignerChoiceLabel.setEnabled (true);
+			}
+		} else {
+			recursiveCheckBox.setEnabled (false);
+			islandSizeLabel.setEnabled (false);
+			islandSizeText.setEnabled (false);
+			backboneGapSizeLabel.setEnabled (false);
+			backboneGapSizeText.setEnabled (false);
+			backboneSizeLabel.setEnabled (false);
+			backboneSizeText.setEnabled (false);
+			extendLcbsCheckBox.setEnabled (false);
+			alignerChoice.setEnabled (false);
+			alignerChoiceLabel.setEnabled (false);
+		}
+	}
+
+	public void recursiveCheckBoxActionPerformed (java.awt.event.ActionEvent e) {
+		if (recursiveCheckBox.isSelected ()) {
+			islandSizeLabel.setEnabled (true);
+			islandSizeText.setEnabled (true);
+			backboneGapSizeLabel.setEnabled (true);
+			backboneGapSizeText.setEnabled (true);
+			backboneSizeLabel.setEnabled (true);
+			backboneSizeText.setEnabled (true);
+			extendLcbsCheckBox.setEnabled (true);
+		} else {
+			islandSizeLabel.setEnabled (false);
+			islandSizeText.setEnabled (false);
+			backboneGapSizeLabel.setEnabled (false);
+			backboneGapSizeText.setEnabled (false);
+			backboneSizeLabel.setEnabled (false);
+			backboneSizeText.setEnabled (false);
+			extendLcbsCheckBox.setEnabled (false);
+		}
+	}
+
+	public boolean getExtendLcbs () {
+		return extendLcbsCheckBox.isSelected ();
+	}
+
+	public boolean isLCBSearchEnabled () {
+		return determineLCBsCheckBox.isSelected ();
+	}
+
+	public int getIslandSize () {
+		try {
+			return Integer.parseInt (islandSizeText.getText ());
+		} catch (NumberFormatException nfe) {
+			return -1;
+		}
+	}
+
+	public int getBackboneSize () {
+		try {
+			return Integer.parseInt (backboneSizeText.getText ());
+		} catch (NumberFormatException nfe) {
+			return -1;
+		}
+	}
+
+	public int getBackboneGapSize () {
+		try {
+			return Integer.parseInt (backboneGapSizeText.getText ());
+		} catch (NumberFormatException nfe) {
+			return -1;
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/MauveFrame.java b/src/org/gel/mauve/gui/MauveFrame.java
new file mode 100644
index 0000000..eda9460
--- /dev/null
+++ b/src/org/gel/mauve/gui/MauveFrame.java
@@ -0,0 +1,766 @@
+/** 
+ * MauveFrame.java
+ *
+ * Title:			Mauve
+ * Description:		Viewer for multiple genome alignments and annotation
+ * @author			koadman
+ * @version			
+ */
+
+package org.gel.mauve.gui;
+
+import gr.zeus.ui.JConsole;
+
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.prefs.BackingStoreException;
+
+import javax.swing.AbstractAction;
+import javax.swing.ImageIcon;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.WindowConstants;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.BrowserLauncher;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.ModelBuilder;
+import org.gel.mauve.ModelProgressListener;
+import org.gel.mauve.MyConsole;
+import org.gel.mauve.SeqFeatureData;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.analysis.SnpExporter;
+import org.gel.mauve.color.ColorMenu;
+import org.gel.mauve.contigs.ContigMauveAlignFrame;
+import org.gel.mauve.contigs.ContigOrderer;
+import org.gel.mauve.gui.dnd.FileDrop;
+import org.gel.mauve.gui.sequence.FeatureFilterer;
+import org.gel.mauve.gui.sequence.FlatFileFeatureImporter;
+
+/**
+ * The window frame for a Mauve application, containing a menu bar, a tool bar
+ * and a RearrangementPanel to display genome alignments. 
+ */
+public class MauveFrame extends JFrame implements ActionListener, ModelProgressListener
+{
+    protected BaseViewerModel model;
+    
+    String documentation_url = "http://darlinglab.org/mauve/user-guide/introduction.html";
+    
+    // member declarations
+    JMenuBar jMenuBar1 = new JMenuBar();
+    protected JMenuItem jMenuFileOpen = new JMenuItem();
+    protected JMenuItem jMenuFileAlign = new JMenuItem();
+    protected JMenuItem jMenuFileProgressiveAlign = new JMenuItem();
+    JMenuItem jMenuFilePrint = new JMenuItem();
+    JMenuItem jMenuFilePageSetup = new JMenuItem();
+    JMenuItem jMenuFilePrintPreview = new JMenuItem();
+    //JMenuItem jMenuFileImport = new JMenuItem();
+    JMenuItem jMenuFileClose = new JMenuItem();
+    JMenuItem jMenuFileQuit = new JMenuItem();
+
+    JMenu jMenuHelp = new JMenu();
+    JMenuItem jMenuHelpAbout = new JMenuItem();
+    JMenuItem jMenuHelpConsole = new JMenuItem();
+    JMenuItem jMenuHelpDocumentation = new JMenuItem();
+    JMenuItem jMenuHelpClearCache = new JMenuItem();
+    
+    
+    JMenu jMenuView = new JMenu ();
+
+    ColorMenu jMenuViewColorScheme = new ColorMenu ();
+    StyleMenu jMenuViewStyle = new StyleMenu ();
+    ExportMenu jMenuToolsExportMenu = new ExportMenu ();
+    
+    JMenu jMenuGoTo = new JMenu ();
+    JMenuItem jMenuGoToSeqPos = new JMenuItem ();
+    JMenuItem jMenuGoToFeatName = new JMenuItem ();
+    JMenuItem jMenuGoToSearchFeatures = new JMenuItem ();
+    
+    JMenu jMenuTools = new JMenu ();
+    protected JMenuItem jMenuToolsOrderContigs = new JMenuItem ();
+    
+    JFileChooser fc;
+    RearrangementPanel rrpanel;
+    JToolBar toolbar;
+    public LCBStatusBar status_bar;
+    Mauve mauve;
+    AlignFrame alignFrame;
+    AlignFrame progressiveAlignFrame;
+    JScrollPane scrollPane;
+    SequenceNavigator navigator;
+    protected FlatFileFeatureImporter importer;
+    
+    private int progressSequenceCount;
+
+
+    static ImageIcon home_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/Home16.gif"));
+    static ImageIcon left_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/Back16.gif"));
+    static ImageIcon right_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/Forward16.gif"));
+    static ImageIcon zoomin_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/ZoomIn16.gif"));
+    static ImageIcon zoomout_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/ZoomOut16.gif"));
+    static ImageIcon zoom_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/Zoom16.gif"));
+    public static ImageIcon mauve_icon = new ImageIcon(MauveFrame.class.getResource("/images/mauve_icon.gif"));
+    static ImageIcon find_feature_icon = new ImageIcon(MauveFrame.class.getResource("/images/searchBinoculars16.png"));
+    static ImageIcon dcj_icon = new ImageIcon(MauveFrame.class.getResource("/images/Dcj16.gif"));
+    static ImageIcon grimm_icon = new ImageIcon(MauveFrame.class.getResource("/images/Grimm16.gif"));
+    static ImageIcon scAss_icon = new ImageIcon(MauveFrame.class.getResource("/images/ScoreAssembly.png"));
+    static Cursor zoom_in_cursor = Toolkit.getDefaultToolkit().createCustomCursor(new ImageIcon(MauveFrame.class.getResource("/images/ZoomIn24.gif")).getImage(), new Point(8, 8), "zoomin");
+    static Cursor zoom_out_cursor = Toolkit.getDefaultToolkit().createCustomCursor(new ImageIcon(MauveFrame.class.getResource("/images/ZoomOut24.gif")).getImage(), new Point(8, 8), "zoomout");
+
+    public MauveFrame(Mauve mauve)
+    {
+        this.mauve = mauve;
+        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+        fc = new JFileChooser();
+        String osname = System.getProperty("os.name");
+        if(osname.indexOf("indow")==-1&&osname.indexOf("Mac")==-1)
+        	fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
+        pack();
+        initComponents();
+        setVisible(true);
+    }
+
+    private void initComponents()
+    {
+        setTitle("Mauve " + mauve.getVersion() + " - Genome Alignment Visualization");
+        setIconImage(mauve_icon.getImage());
+        status_bar = new LCBStatusBar();
+        getContentPane().add(status_bar, BorderLayout.SOUTH);
+        setLocation(new java.awt.Point(0, 0));
+
+        initMenus();
+
+        setSize(new java.awt.Dimension(791, 500));
+
+        // event handling
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing(java.awt.event.WindowEvent e)
+            {
+                thisWindowClosing(e);
+            }
+            public void windowClosed(java.awt.event.WindowEvent e)
+            {
+            	mauve.frameClosed();
+            }
+        });
+
+        // Drag-and-drop handling.
+        new FileDrop(getContentPane(), new FileDrop.Listener()
+        {
+            public void filesDropped(java.io.File[] files)
+            {
+                for (int i = 0; i < files.length; i++)
+                {
+                    mauve.loadFile(files[i]);
+                }
+            }
+        });
+    }
+
+    /**
+     *  
+     */
+    private void initMenus()
+    {
+        JMenu jMenuFile = new JMenu();
+        jMenuFile.setToolTipText("Perform file related actions");
+        jMenuFile.setVisible(true);
+        jMenuFile.setText("File");
+        jMenuFile.setMnemonic('F');
+        jMenuFileOpen.setToolTipText("Open an existing alignment...");
+        jMenuFileOpen.setVisible(true);
+        jMenuFileOpen.setText("Open alignment...");
+        jMenuFileOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+        jMenuFileOpen.setMnemonic('O');
+        
+        jMenuFileAlign.setToolTipText("Align sequences with the mauveAligner algorithm...");
+        jMenuFileAlign.setVisible(true);
+        jMenuFileAlign.setText("Align sequences...");
+        jMenuFileAlign.setMnemonic('A');
+        
+        jMenuFileProgressiveAlign.setToolTipText("Align sequences with progressiveMauve (more accurate)...");
+        jMenuFileProgressiveAlign.setVisible(true);
+        jMenuFileProgressiveAlign.setText("Align with progressiveMauve...");
+        jMenuFileProgressiveAlign.setMnemonic('M');
+
+        jMenuFilePrint.setToolTipText("Print the current view...");
+        jMenuFilePrint.setVisible(true);
+        jMenuFilePrint.setEnabled(false);
+        jMenuFilePrint.setText("Print");
+        jMenuFilePrint.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+        jMenuFilePrint.setMnemonic('P');
+        
+        jMenuFilePageSetup.setToolTipText("Choose printer settings...");
+        jMenuFilePageSetup.setVisible(true);
+        jMenuFilePageSetup.setEnabled(false);
+        jMenuFilePageSetup.setText("Page Setup...");
+        
+        jMenuFilePrintPreview.setToolTipText("Show a print preview...");
+        jMenuFilePrintPreview.setVisible(true);
+        jMenuFilePrintPreview.setEnabled(false);
+        jMenuFilePrintPreview.setText("Print Preview");
+        jMenuFilePrintPreview.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.SHIFT_MASK + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+        
+
+        /*jMenuFileImport.setToolTipText("Import annotation file...");
+        jMenuFileImport.setVisible(true);
+        jMenuFileImport.setEnabled(false);
+        jMenuFileImport.setText("Import Annotations");
+        jMenuFileImport.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+        jMenuFileImport.setMnemonic('s');*/
+        
+        jMenuFileClose.setToolTipText("Close this alignment...");
+        jMenuFileClose.setVisible(true);
+        jMenuFileClose.setEnabled(false);
+        jMenuFileClose.setText("Close");
+        jMenuFileClose.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+        jMenuFileClose.setMnemonic('C');
+
+        JSeparator jMenuFileSeparator1 = new JSeparator();
+        jMenuFileSeparator1.setVisible(true);
+
+        jMenuFileQuit.setToolTipText("Quit this application");
+        jMenuFileQuit.setVisible(true);
+        jMenuFileQuit.setText("Quit");
+        jMenuFileQuit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+        jMenuFileQuit.setMnemonic('Q');
+
+        jMenuHelp.setToolTipText("Get help using this program");
+        jMenuHelp.setVisible(true);
+        jMenuHelp.setText("Help");
+        jMenuHelp.setMnemonic('H');
+        jMenuHelpAbout.setToolTipText("Display version and author information...");
+        jMenuHelpAbout.setVisible(true);
+        jMenuHelpAbout.setText("About Mauve...");
+        jMenuHelpAbout.setMnemonic('A');
+
+        jMenuHelpDocumentation.setToolTipText("View Mauve's online documentation...");
+        jMenuHelpDocumentation.setVisible(true);
+        jMenuHelpDocumentation.setText("Online documentation...");
+        jMenuHelpDocumentation.setMnemonic('d');
+
+        jMenuHelpConsole.setToolTipText("Shows the console where error messages and other information is reported");
+        jMenuHelpConsole.setVisible(true);
+        jMenuHelpConsole.setText("Show console");
+        jMenuHelpConsole.setMnemonic('c');
+        
+        jMenuHelpClearCache.setToolTipText("Clear the on-disk cache of processed alignments");
+        jMenuHelpClearCache.setVisible(true);
+        jMenuHelpClearCache.setText("Clear alignment cache");
+        jMenuHelpClearCache.setMnemonic('r');
+        
+        jMenuView.setToolTipText("Viewer controls");
+        jMenuView.setVisible(true);
+        jMenuView.setEnabled(false);
+        jMenuView.setText("View");
+        jMenuView.setMnemonic('V');
+
+//        jMenuViewColorScheme.setToolTipText("Color schemes for the similarity plot");
+        jMenuViewColorScheme.setVisible(true);
+        jMenuViewColorScheme.setEnabled(false);
+        jMenuViewColorScheme.setText("Color Scheme");
+        jMenuViewColorScheme.setMnemonic('C');
+
+        jMenuViewStyle.setToolTipText("Drawing styles for the genome comparison");
+        jMenuViewStyle.setVisible(true);
+        jMenuViewStyle.setEnabled(false);
+        jMenuViewStyle.setText("Style");
+        jMenuViewStyle.setMnemonic('S');
+        
+//       jMenuGoTo.setToolTipText("Go to specific location in genome");
+        jMenuGoTo.setVisible(true);
+        jMenuGoTo.setEnabled(false);
+        jMenuGoTo.setText("Go To");
+        jMenuGoTo.setMnemonic('T');
+        
+        jMenuGoToSeqPos.setToolTipText("Go To Numerical Sequence Position");
+        jMenuGoToSeqPos.setVisible(true);
+        jMenuGoToSeqPos.setText("Sequence Position");
+        jMenuGoToSeqPos.setMnemonic('e');
+        
+        jMenuGoToFeatName.setToolTipText("Find a feature by exact name");
+        jMenuGoToFeatName.setVisible(true);
+        jMenuGoToFeatName.setText("Feature Named. . .");
+        jMenuGoToFeatName.setMnemonic('N');
+        
+        jMenuGoToSearchFeatures.setToolTipText("Find features with specified constraints");
+        jMenuGoToSearchFeatures.setVisible(true);
+        jMenuGoToSearchFeatures.setText("Find Features. . .");
+        jMenuGoToSearchFeatures.setMnemonic('i');
+        jMenuGoToSearchFeatures.setAccelerator(KeyStroke.getKeyStroke(
+        		KeyEvent.VK_I, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
+        
+        jMenuTools.setToolTipText("Additional Mauve Tools");
+        jMenuTools.setVisible(true);
+        jMenuTools.setText("Tools");
+        jMenuTools.setMnemonic('T');
+        jMenuToolsOrderContigs.setToolTipText("Order contigs of draft genome");
+        jMenuToolsOrderContigs.setVisible(true);
+        jMenuToolsOrderContigs.setText("Move Contigs");
+        jMenuToolsOrderContigs.setMnemonic('O');
+
+        jMenuToolsExportMenu.setToolTipText("Export data in various formats");
+        jMenuToolsExportMenu.setVisible(true);
+        jMenuToolsExportMenu.setEnabled(true);
+        jMenuToolsExportMenu.setText("Export");
+        jMenuToolsExportMenu.setMnemonic('x');
+        
+        setJMenuBar(jMenuBar1);
+
+        jMenuBar1.add(jMenuFile);
+        jMenuFile.add(jMenuFileOpen);
+        jMenuFile.add(jMenuFileAlign);
+        jMenuFile.add(jMenuFileProgressiveAlign);
+        jMenuFile.add(jMenuFilePrint);
+        jMenuFile.add(jMenuFilePageSetup);
+        jMenuFile.add(jMenuFilePrintPreview);
+        //jMenuFile.add(jMenuFileImport);
+        jMenuFile.add(jMenuFileClose);
+        jMenuFile.add(jMenuFileSeparator1);
+        jMenuFile.add(jMenuFileQuit);
+
+        jMenuBar1.add (jMenuView);
+        jMenuView.add(jMenuViewColorScheme);
+        jMenuView.add(jMenuViewStyle);
+        jMenuView.add(jMenuGoTo);
+
+//        jMenuBar1.add (jMenuGoTo);
+        jMenuGoTo.add (jMenuGoToSeqPos);
+        jMenuGoTo.add (jMenuGoToFeatName);
+        jMenuGoTo.add (jMenuGoToSearchFeatures);
+        
+        jMenuBar1.add(jMenuTools);
+        jMenuTools.add(jMenuToolsOrderContigs);
+        jMenuTools.add(jMenuToolsExportMenu);
+        
+        jMenuBar1.add(jMenuHelp);
+        jMenuHelp.add(jMenuHelpAbout);
+        jMenuHelp.add(jMenuHelpDocumentation);
+        jMenuHelp.add(jMenuHelpClearCache);
+        jMenuHelp.add(jMenuHelpConsole);
+
+        jMenuFile.setMinimumSize(jMenuFile.getSize());
+        jMenuView.setMinimumSize(jMenuView.getSize());
+        jMenuHelp.setMinimumSize(jMenuHelp.getSize());
+        jMenuBar1.setVisible(true);
+
+        jMenuFileOpen.addActionListener(this);
+        jMenuFileAlign.addActionListener(this);
+        jMenuFileProgressiveAlign.addActionListener(this);        
+        jMenuFileClose.addActionListener(this);
+        jMenuFilePrint.addActionListener(this);
+        jMenuFilePageSetup.addActionListener(this);
+        jMenuFilePrintPreview.addActionListener(this);
+        //jMenuFileImport.addActionListener(this);
+        jMenuFileQuit.addActionListener(this);
+        jMenuHelpAbout.addActionListener(this);
+        jMenuHelpDocumentation.addActionListener(this);
+        jMenuHelpConsole.addActionListener(this);
+        jMenuHelpClearCache.addActionListener(this);
+        jMenuGoToSeqPos.addActionListener (this);
+        jMenuGoToFeatName.addActionListener (this);
+        jMenuGoToSearchFeatures.addActionListener (this);
+        jMenuToolsOrderContigs.addActionListener(this);
+        
+        // set up key bindings
+    	String cmd_key = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() == Event.CTRL_MASK ? "ctrl" : "meta";
+        jMenuFilePrint.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(cmd_key + " P"), "Print");
+        jMenuFileOpen.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(cmd_key + " O"), "Open");
+        jMenuFileClose.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(cmd_key + " W"), "Close");
+        jMenuFileQuit.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(cmd_key + " Q"), "Quit");
+        jMenuGoToSearchFeatures.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
+        		KeyStroke.getKeyStroke(cmd_key + " I"), jMenuGoToSearchFeatures.getText ());
+        jMenuFilePrint.getActionMap().put("Print", new GenericAction(this, "Print"));
+        jMenuFilePageSetup.getActionMap().put("PageSetup", new GenericAction(this, "PageSetup"));
+        jMenuFilePrintPreview.getActionMap().put("PrintPreview", new GenericAction(this, "PrintPreview"));
+        //jMenuFileImport.getActionMap().put("Import", new GenericAction(this, "Import"));
+        jMenuFileOpen.getActionMap().put("Open", new GenericAction(this, "Open"));
+        jMenuFileClose.getActionMap().put("Close", new GenericAction(this, "Close"));
+        jMenuFileQuit.getActionMap().put("Quit", new GenericAction(this, "Quit"));
+        jMenuHelpAbout.getActionMap().put("About", new GenericAction(this, "About"));
+        jMenuHelpDocumentation.getActionMap().put("Documentation", new GenericAction(this, "About"));
+        jMenuHelpClearCache.getActionMap().put("ClearCache", new GenericAction(this, "ClearCache"));
+        jMenuHelpConsole.getActionMap().put("Console", new GenericAction(this, "Console"));
+        jMenuGoToSeqPos.getActionMap().put(jMenuGoToSeqPos.getText (), new GenericAction(
+        		this, jMenuGoToSeqPos.getText ()));
+        jMenuGoToFeatName.getActionMap().put(jMenuGoToFeatName.getText (), new GenericAction(
+        		this, jMenuGoToFeatName.getText ()));
+        jMenuGoToSearchFeatures.getActionMap().put(jMenuGoToSearchFeatures.getText (), 
+        		new GenericAction(this, jMenuGoToSearchFeatures.getText ()));
+        jMenuToolsOrderContigs.getActionMap().put(jMenuToolsOrderContigs.getText(), 
+        		new GenericAction(this, jMenuToolsOrderContigs.getText()));
+    }
+
+    class GenericAction extends AbstractAction
+    {
+
+        GenericAction(ActionListener al, String command)
+        {
+            super(command);
+            this.al = al;
+            putValue(ACTION_COMMAND_KEY, command);
+        }
+
+        ActionListener al;
+
+        public void actionPerformed(ActionEvent e)
+        {
+            al.actionPerformed(e);
+        }
+    }
+
+    private boolean mShown = false;
+
+    public void actionPerformed(ActionEvent ae)
+    {
+        if (ae.getSource() instanceof JMenuItem)
+        {
+            JMenuItem source = (JMenuItem) (ae.getSource());
+            if (source == jMenuFileQuit || ae.getActionCommand().equals("Quit"))
+            {
+                setVisible(false);
+                dispose();
+                System.exit(0);
+            }
+            if (source == jMenuFileOpen || ae.getActionCommand().equals("Open"))
+            {
+                doFileOpen();
+            }
+            if (source == jMenuFileClose || ae.getActionCommand().equals("Close"))
+            {
+                this.
+                thisWindowClosing(null);
+            }
+            if (source == jMenuFileAlign)
+            {
+                doAlign();
+            }
+            if (source == jMenuFileProgressiveAlign)
+            {
+                doProgressiveAlign();
+            }
+            if (source == jMenuFilePrint || ae.getActionCommand().equals("Print"))
+            {
+                rrpanel.print();
+            }
+            if (source == jMenuFilePageSetup || ae.getActionCommand().equals("PageSetup"))
+            {
+                rrpanel.pageSetup();
+            }
+            if (source == jMenuFilePrintPreview || ae.getActionCommand().equals("PrintPreview"))
+            {
+            	if (rrpanel != null)
+            	{
+            		PrintPreviewDialog dialog = new PrintPreviewDialog(rrpanel, rrpanel.pageFormat, 1);
+            		dialog.setVisible(true);
+            	}
+            }
+            /*if (source == jMenuFileImport || ae.getActionCommand().equals("Import"))
+            {
+            	importer.setVisible (true);
+            }*/            
+
+            if (source == jMenuHelpAbout || ae.getActionCommand().equals("About"))
+            {
+                new SplashScreen("/images/mauve_logo.png", Mauve.about_message, this, 5000000);
+            }
+            if (source == jMenuHelpDocumentation || ae.getActionCommand().equals("Documentation"))
+            {
+        		try{
+        			BrowserLauncher.openURL(documentation_url);
+        		}catch(IOException ioe){}
+            }
+            if (source == jMenuHelpConsole || ae.getActionCommand().equals("Console"))
+            {
+            	JConsole console = JConsole.getConsole();
+            	console.showConsole();
+            }
+            if (source == jMenuHelpClearCache || ae.getActionCommand().equals("ClearCache"))
+            {
+            	try{
+            		ModelBuilder.clearDataCache();
+            	}catch(BackingStoreException bse)
+            	{
+            		bse.printStackTrace();
+            	}
+            }
+            if (source == jMenuGoToSearchFeatures || ae.getActionCommand().equals(
+            		jMenuGoToSearchFeatures.getText ())) {
+            	navigator.showNavigator ();
+            }
+            if (source == jMenuGoToSeqPos || ae.getActionCommand().equals(
+            		jMenuGoToSeqPos.getText ())) {
+            	SequenceNavigator.goToSeqPos (this, getModel(), rrpanel);
+            }
+            if (source == jMenuGoToFeatName || ae.getActionCommand().equals(
+            		jMenuGoToFeatName.getText ())) {
+            	navigator.goToFeatureByName ();
+            }
+            if (source == jMenuToolsOrderContigs || ae.getActionCommand().equals(
+            		jMenuToolsOrderContigs.getText()))
+            	new ContigOrderer (null,mauve.frames,true);
+        }
+    }
+
+    public void addNotify()
+    {
+        super.addNotify();
+
+        if (mShown)
+            return;
+
+        // resize frame to account for menubar
+        JMenuBar jMenuBar = getJMenuBar();
+        if (jMenuBar != null)
+        {
+            int jMenuBarHeight = jMenuBar.getPreferredSize().height;
+            Dimension dimension = getSize();
+            dimension.height += jMenuBarHeight;
+            setSize(dimension);
+        }
+
+        mShown = true;
+    }
+
+    /** Close the window when the close box is clicked */
+    void thisWindowClosing(java.awt.event.WindowEvent e)
+    {
+    	/*if (model != null)
+    		FeatureFilterer.removeFilterer (model);*/
+        mauve.closeFrame(this);
+        if (navigator != null) {
+        	navigator.dispose ();
+        	navigator = null;
+        }
+        if (importer != null) {
+        	importer.dispose ();
+        	importer = null;
+        }
+        System.gc ();
+    }
+    
+    /**
+     * 
+     */
+    public void reset()
+    {
+        getContentPane().remove(scrollPane);
+        rrpanel = null;
+        jMenuBar1.remove(toolbar);
+        toolbar = null;
+        jMenuBar1.validate();
+        jMenuBar1.repaint();
+        getContentPane().repaint();
+        setTitle("Mauve " + mauve.getVersion() + " - Genome Alignment Visualization");
+        jMenuFilePrint.setEnabled(false);
+        jMenuFilePageSetup.setEnabled(false);
+        jMenuFilePrintPreview.setEnabled(false);
+        //jMenuFileImport.setEnabled(false);
+        jMenuToolsExportMenu.setEnabled(false);
+        jMenuGoTo.setEnabled(false);
+        jMenuFileClose.setEnabled(false);
+        model.removeHighlightListener(status_bar);
+        model = null;
+        status_bar.clear();
+    }
+
+    void loadError(String message)
+    {
+        MyConsole.err().println(message);
+        setTitle("Mauve " + mauve.getVersion() + " - Genome Alignment Visualization");
+        model = null;
+    }
+    
+	public FlatFileFeatureImporter getFeatureImporter () {
+		return importer;
+	}
+
+    public BaseViewerModel getModel()
+    {
+        return model;
+    }
+    
+	public RearrangementPanel getRearrangementPanel () {
+		return rrpanel;
+	}
+    
+    public void setModel(BaseViewerModel model)
+    {
+        if (model == null)
+        {
+            this.model = null;
+        }
+        else
+        {
+            this.model = model;
+ 
+            toolbar = new JToolBar();
+            jMenuBar1.add(toolbar);
+            
+            rrpanel = new RearrangementPanel(toolbar, this);
+            Dimension max_size = new Dimension( 10000, 10000 );
+            rrpanel.setMaximumSize(max_size);
+
+            scrollPane = new JScrollPane(rrpanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+            getContentPane().add(scrollPane);
+            scrollPane.getVerticalScrollBar().setUnitIncrement(25);
+
+            model.addHighlightListener(status_bar);
+            status_bar.setModel(model);
+            rrpanel.addHintMessageListener(status_bar);
+            
+            rrpanel.init(model);
+            validate();
+            toFront();
+
+            if(model instanceof XmfaViewerModel)
+            	((XmfaViewerModel)model).initDbusCommunication();
+
+            /*
+             * go into visualization mode: set the title and enable the print
+             * option
+             */
+            setTitle("Mauve " + mauve.getVersion() + " - " + model.getSrc().getName());
+            jMenuFilePrint.setEnabled(true);
+            jMenuFilePageSetup.setEnabled(true);
+            jMenuFilePrintPreview.setEnabled(true);
+            //jMenuFileImport.setEnabled(true);
+            jMenuToolsExportMenu.setEnabled(true);
+            jMenuFileClose.setEnabled(true);
+            jMenuGoTo.setEnabled(true);
+            if (SeqFeatureData.userSelectableGenomes (model, false, true).size () > 0) {
+            	navigator = new SequenceNavigator(this, rrpanel, model);
+            	jMenuGoToFeatName.setEnabled (true);
+            	jMenuGoToSearchFeatures.setEnabled (true);
+            }
+            else {
+            	jMenuGoToFeatName.setEnabled (false);
+            	jMenuGoToSearchFeatures.setEnabled (false);
+            }
+
+            jMenuView.setEnabled(true);
+            jMenuViewColorScheme.setEnabled(true);
+            jMenuViewColorScheme.build(model);
+            jMenuViewStyle.setTarget(model, rrpanel);
+            importer = new FlatFileFeatureImporter (this);
+            jMenuToolsExportMenu.setTarget(model, rrpanel);
+            toFront();
+        }
+    }
+    
+    /**
+     * Pop up an AlignFrame to let the user select parameters for genome
+     * alignment
+     */
+    //TODO: Move out to Mauve (or some other class).
+    public void doAlign()
+    {
+        if (alignFrame == null)
+        {
+            alignFrame = new MauveAlignFrame(mauve);
+            alignFrame.initComponents();
+       		// notify the alignment frame if the parent window is closed...
+            addWindowListener(new java.awt.event.WindowAdapter()
+            {
+                public void windowClosed(java.awt.event.WindowEvent e)
+                {
+                	alignFrame.thisWindowClosed(e);
+                }
+            });
+        }
+        int result = JOptionPane.showConfirmDialog(this, "<html>The mauveAligner algorithm has been superseded by progressiveMauve<br>and is being maintained here only for historical reasons.<p>It is suggested that you select File->\"Align with progressiveMauve\"<br>to compute the alignment instead.  Continue with mauveAligner<br>only if certain that it's the right tool for the job.", "mauveAligner is obsolete", JOptionPane.OK_CANCEL_OPTION,JOptionPane.WARNING_MESSAGE);
+        if (result == JOptionPane.CANCEL_OPTION)
+        {
+            return;
+        }
+        
+        alignFrame.setVisible(true);
+    }
+    
+    public void doProgressiveAlign()
+    {
+        if (progressiveAlignFrame == null)
+        {
+        	progressiveAlignFrame = new ProgressiveMauveAlignFrame(mauve);
+        	progressiveAlignFrame.initComponents();
+       		// notify the alignment frame if the parent window is closed...
+            addWindowListener(new java.awt.event.WindowAdapter()
+            {
+                public void windowClosed(java.awt.event.WindowEvent e)
+                {
+                	alignFrame.thisWindowClosed(e);
+                }
+            });
+        }
+        progressiveAlignFrame.setVisible(true);
+    }
+    
+    /** called when the user selects 'Open' from the file menu */
+    //TODO: Move out to Mauve (or some other class).
+   public void doFileOpen()
+    {
+        int returnVal = fc.showOpenDialog(this);
+
+        if (returnVal == JFileChooser.APPROVE_OPTION)
+        {
+            File rr_file = fc.getSelectedFile();
+            mauve.loadFile(rr_file);
+        }
+    }
+
+    public void buildStart()
+    {
+        status_bar.setHint("Starting...");
+    }
+
+    public void downloadStart()
+    {
+        status_bar.setHint("Downloading file");
+    }
+
+    public void alignmentStart()
+    {
+        status_bar.setHint("Reading file");
+    }
+
+    public void alignmentEnd(int sequenceCount)
+    {
+        progressSequenceCount = sequenceCount;
+    }
+
+    public void featureStart(int sequenceIndex)
+    {
+        status_bar.setHint("Reading sequence " + (sequenceIndex + 1) + " of " + progressSequenceCount);
+    }
+
+    public void done()
+    {
+        status_bar.setHint("Done");
+    }
+
+}
diff --git a/src/org/gel/mauve/gui/MauveRenderingHints.java b/src/org/gel/mauve/gui/MauveRenderingHints.java
new file mode 100644
index 0000000..fa3cc50
--- /dev/null
+++ b/src/org/gel/mauve/gui/MauveRenderingHints.java
@@ -0,0 +1,19 @@
+package org.gel.mauve.gui;
+
+import java.awt.RenderingHints;
+
+public class MauveRenderingHints {
+	public static RenderingHints.Key KEY_SIMILARITY_DENSITY = new SimilarityDensityKey (
+			42);
+
+	private static class SimilarityDensityKey extends RenderingHints.Key {
+		protected SimilarityDensityKey (int privatekey) {
+			super (privatekey);
+		}
+
+		public boolean isCompatibleValue (Object value) {
+			return (value instanceof Double);
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/MySymbolSequenceRenderer.java b/src/org/gel/mauve/gui/MySymbolSequenceRenderer.java
new file mode 100644
index 0000000..26217aa
--- /dev/null
+++ b/src/org/gel/mauve/gui/MySymbolSequenceRenderer.java
@@ -0,0 +1,116 @@
+package org.gel.mauve.gui;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.event.MouseEvent;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.util.List;
+
+import org.biojava.bio.BioRuntimeException;
+import org.biojava.bio.gui.sequence.GUITools;
+import org.biojava.bio.gui.sequence.SequenceRenderContext;
+import org.biojava.bio.gui.sequence.SequenceRenderer;
+import org.biojava.bio.gui.sequence.SequenceViewerEvent;
+import org.biojava.bio.seq.io.SymbolTokenization;
+import org.biojava.bio.symbol.Location;
+import org.biojava.bio.symbol.SymbolList;
+
+/**
+ * A slightly revised version of Biojava's SymbolSequenceRenderer, which prints
+ * symbols near center of range for location, and offset a little further down.
+ */
+public class MySymbolSequenceRenderer implements SequenceRenderer {
+	private double depth = 51.0;
+
+	private Paint outline;
+
+	public MySymbolSequenceRenderer () {
+		outline = Color.black;
+	}
+
+	public double getDepth (SequenceRenderContext context) {
+		return depth + 1.0;
+	}
+
+	public double getMinimumLeader (SequenceRenderContext context) {
+		return 0.0;
+	}
+
+	public double getMinimumTrailer (SequenceRenderContext context) {
+		return 0.0;
+	}
+
+	public void paint (Graphics2D g2, SequenceRenderContext context) {
+		Rectangle2D prevClip = g2.getClipBounds ();
+		AffineTransform prevTransform = g2.getTransform ();
+
+		g2.setPaint (outline);
+
+		Font font = context.getFont ();
+
+		Rectangle2D maxCharBounds = font.getMaxCharBounds (g2
+				.getFontRenderContext ());
+
+		double scale = context.getScale ();
+
+		if (scale >= (maxCharBounds.getWidth () * 0.3)
+				&& scale >= (maxCharBounds.getHeight () * 0.3)) {
+			double xFontOffset = 0.0;
+			double yFontOffset = 0.0;
+
+			// These offsets are not set quite correctly yet. The
+			// Rectangle2D from getMaxCharBounds() seems slightly
+			// off. The "correct" application of translations based on
+			// the Rectangle2D seem to give the wrong results. The
+			// values below are mostly fudges.
+			if (context.getDirection () == SequenceRenderContext.HORIZONTAL) {
+				xFontOffset = maxCharBounds.getCenterX () * 0.2;
+				yFontOffset = -maxCharBounds.getCenterY () + (depth * 0.5);
+			} else {
+				xFontOffset = -maxCharBounds.getCenterX () + (depth * 0.5);
+				yFontOffset = -maxCharBounds.getCenterY () * 3.0;
+			}
+
+			SymbolList seq = context.getSymbols ();
+			SymbolTokenization toke = null;
+			try {
+				toke = seq.getAlphabet ().getTokenization ("token");
+			} catch (Exception e) {
+				throw new BioRuntimeException (e);
+			}
+
+			Location visible = GUITools.getVisibleRange (context, g2);
+			for (int sPos = visible.getMin (); sPos <= visible.getMax (); sPos++) {
+				double gPos = (context.sequenceToGraphics (sPos) + context
+						.sequenceToGraphics (sPos + 1)) / 2.0;
+				String s = "?";
+				try {
+					s = toke.tokenizeSymbol (seq.symbolAt (sPos));
+				} catch (Exception ex) {
+					// We'll ignore the case of not being able to tokenize it
+				}
+
+				if (context.getDirection () == SequenceRenderContext.HORIZONTAL) {
+					g2.drawString (s, (float) (gPos + xFontOffset),
+							(float) yFontOffset);
+				} else {
+					g2.drawString (s, (float) xFontOffset,
+							(float) (gPos + yFontOffset));
+				}
+			}
+		}
+
+		g2.setClip (prevClip);
+		g2.setTransform (prevTransform);
+	}
+
+	public SequenceViewerEvent processMouseEvent (
+			SequenceRenderContext context, MouseEvent me, List path) {
+		path.add (this);
+		int sPos = context.graphicsToSequence (me.getPoint ());
+		return new SequenceViewerEvent (this, null, sPos, me, path);
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/PrintPreviewCanvas.java b/src/org/gel/mauve/gui/PrintPreviewCanvas.java
new file mode 100644
index 0000000..3d8ceaf
--- /dev/null
+++ b/src/org/gel/mauve/gui/PrintPreviewCanvas.java
@@ -0,0 +1,77 @@
+package org.gel.mauve.gui;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.print.Book;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+
+import javax.swing.JPanel;
+
+public class PrintPreviewCanvas extends JPanel {
+	private Book book;
+
+	private int currentPage;
+
+	public PrintPreviewCanvas (Book b) {
+		book = b;
+		currentPage = 0;
+	}
+
+	public void paintComponent (Graphics g) {
+		super.paintComponent (g);
+		Graphics2D g2 = (Graphics2D) g;
+		PageFormat pageFormat = book.getPageFormat (currentPage);
+
+		double xoff;
+		double yoff;
+		double scale;
+
+		double px = pageFormat.getWidth ();
+		double py = pageFormat.getHeight ();
+		double sx = getWidth () - 1;
+		double sy = getHeight () - 1;
+
+		if (px / py < sx / sy) // center horizontally
+		{
+			scale = sy / py;
+			xoff = 0.5 * (sx - scale * px);
+			yoff = 0;
+		} else // Center vertically
+		{
+			scale = sx / px;
+			xoff = 0;
+			yoff = 0.5 * (sy - scale * py);
+		}
+
+		g2.translate ((float) xoff, (float) yoff);
+		g2.scale ((float) scale, (float) scale);
+
+		Rectangle2D page = new Rectangle2D.Double (0, 0, px, py);
+		g2.setPaint (Color.WHITE);
+		g2.fill (page);
+		g2.setPaint (Color.BLACK);
+		g2.draw (page);
+
+		Printable printable = book.getPrintable (currentPage);
+		try {
+			printable.print (g2, pageFormat, currentPage);
+		} catch (PrinterException exception) {
+			g2.draw (new Line2D.Double (0, 0, px, py));
+			g2.draw (new Line2D.Double (0, px, 0, py));
+		}
+
+	}
+
+	public void flipPage (int by) {
+		int newPage = currentPage + by;
+		if (0 <= newPage && newPage < book.getNumberOfPages ()) {
+			currentPage = newPage;
+			repaint ();
+		}
+	}
+}
diff --git a/src/org/gel/mauve/gui/PrintPreviewDialog.java b/src/org/gel/mauve/gui/PrintPreviewDialog.java
new file mode 100644
index 0000000..1ec7326
--- /dev/null
+++ b/src/org/gel/mauve/gui/PrintPreviewDialog.java
@@ -0,0 +1,51 @@
+package org.gel.mauve.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.print.Book;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+
+public class PrintPreviewDialog extends JDialog {
+	private static final int HEIGHT = 570;
+
+	private static final int WIDTH = 400;
+
+	private PrintPreviewCanvas canvas;
+
+	public PrintPreviewDialog (Printable p, PageFormat pf, int pages) {
+		Book book = new Book ();
+		book.append (p, pf, pages);
+		layoutUI (book);
+	}
+
+	public PrintPreviewDialog (Book b) {
+		layoutUI (b);
+	}
+
+	public void layoutUI (Book b) {
+		setSize (WIDTH, HEIGHT);
+		setTitle ("Mauve Print Preview");
+		Container contentPane = getContentPane ();
+		canvas = new PrintPreviewCanvas (b);
+		contentPane.add (canvas, BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel ();
+
+		JButton closeButton = new JButton ("Close");
+		buttonPanel.add (closeButton);
+		closeButton.addActionListener (new ActionListener () {
+			public void actionPerformed (ActionEvent evt) {
+				setVisible (false);
+			}
+		});
+
+		contentPane.add (buttonPanel, BorderLayout.SOUTH);
+	}
+}
diff --git a/src/org/gel/mauve/gui/PrintUtilities.java b/src/org/gel/mauve/gui/PrintUtilities.java
new file mode 100644
index 0000000..fcfdeab
--- /dev/null
+++ b/src/org/gel/mauve/gui/PrintUtilities.java
@@ -0,0 +1,34 @@
+package org.gel.mauve.gui;
+
+import java.awt.Component;
+
+import javax.swing.RepaintManager;
+
+/**
+ * A couple of utilities.
+ * 
+ * 7/99 Marty Hall, http://www.apl.jhu.edu/~hall/java/ May be freely used or
+ * adapted.
+ */
+
+public class PrintUtilities {
+
+	/**
+	 * The speed and quality of printing suffers dramatically if any of the
+	 * containers have double buffering turned on. So this turns if off
+	 * globally.
+	 * 
+	 * @see enableDoubleBuffering
+	 */
+	public static void disableDoubleBuffering (Component c) {
+		RepaintManager currentManager = RepaintManager.currentManager (c);
+		currentManager.setDoubleBufferingEnabled (false);
+	}
+
+	/** Re-enables double buffering globally. */
+
+	public static void enableDoubleBuffering (Component c) {
+		RepaintManager currentManager = RepaintManager.currentManager (c);
+		currentManager.setDoubleBufferingEnabled (true);
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/ProgressiveMauveAlignFrame.java b/src/org/gel/mauve/gui/ProgressiveMauveAlignFrame.java
new file mode 100644
index 0000000..dba9cf8
--- /dev/null
+++ b/src/org/gel/mauve/gui/ProgressiveMauveAlignFrame.java
@@ -0,0 +1,602 @@
+package org.gel.mauve.gui;
+
+import java.awt.Dimension;
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Vector;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.JTextField;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.gel.mauve.MyConsole;
+
+public class ProgressiveMauveAlignFrame extends AlignFrame implements ChangeListener {
+	
+	
+	/* 
+	 * These variables should be private. Before they had no modifier, which
+	 * means they were accessible to to everything else in this package.
+	 * There is no reason for these members to be directly accessible to 
+	 * the outside world. If you need to access them, make setters
+	 * and getters -- don't be lazy! 
+	 */
+    // member declarations
+	protected Dimension d;
+	protected JCheckBox refineCheckBox = new JCheckBox();
+	protected JCheckBox seedFamiliesCheckBox = new JCheckBox();
+    
+	protected JCheckBox sumOfPairsCheckBox = new JCheckBox();
+	protected JSlider breakpointWeightScaleSlider = new JSlider();
+	protected JLabel breakpointWeightScaleLabel = new JLabel();
+	protected JTextField breakpointWeightScaleText = new JTextField(5);
+	protected JSlider conservationWeightScaleSlider = new JSlider();
+	protected JLabel conservationWeightScaleLabel = new JLabel();
+	protected JTextField conservationWeightScaleText = new JTextField(5);
+
+    
+	protected JPanel scorePanel = new JPanel();
+	protected JComboBox matrixChoice = new JComboBox(new String[] {"HOXD (default)", "Custom"});
+	protected JLabel matrixChoiceLabel = new JLabel();
+	protected JTextField[][] scoreText = new JTextField[4][4];
+	protected JLabel[] scoreLabelRow = new JLabel[4];
+	protected JLabel[] scoreLabelCol = new JLabel[4];
+	protected String[] scoreLabels = {"A", "C", "G", "T"};
+	protected String[][] hoxd_matrix = {
+    		{"91",   "-114", "-31",  "-123"},
+    		{"-114", "100",  "-125", "-31" },
+    		{"-31",  "-125", "100",  "-114"},
+    		{"-123", "-31",  "-114", "91"  }
+    };
+	protected String hoxd_go = "-400";
+	protected String hoxd_ge = "-30";
+	protected JTextField gapOpenText = new JTextField();
+	protected JLabel gapOpenLabel = new JLabel();
+	protected JTextField gapExtendText = new JTextField();
+	protected JLabel gapExtendLabel = new JLabel();
+    
+    public ProgressiveMauveAlignFrame(Mauve mauve)
+    {
+    	super(mauve);
+    }
+    public ProgressiveMauveAlignFrame(Mauve mauve, boolean gui)
+    {
+    	super(mauve, gui);
+    }
+
+    public void initComponents()
+    {
+    	super.initComponents();
+    	
+        // the following code sets the frame's initial state
+        defaultSeedCheckBox.setLocation(new java.awt.Point(10, 10));
+        determineLCBsCheckBox.setLocation(new java.awt.Point(10, 50));
+        seedLengthSlider.setLocation(new java.awt.Point(200, 30));
+        seedLengthLabel.setLocation(new java.awt.Point(210, 10));
+        recursiveCheckBox.setLocation(new java.awt.Point(10, 90));
+        collinearCheckBox.setLocation(new java.awt.Point(10, 70));
+        d = minLcbWeightLabel.getPreferredSize();
+        minLcbWeightLabel.setLocation(new java.awt.Point(265 - d.width, 90));
+        minLcbWeightText.setLocation(new java.awt.Point(270, 90));
+//        alignButton.setLocation(new java.awt.Point(220, 320));
+
+        //
+        // add a panel to define MUSCLE behavior
+        //
+        scorePanel.setSize(new java.awt.Dimension(350, 150));
+        scorePanel.setLocation(new java.awt.Point(0, 210));
+        scorePanel.setVisible(true);
+        scorePanel.setLayout(null);
+
+        d = matrixChoice.getPreferredSize();
+        matrixChoice.setSize(d);
+        matrixChoice.setLocation(new java.awt.Point(130, 10));
+        matrixChoice.setVisible(true);
+    	scorePanel.add(matrixChoice);
+        matrixChoiceLabel.getPreferredSize();
+        matrixChoiceLabel.setText("Scoring matrix:");
+        matrixChoiceLabel.setSize(new Dimension(120, 15));
+        matrixChoiceLabel.setLocation(10, 15);
+        matrixChoiceLabel.setVisible(true);
+    	scorePanel.add(matrixChoiceLabel);
+        
+        // layout substitution scoring matrix
+        int score_matrix_left = 15;
+        int score_matrix_top = 35;
+        int score_left = score_matrix_left + 25;
+        int score_top = score_matrix_top + 25;
+        int score_w_offset = 40;
+        int score_w = score_w_offset - 5;
+        int score_h_offset = 25;
+        int score_h = score_h_offset - 5;
+
+        int t = score_top;
+    	for( int sI = 0; sI < 4; sI++ )
+        {
+        	int l = score_left;
+        	for( int sJ = 0; sJ < 4; sJ++ )
+        	{
+        		scoreText[sI][sJ] = new JTextField();
+                scoreText[sI][sJ].setVisible(true);
+                scoreText[sI][sJ].setSize(new java.awt.Dimension(score_w, score_h));
+                scoreText[sI][sJ].setLocation(new java.awt.Point(l, t));
+                scoreText[sI][sJ].setHorizontalAlignment(JTextField.RIGHT);
+                scoreText[sI][sJ].addActionListener(new java.awt.event.ActionListener()
+                        {
+                            public void actionPerformed(java.awt.event.ActionEvent e)
+                            {
+                                scoreTextActionPerformed(e);
+                            }
+                        });
+            	scorePanel.add(scoreText[sI][sJ]);
+                l += score_w_offset;
+        	}
+        	t += score_h_offset;
+        }
+    	setScoreEditable(false);
+    	setMatrixValues(hoxd_matrix);
+        int score_label_top = score_matrix_top;
+        int score_label_left = score_matrix_left;
+        t = score_label_top;
+        int l = score_label_left;
+        for( int sI = 0; sI < 4; sI++ )
+        {
+        	t += score_h_offset;
+        	l += score_w_offset;
+        	scoreLabelRow[sI] = new JLabel();
+        	scoreLabelRow[sI].setSize(new java.awt.Dimension(20, 20));
+        	scoreLabelRow[sI].setLocation(new java.awt.Point(l, score_label_top + 5));
+        	scoreLabelRow[sI].setVisible(true);
+        	scoreLabelRow[sI].setText(scoreLabels[sI]);
+        	scorePanel.add(scoreLabelRow[sI]);
+        	scoreLabelCol[sI] = new JLabel();
+        	scoreLabelCol[sI].setSize(new java.awt.Dimension(20, 20));
+        	scoreLabelCol[sI].setLocation(new java.awt.Point(score_label_left + 10, t));
+        	scoreLabelCol[sI].setVisible(true);
+        	scoreLabelCol[sI].setText(scoreLabels[sI]);
+        	scorePanel.add(scoreLabelCol[sI]);
+        }
+
+        gapOpenText.setVisible(true);
+        gapOpenText.setSize(new java.awt.Dimension(50, 20));
+        gapOpenText.setLocation(new java.awt.Point(150, 160));
+        gapOpenText.setText(hoxd_go);
+        gapOpenText.setHorizontalAlignment(JTextField.RIGHT);
+    	scorePanel.add(gapOpenText);
+    	gapOpenLabel.setSize(new java.awt.Dimension(200, 20));
+        gapOpenLabel.setLocation(new java.awt.Point(15, 160));
+    	gapOpenLabel.setVisible(true);
+    	gapOpenLabel.setText("Gap open score:");
+    	scorePanel.add(gapOpenLabel);
+
+        gapExtendText.setVisible(true);
+        gapExtendText.setSize(new java.awt.Dimension(50, 20));
+        gapExtendText.setLocation(new java.awt.Point(150, 185));
+        gapExtendText.setText(hoxd_ge);
+        gapExtendText.setHorizontalAlignment(JTextField.RIGHT);
+    	scorePanel.add(gapExtendText);
+    	gapExtendLabel.setSize(new java.awt.Dimension(200, 20));
+        gapExtendLabel.setLocation(new java.awt.Point(15, 185));
+    	gapExtendLabel.setVisible(true);
+    	gapExtendLabel.setText("Gap extend score:");
+    	scorePanel.add(gapExtendLabel);
+
+    	// allow use of custom scoring matrices
+        alignmentOptionPane.addTab("Scoring", scorePanel);
+
+        // initialize progressiveMauve-specific configuration options
+        seedFamiliesCheckBox.setVisible(true);
+        seedFamiliesCheckBox.setSize(new java.awt.Dimension(180, 20));
+        seedFamiliesCheckBox.setText("Use seed families");
+        seedFamiliesCheckBox.setSelected(false);
+        seedFamiliesCheckBox.setLocation(new java.awt.Point(10, 30));
+        seedFamiliesCheckBox.setToolTipText("<html>Uses multiple spaced seed patterns to identify potential homology.<br>Can substantially improve sensitivity and accuracy on divergent genomes.</html>");
+
+        refineCheckBox.setVisible(true);
+        refineCheckBox.setSize(new java.awt.Dimension(180, 20));
+        refineCheckBox.setText("Iterative refinement");
+        refineCheckBox.setSelected(true);
+        refineCheckBox.setLocation(new java.awt.Point(10, 110));
+        refineCheckBox.setToolTipText("Iteratively refines the alignment, significantly improving accuracy");
+
+        sumOfPairsCheckBox.setVisible(true);
+        sumOfPairsCheckBox.setSize(new java.awt.Dimension(220, 20));
+        sumOfPairsCheckBox.setText("Sum-of-pairs LCB scoring");
+        sumOfPairsCheckBox.setSelected(true);
+        sumOfPairsCheckBox.setLocation(new java.awt.Point(10, 135));
+        sumOfPairsCheckBox.setToolTipText("Set to use sum-of-pairs scoring instead of scoring LCBs against an inferred ancestral order");
+
+        breakpointWeightScaleLabel.setSize(new java.awt.Dimension(215, 20));
+        breakpointWeightScaleLabel.setVisible(true);
+        breakpointWeightScaleLabel.setText("Breakpoint dist. weight scaling:");
+        breakpointWeightScaleLabel.setLocation(new java.awt.Point(10, 155));
+
+        breakpointWeightScaleSlider.setMinimum(0);
+        breakpointWeightScaleSlider.setMaximum(100);
+        breakpointWeightScaleSlider.setValue(50);
+        breakpointWeightScaleSlider.setMinorTickSpacing(5);
+        breakpointWeightScaleSlider.setMajorTickSpacing(10);
+        breakpointWeightScaleSlider.setToolTipText("Set the pairwise breakpoint distance scaling for LCB weight");
+        breakpointWeightScaleSlider.setPaintTicks(true);
+        breakpointWeightScaleSlider.setPaintLabels(false);
+        breakpointWeightScaleSlider.setSnapToTicks(false);
+        d = breakpointWeightScaleSlider.getPreferredSize();
+//        d.setSize(125, d.getHeight());
+        breakpointWeightScaleSlider.setPreferredSize(d);
+        breakpointWeightScaleSlider.setMaximumSize(d);
+        breakpointWeightScaleSlider.addChangeListener(this);
+        breakpointWeightScaleSlider.setLocation(new java.awt.Point(10, 175));
+        breakpointWeightScaleSlider.setVisible(true);
+        breakpointWeightScaleSlider.setEnabled(true);
+
+        d = breakpointWeightScaleText.getPreferredSize();
+        d.setSize(40, d.getHeight());
+        breakpointWeightScaleText.setPreferredSize(d);
+        breakpointWeightScaleText.setMaximumSize(d);
+        breakpointWeightScaleText.setHorizontalAlignment(JTextField.RIGHT);
+        breakpointWeightScaleText.setText("0.500");
+        breakpointWeightScaleText.setLocation(new java.awt.Point(200, 175));
+        
+
+        conservationWeightScaleLabel.setSize(new java.awt.Dimension(215, 20));
+        conservationWeightScaleLabel.setVisible(true);
+        conservationWeightScaleLabel.setText("Conservation dist. weight scaling:");
+        conservationWeightScaleLabel.setLocation(new java.awt.Point(10, 210));
+
+        conservationWeightScaleSlider.setMinimum(0);
+        conservationWeightScaleSlider.setMaximum(100);
+        conservationWeightScaleSlider.setValue(50);
+        conservationWeightScaleSlider.setMinorTickSpacing(5);
+        conservationWeightScaleSlider.setMajorTickSpacing(10);
+        conservationWeightScaleSlider.setToolTipText("Set the pairwise conservation distance scaling for LCB weight");
+        conservationWeightScaleSlider.setPaintTicks(true);
+        conservationWeightScaleSlider.setPaintLabels(false);
+        conservationWeightScaleSlider.setSnapToTicks(false);
+        d = conservationWeightScaleSlider.getPreferredSize();
+        d.setSize(125, d.getHeight());
+        conservationWeightScaleSlider.setPreferredSize(d);
+        conservationWeightScaleSlider.setMaximumSize(d);
+        conservationWeightScaleSlider.addChangeListener(this);
+        conservationWeightScaleSlider.setLocation(new java.awt.Point(10, 230));
+        conservationWeightScaleSlider.setVisible(true);
+        conservationWeightScaleSlider.setEnabled(true);
+
+        d = conservationWeightScaleText.getPreferredSize();
+        d.setSize(40, d.getHeight());
+        conservationWeightScaleText.setPreferredSize(d);
+        conservationWeightScaleText.setMaximumSize(d);
+        conservationWeightScaleText.setHorizontalAlignment(JTextField.RIGHT);
+        conservationWeightScaleText.setText("0.500");
+        conservationWeightScaleText.setLocation(new java.awt.Point(200, 230));
+
+        parameterPanel.add(seedFamiliesCheckBox);
+        parameterPanel.add(refineCheckBox);
+        parameterPanel.add(sumOfPairsCheckBox);
+// this stuff isn't working yet:
+/*
+        parameterPanel.add(breakpointWeightScaleLabel);
+        parameterPanel.add(breakpointWeightScaleSlider);
+        parameterPanel.add(breakpointWeightScaleText);
+
+        parameterPanel.add(conservationWeightScaleLabel);
+        parameterPanel.add(conservationWeightScaleSlider);
+        parameterPanel.add(conservationWeightScaleText);
+*/
+        // event handling
+        determineLCBsCheckBox.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                determineLCBsCheckBoxActionPerformed(e);
+            }
+        });
+
+        recursiveCheckBox.addActionListener(new java.awt.event.ActionListener()
+                {
+                    public void actionPerformed(java.awt.event.ActionEvent e)
+                    {
+                        recursiveCheckBoxActionPerformed(e);
+                    }
+                });
+        collinearCheckBox.addActionListener(new java.awt.event.ActionListener()
+                {
+                    public void actionPerformed(java.awt.event.ActionEvent e)
+                    {
+                    	collinearCheckBoxActionPerformed(e);
+                    }
+                });
+        matrixChoice.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent e)
+            {
+                matrixChoiceActionPerformed(e);
+            }
+        });
+    }
+
+    File getDefaultFile() throws IOException
+    {
+        return File.createTempFile("mauve", ".xmfa");
+    }
+
+    public void stateChanged(ChangeEvent e)
+    {
+        if (e.getSource() == breakpointWeightScaleSlider)
+        {
+            double w = (double)breakpointWeightScaleSlider.getValue();
+            Double d = new Double(w/100);
+            breakpointWeightScaleText.setText(d.toString());
+        }
+        if (e.getSource() == breakpointWeightScaleText)
+        {
+        }
+    }
+    
+    protected String[] makeAlignerCommand()
+    {
+        Vector cmd_vec = new Vector();
+        read_filename = null;
+        String cur_cmd;
+        boolean detect_lcbs = true;
+
+        String pname = getBinaryPath("progressiveMauve");
+        cmd_vec.addElement(pname);
+
+        if (getSeedFamilies())
+        {
+        	cmd_vec.addElement("--seed-family");
+        }
+        	
+        if (getSeedWeight() > 0)
+        {
+            cur_cmd = "--seed-weight=";
+            cur_cmd += Integer.toString(getSeedWeight());
+            cmd_vec.addElement(cur_cmd);
+        }
+
+        // get a good output file name
+        String output_file = getOutput();
+        cur_cmd = "--output=";
+        cur_cmd += output_file;
+        cmd_vec.addElement(cur_cmd);
+
+        read_filename = output_file;
+        
+        detect_lcbs = isLCBSearchEnabled();
+        if (detect_lcbs)
+        {
+
+            if (!getRecursive())
+            {
+                cmd_vec.addElement("--skip-gapped-alignment");
+            }
+            if(!getRefine())
+            {
+            	cmd_vec.addElement("--skip-refinement");
+            }
+            if(!getSumOfPairs())
+            {
+            	cmd_vec.addElement("--scoring-scheme=ancestral");
+            }
+
+            if( getCollinear() )
+            {
+            	cmd_vec.addElement("--collinear");
+            }
+
+            if (getMinLcbWeight() != -1)
+            {
+                cur_cmd = "--weight=";
+                cur_cmd += Integer.toString(getMinLcbWeight());
+                cmd_vec.addElement(cur_cmd);
+            }
+
+            // make a guide tree file name
+            cur_cmd = "--output-guide-tree=" + output_file + ".guide_tree";
+            cmd_vec.addElement(cur_cmd);
+
+            cmd_vec.addElement("--backbone-output=" + output_file + ".backbone");
+        }
+        else if (!detect_lcbs)
+        {
+            cur_cmd = "--mums";
+            cmd_vec.addElement(cur_cmd);
+        }
+        
+        if(!(getScoreMatrixName().indexOf("default") > 0))
+        {
+        	File mat_file = null;
+        	String[][] mat = getScoreMatrix();
+        	// create a score matrix file
+        	try{
+        		mat_file = File.createTempFile("scoremat", ".txt");
+        		FileWriter outtie = new FileWriter(mat_file);
+        		outtie.write("# user-defined scoring matrix\n");
+        		for(int i = 0; i < 4; i++)
+        		{
+					outtie.write("     ");
+					outtie.write(scoreLabels[i]);
+        		}
+				outtie.write("     N");
+        		outtie.write("\n");
+        		for(int i = 0; i < 4; i++)
+        		{
+        			for(int j = 0; j < 4; j++)
+        			{
+        				if(j == 0)
+        					outtie.write(scoreLabels[i]);
+
+        				// space pad the score value
+        				String space_str = new String();
+        				for( int sI = 0; sI < 6 - mat[i][j].length(); ++sI)
+        					space_str += " ";
+    					outtie.write(space_str);
+        				
+    					// write the score value
+        				outtie.write(mat[i][j]);
+        			}
+        			outtie.write("     0");	// for the N column
+        			outtie.write("\n");
+        		}
+        		outtie.write("N     0     0     0     0     0");
+        		outtie.flush();
+        		outtie.close();
+        	}catch(IOException ioe)
+        	{
+        		System.err.println("Error creating score matrix file");
+        	}
+        	if(mat_file != null)
+        	{
+        		cmd_vec.addElement("--substitution-matrix=" + mat_file.getAbsolutePath());
+        		cmd_vec.addElement("--gap-open=" + getGapOpen());
+        		cmd_vec.addElement("--gap-extend=" + getGapExtend());
+        	}
+        }
+
+        String[] sequences = getSequences();
+        for (int seqI = 0; seqI < sequences.length; seqI++)
+        {
+            cmd_vec.addElement(sequences[seqI]);
+            // preemptively delete SMLs to avoid crashes
+            File sml_file = new File(sequences[seqI] + ".sslist");
+            if(sml_file.exists())  sml_file.delete();
+        }
+
+        String[] mauve_cmd = new String[cmd_vec.size()];
+        mauve_cmd = (String[]) (cmd_vec.toArray(mauve_cmd));
+        
+        return mauve_cmd;
+    }
+
+    public void updateEnabledStates()
+    {
+        if (determineLCBsCheckBox.isSelected())
+        {
+            refineCheckBox.setEnabled(true);
+        }
+        else
+        {
+        	refineCheckBox.setEnabled(false);
+        }
+    	if(collinearCheckBox.isSelected() || !determineLCBsCheckBox.isSelected())
+    	{
+        	sumOfPairsCheckBox.setEnabled(false);
+    		breakpointWeightScaleText.setEnabled(false);
+    		breakpointWeightScaleSlider.setEnabled(false);
+    		breakpointWeightScaleLabel.setEnabled(false);
+    		conservationWeightScaleText.setEnabled(false);
+    		conservationWeightScaleSlider.setEnabled(false);
+    		conservationWeightScaleLabel.setEnabled(false);
+    	}else if(determineLCBsCheckBox.isSelected())
+    	{
+        	sumOfPairsCheckBox.setEnabled(true);
+    		breakpointWeightScaleText.setEnabled(true);
+    		breakpointWeightScaleSlider.setEnabled(true);
+    		breakpointWeightScaleLabel.setEnabled(true);
+    		conservationWeightScaleText.setEnabled(true);
+    		conservationWeightScaleSlider.setEnabled(true);
+    		conservationWeightScaleLabel.setEnabled(true);
+    	}
+    }
+    public void determineLCBsCheckBoxActionPerformed(java.awt.event.ActionEvent e)
+    {
+    	super.determineLCBsCheckBoxActionPerformed(e);
+    	updateEnabledStates();
+    }
+
+    public void collinearCheckBoxActionPerformed(java.awt.event.ActionEvent e)
+    {
+    	super.collinearCheckBoxActionPerformed(e);
+    	updateEnabledStates();
+    }
+    public void recursiveCheckBoxActionPerformed(java.awt.event.ActionEvent e)
+    {
+    	updateEnabledStates();
+    }
+    public void setMatrixValues(String[][] mat)
+    {
+    	for(int i = 0; i < 4; i++)
+    		for(int j = 0; j < 4; j++)
+    			scoreText[i][j].setText(mat[i][j]);
+    }
+    public void setScoreEditable(boolean edit)
+    {
+    	gapOpenText.setEditable(edit);
+    	gapExtendText.setEditable(edit);
+		for(int i = 0; i < 4; i++)
+			for(int j = 0; j < 4; j++)
+				scoreText[i][j].setEditable(edit);
+    }
+    public void matrixChoiceActionPerformed(java.awt.event.ActionEvent e)
+    {
+    	if(matrixChoice.getSelectedIndex() == 0)
+    	{
+    		setMatrixValues(hoxd_matrix);
+    		gapOpenText.setText(hoxd_go);
+    		gapExtendText.setText(hoxd_ge);
+    		setScoreEditable(false);
+    	}
+    	if(matrixChoice.getSelectedIndex() == matrixChoice.getItemCount()-1)
+    	{
+    		// last item is custom matrix
+    		setScoreEditable(true);
+    	}
+    }
+    public void scoreTextActionPerformed(java.awt.event.ActionEvent e)
+    {
+    	matrixChoice.setSelectedIndex(matrixChoice.getItemCount()-1);
+    }
+
+    public boolean getSeedFamilies()
+    {
+    	if(seedFamiliesCheckBox.isEnabled())
+    		return seedFamiliesCheckBox.isSelected();
+    	return false;
+    }
+
+    public boolean getRefine()
+    {
+    	if(refineCheckBox.isEnabled())
+    		return refineCheckBox.isSelected();
+    	return false;
+    }
+
+    public boolean getSumOfPairs()
+    {
+    	if(sumOfPairsCheckBox.isEnabled())
+    		return sumOfPairsCheckBox.isSelected();
+    	return false;
+    }
+
+    public String getScoreMatrixName()
+    {
+    	return matrixChoice.getSelectedItem().toString();
+    }
+    public String[][] getScoreMatrix()
+    {
+    	String[][] mat = new String[4][4];
+    	for(int i = 0; i < 4; i++)
+    		for(int j = 0; j < 4; j++)
+    			mat[i][j] = scoreText[i][j].getText();
+    	return mat;
+    }
+    public String getGapOpen()
+    {
+    	return gapOpenText.getText();
+    }
+    public String getGapExtend()
+    {
+    	return gapExtendText.getText();
+    }
+}
diff --git a/src/org/gel/mauve/gui/QualifierPanel.java b/src/org/gel/mauve/gui/QualifierPanel.java
new file mode 100644
index 0000000..95a9c35
--- /dev/null
+++ b/src/org/gel/mauve/gui/QualifierPanel.java
@@ -0,0 +1,68 @@
+package org.gel.mauve.gui;
+
+import java.awt.Font;
+import java.util.Iterator;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+
+import org.biojava.bio.Annotation;
+import org.biojava.bio.seq.Feature;
+
+public class QualifierPanel extends Box {
+	private final static int WRAP_COL = 60;
+
+	public QualifierPanel (Feature f) {
+		super (BoxLayout.Y_AXIS);
+		setName ("<html>" + f.getType () + "<br>" + f.getLocation ()
+				+ "</html>");
+
+		StringBuffer msg = new StringBuffer ();
+		msg.append ("<html>");
+
+		Annotation a = f.getAnnotation ();
+
+		if (a != null) {
+			msg.append ("<table border='1'>");
+			Iterator i = a.keys ().iterator ();
+			while (i.hasNext ()) {
+				Object key = i.next ();
+				msg.append ("<tr><td valign='top'><b>");
+				msg.append (key.toString ());
+				msg.append ("</b></td><td valign='top'>");
+				msg.append (wrapLong (a.getProperty (key).toString ()));
+				msg.append ("</td></tr>");
+			}
+			msg.append ("</table>");
+		}
+
+		msg.append ("</html>");
+		JLabel label = new JLabel (msg.toString ());
+		label.setFont (new Font (label.getFont ().getName (), Font.PLAIN, label
+				.getFont ().getSize ()));
+		add (label);
+	}
+
+	private String wrapLong (String in) {
+		if (in == null)
+			return null;
+
+		if (in.length () < WRAP_COL)
+			return in;
+
+		StringBuffer s = new StringBuffer ();
+
+		int i = 0;
+		for (; i < in.length () - WRAP_COL; i += WRAP_COL) {
+			s.append (in.subSequence (i, i + WRAP_COL));
+			s.append ("<br>");
+		}
+
+		if (i < in.length ()) {
+			s.append (in.subSequence (i, in.length ()));
+		}
+
+		return s.toString ();
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/RearrangementPanel.java b/src/org/gel/mauve/gui/RearrangementPanel.java
new file mode 100644
index 0000000..a23ad1b
--- /dev/null
+++ b/src/org/gel/mauve/gui/RearrangementPanel.java
@@ -0,0 +1,976 @@
+package org.gel.mauve.gui;
+
+import java.awt.Color;
+import java.awt.ComponentOrientation;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Insets;
+import java.awt.KeyEventDispatcher;
+import java.awt.KeyboardFocusManager;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+import java.io.File;
+import java.util.Vector;
+
+import javax.print.DocFlavor;
+import javax.print.PrintService;
+import javax.print.attribute.standard.PrinterResolution;
+import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JPanel;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JSlider;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.Scrollable;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.ColorScheme;
+import org.gel.mauve.HighlightListener;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.ModelListener;
+import org.gel.mauve.MyConsole;
+import org.gel.mauve.ViewerMode;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.color.BackboneLcbColor;
+import org.gel.mauve.color.BackboneMultiplicityColor;
+import org.gel.mauve.color.LCBColorScheme;
+import org.gel.mauve.color.MultiplicityColorScheme;
+import org.gel.mauve.color.MultiplicityTypeColorScheme;
+import org.gel.mauve.color.NormalizedMultiplicityTypeColorScheme;
+import org.gel.mauve.color.NormalizedOffsetColorScheme;
+import org.gel.mauve.color.OffsetColorScheme;
+import org.gel.mauve.gui.sequence.RRSequencePanel;
+import org.gel.mauve.gui.sequence.SeqPanel;
+import org.gel.mauve.recombination.WeakArgModelBuilder;
+
+/**
+ * The primary container class for the visualization interface. For every genome
+ * being displayed, a RearrangementPanel contains a ruler (RulerPanel) and a
+ * sequence display (RRSequencePanel). For each genome, the ruler displays
+ * sequence coordinates currently in view, while the RRSequencePanel displays
+ * some sort of similarity information: either the location of exact matches or
+ * a similarity profile. The RearrangementPanel sets up the entire display and
+ * coordinates its interface. It mediates shifts in the viewable range and other
+ * user interaction. There are three primary information display modes supported
+ * by the RearrangementPanel and associated classes. Mode 1) Display a set of
+ * ungapped local alignments among multiple genomes. Mode 2) Display a set of
+ * ungapped local alignments that have been grouped into locally collinear
+ * blocks (LCBs). The LCBs are assumed to not overlap each other. In this
+ * display mode, a bounding box around each LCB is usually drawn and an
+ * LCBLinePanel can be used to draw connecting lines between LCB bounding boxes
+ * Mode 3) Display a gapped global alignment of locally collinear blocks (LCBs)
+ * stored in an XMFAAlignment object. A SimilarityIndex object is used to
+ * calculate average sequence similarity among the genomes over any arbitrary
+ * interval in one sequence. LCB bounding boxes are drawn around each LCB and
+ * within each LCB the sequence similarity profile gets displayed. An
+ * LCBLinePanel draws connecting lines among LCB bounding boxes in each genome.
+ */
+public class RearrangementPanel extends JLayeredPane implements ActionListener, ChangeListener, Scrollable, Printable, ModelListener
+{
+    private final static Integer LCB_PANEL = new Integer(2);
+    private final static Integer SEQ_PANEL = new Integer(1);
+
+    // The frame containing this RearrangementPanel
+    public MauveFrame mauveFrame;
+
+    // New-style SeqPanels.
+    Vector newPanels = new Vector();
+
+    // Panel that show lines connecing LCBs
+    LcbLinePanel lcbLinePanel;
+
+    // Panel showing the sequences, rulers, etc.
+    JPanel sequencePanel;
+
+    // A toolbar for various alignment manipulation tools, given by the parent
+    // frame
+    JToolBar toolbar;
+
+    // A slider to control the minimum weight of LCBs that get displayed. Greedy
+    // breakpoint elimination is used to remove LCBs with weights below the
+    // selected value.
+    JSlider weight_slider = new JSlider(0, 100, 0);
+
+    // A text field for weight slider
+    JTextField weight_value_text = new JTextField(5);
+
+    static final int CS_SELECT = 0;
+    static final int CS_UNIV_ZOOM = 1;
+    static final int CS_UNIV_MOVE = 2;
+    static final int CS_SEQ_ZOOM = 3;
+    static final int CS_SEQ_MOVE = 4;
+
+    public static final Color bg_color = new Color(.85f, .85f, .85f);
+
+    // Printer settings for this panel.
+    PageFormat pageFormat = new PageFormat();
+
+    // Used for printing.
+    private double printingScale = -1;
+    private int printingResolution = 600;
+    
+    // Data model.
+    BaseViewerModel model;
+
+    private boolean sliderAdjustmentInProcess = false;
+    private boolean oldDrawMatches;
+    private boolean oldFillBoxes;
+    private JToggleButton zoom_button;
+    
+    // A weird hack to change cursor for zooming.
+    private CtrlKeyDetector ctrlDetector = new CtrlKeyDetector();
+    
+    /** a list of objects listening for hint messages */
+    private EventListenerList hintMessageListeners = new EventListenerList();
+    
+    /** variables controlling the display layout */
+    private GridBagLayout gbl = new GridBagLayout();
+    private GridBagConstraints gbc = new GridBagConstraints();
+    static final double gbc_weighty = 5.0;
+
+    /**
+     * Does basic initialization for a RearrangementPanel. Call
+     * readRearrangementData() to load and display data.
+     * 
+     * @param toolbar
+     *            A toolbar which can be manipulated by this panel, or null to indicate no
+     *            toolbar will be available
+     */
+    public RearrangementPanel(JToolBar toolbar, MauveFrame parent)
+    {
+        setLayout(new FillLayout());
+        this.toolbar = toolbar;
+        mauveFrame = parent;
+    }
+
+    /**
+     * Initialize all the GUI elements.
+     */
+    public void init(BaseViewerModel model)
+    {
+        this.model = model;
+        initMatchDisplay();
+        if (model instanceof LcbViewerModel)
+        {
+            initLCBTools();
+        }
+        model.addModelListener(this);
+    }
+
+    public BaseViewerModel getModel()
+    {
+        return model;
+    }
+
+    void setNewPanels(Vector v)
+    {
+        newPanels = v;
+    }
+
+    public SeqPanel getNewPanel(int i)
+    {
+        return (SeqPanel) newPanels.elementAt(i);
+    }
+
+    public RRSequencePanel getSequencePanel(int i)
+    {
+        return getNewPanel(i).getSequencePanel();
+    }
+
+    /**
+     * This function reorders sequence data structures when the user has
+     * requested a new ordering of sequences in the display.
+     */
+    public void reorderSequences(int new_order[])
+    {
+        // reorder the new panels, names etc.
+        Vector tmp_newPanels = new Vector();
+        for (int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+        {
+            tmp_newPanels.addElement(getNewPanel(new_order[seqI]));
+        }
+        setNewPanels(tmp_newPanels);
+        sequencePanel.removeAll();
+        for (int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+        {
+        	SeqPanel sp = getNewPanel(seqI);
+        	if( seqI % 2 == 0 )
+        		sp.setBackground(null);
+        	else
+        		sp.setBackground(Color.WHITE);
+            sequencePanel.add(sp);
+            gbc.weighty = model.getGenomeByViewingIndex(new_order[seqI]).getVisible() ? gbc_weighty : 0;
+            gbl.setConstraints(sp, gbc);
+        }
+
+        // Reorder the underlying model (at some point, perhaps this
+        // will no longer be necessary?
+        model.reorderSequences(new_order);
+
+        validate();
+    }
+
+    /**
+     * Initialize the display environment. Set up a RRSequencePanel and a
+     * RulerPanel for each sequence. also populate the toolbar and set up the
+     * LCB line panel Register key bindings for various interface elements
+     */
+    protected void initMatchDisplay()
+    {
+        sequencePanel = new JPanel();
+        gbc.anchor = GridBagConstraints.CENTER;
+        gbc.fill = GridBagConstraints.BOTH;
+        gbc.gridheight = 1;
+        gbc.gridwidth = 1;
+        gbc.gridx = 0;
+        gbc.insets = new Insets(0, 0, 0, 0);
+        gbc.ipadx = 0;
+        gbc.ipady = 0;
+        gbc.weightx = 1;
+        gbc.weighty = gbc_weighty;
+        gbc.gridy = GridBagConstraints.RELATIVE;
+        sequencePanel.setLayout(gbl);
+        add(sequencePanel, SEQ_PANEL);
+        for (int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+        {
+            SeqPanel seqPanel = new SeqPanel(model, model.getGenomeByViewingIndex(seqI), this);
+            sequencePanel.add(seqPanel);
+            gbl.setConstraints(seqPanel, gbc);
+            newPanels.add(seqPanel);
+        }
+        initKeyBindings();
+        initToolbar();
+    }
+    
+    /**
+     *  
+     */
+    private void initKeyBindings()
+    {
+    	String cmd_key = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() == Event.CTRL_MASK ? "ctrl" : "meta";
+        addKeyMapping(cmd_key + " UP", "ZoomIn", this);
+        addKeyMapping(cmd_key + " DOWN", "ZoomOut", this);
+        addKeyMapping(cmd_key + " LEFT", "ScrollLeft", this);
+        addKeyMapping(cmd_key + " RIGHT", "ScrollRight", this);
+        addKeyMapping(cmd_key + " D", "DCJ", this);
+        addKeyMapping(cmd_key + " shift LEFT", "ShiftLeft", this);
+        addKeyMapping(cmd_key + " shift RIGHT", "ShiftRight", this);
+        addKeyMapping(cmd_key + " R", "Recombination", this);
+    }
+    
+    public void addKeyMapping(String stroke, String actionName, ActionListener listener)
+    {
+        getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(stroke), actionName);
+        getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(stroke), actionName);
+        getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(stroke), actionName);
+        getActionMap().put(actionName, new GenericAction(listener, actionName));
+    }
+    public void removeKeyMapping(String stroke)
+    {
+        getInputMap(WHEN_IN_FOCUSED_WINDOW).remove(KeyStroke.getKeyStroke(stroke));
+    }
+        
+
+    private boolean haveNwayLcbData()
+    {
+    	if(model instanceof LcbViewerModel)
+    	{
+    		LcbViewerModel lm = (LcbViewerModel)model;
+        	return lm.isNwayLcbList();
+    	}else if (model instanceof XmfaViewerModel)
+        {
+        	XmfaViewerModel xmfa = (XmfaViewerModel)model;
+        	return xmfa.isNwayLcbList();
+        }
+        return false;
+    }
+    
+    private boolean haveBackboneData()
+    {
+        if (model instanceof XmfaViewerModel)
+        {
+        	XmfaViewerModel xmfa = (XmfaViewerModel)model;
+        	if(xmfa.getBackboneList() != null)
+        		return true;
+        }
+        return false;
+    }
+    /**
+     *  
+     */
+    private void initToolbar()
+    {
+    	if(toolbar == null)
+    		return;
+
+        toolbar.removeAll();
+
+        // When clicked, the home button resets the display to show each genome
+        // in its entirety starting at position 1 */
+        JButton home_button = new JButton(MauveFrame.home_button_icon);
+        home_button.setToolTipText("Reset display");
+        home_button.setActionCommand("Home");
+        home_button.addActionListener(this);
+        toolbar.add(home_button);
+
+        // When clicked, the left button shifts the display 20% to the left
+        JButton left_button = new JButton(MauveFrame.left_button_icon);
+        left_button.setToolTipText("Shift display left (Ctrl+Left)");
+        left_button.setActionCommand("ShiftLeft");
+        left_button.addActionListener(this);
+        toolbar.add(left_button);
+
+        // When clicked, the right button shifts the display 20% to the right
+        JButton right_button = new JButton(MauveFrame.right_button_icon);
+        right_button.setToolTipText("Shift display right (Ctrl+Right)");
+        right_button.addActionListener(this);
+        right_button.setActionCommand("ShiftRight");
+        toolbar.add(right_button);
+
+        // When clicked, the zoom in button zooms the display 200% (halves the
+        // displayed area)
+        JButton zoomin_button = new JButton(MauveFrame.zoomin_button_icon);
+        zoomin_button.setToolTipText("Zoom in (Ctrl+Up)");
+        zoomin_button.setActionCommand("ZoomIn");
+        zoomin_button.addActionListener(this);
+        toolbar.add(zoomin_button);
+
+        // When clicked, the zoom out button zooms the display 50% (doubles the
+        // displayed area)
+        JButton zoomout_button = new JButton(MauveFrame.zoomout_button_icon);
+        zoomout_button.setToolTipText("Zoom out (Ctrl+Down)");
+        zoomout_button.setActionCommand("ZoomOut");
+        zoomout_button.addActionListener(this);
+        toolbar.add(zoomout_button);
+        
+        zoom_button = new JToggleButton(MauveFrame.zoom_button_icon);
+        zoom_button.setToolTipText("Zoom");
+        zoom_button.setActionCommand("Zoom");
+        zoom_button.addActionListener(this);
+        toolbar.add(zoom_button);
+
+        final RearrangementPanel thisrrpanel = this;
+        JButton findFeatureButton = new JButton();
+        findFeatureButton.setAction( new AbstractAction(){
+        	public void actionPerformed(ActionEvent ae){
+        		new SequenceNavigator(thisrrpanel, thisrrpanel, getModel()).showNavigator();
+        	}
+        });
+        findFeatureButton.setToolTipText("Find an annotated feature... (Ctrl+I)");
+        findFeatureButton.setIcon(MauveFrame.find_feature_icon);
+        toolbar.add(findFeatureButton);
+
+        JButton dcj_button = new JButton(MauveFrame.dcj_icon);
+	    dcj_button.setToolTipText("Perform a Block-Interchange (DCJ) rearrangement history analysis (Ctrl+D)");
+	    dcj_button.setActionCommand("DCJ");
+	    dcj_button.addActionListener(this);
+	    toolbar.add(dcj_button);
+        
+        // When clicked, the zoom out button zooms the display 50% (doubles the
+        // displayed area)
+
+	    if (haveNwayLcbData())
+	    {
+	        JButton grimm_button = new JButton(MauveFrame.grimm_icon);
+	        grimm_button.setToolTipText("Perform a GRIMM rearrangement history analysis (DCJ generalizes the GRIMM model)");
+	        grimm_button.setActionCommand("GRIMM");
+	        grimm_button.addActionListener(this);
+	        toolbar.add(grimm_button);
+        }
+	    
+	    if (model.getGenomes().size() == 2){
+	    	JButton scAss_button = new JButton(MauveFrame.scAss_icon);
+	    	scAss_button.setToolTipText("Score an assembly by DCJ, SNP, and Gap analysis");
+	    	scAss_button.setActionCommand("ScoreAssembly");
+	    	scAss_button.addActionListener(this);
+	    	toolbar.add(scAss_button);
+	    }
+        
+        // Fill out the toolbar
+        Dimension minSize = new Dimension(5, 3);
+        Dimension prefSize = new Dimension(5, 3);
+        Dimension maxSize = new Dimension(Short.MAX_VALUE, 3);
+        toolbar.add(new Box.Filler(minSize, prefSize, maxSize));
+    }
+
+
+    /**
+     * Initialize data structures to support LCB display and manipulation.
+     * Initializes the LCB weight slider.
+     */
+    protected void initLCBTools()
+    {
+        if (!(model instanceof LcbViewerModel))
+            return;
+
+        LcbViewerModel lm = (LcbViewerModel) model;
+
+        //
+        // add interface components
+        //
+        Dimension prefSize = new Dimension(5, 3);
+
+        // add the line panel to the top pane
+        lcbLinePanel = new LcbLinePanel(this, lm);
+        add(lcbLinePanel, LCB_PANEL);
+        lcbLinePanel.setVisible(true);
+
+        // add the weight slider components, only if we have nway LCBs
+        if(haveNwayLcbData() && toolbar != null && lm.getLcbCount() > 1)
+        {
+	        toolbar.add(new JLabel("LCB weight:"));
+	
+	        weight_slider.setMinimum(0);
+	        weight_slider.setMaximum(lm.getLcbChangePoints().size() - 1);
+	        weight_slider.setMinorTickSpacing(1);
+	        weight_slider.setMajorTickSpacing((int) (lm.getLcbChangePoints().size() / 10));
+	        weight_slider.setToolTipText("Set the minimum weight for Locally Collinear Blocks");
+	        weight_slider.setPaintTicks(true);
+	        weight_slider.setPaintLabels(false);
+	        weight_slider.setSnapToTicks(false);
+	        prefSize = weight_slider.getPreferredSize();
+	        prefSize.setSize(250, prefSize.getHeight());
+	        weight_slider.setPreferredSize(prefSize);
+	        weight_slider.setMaximumSize(prefSize);
+	        toolbar.add(weight_slider);
+	        weight_slider.addChangeListener(this);
+	
+	        prefSize = weight_value_text.getPreferredSize();
+	        prefSize.setSize(40, prefSize.getHeight());
+	        weight_value_text.setPreferredSize(prefSize);
+	        weight_value_text.setMaximumSize(prefSize);
+	        weight_value_text.setHorizontalAlignment(JTextField.RIGHT);
+	        weight_value_text.setEditable(false);
+	        if(lm.getLcbChangePoints().size() > 0)
+	        	weight_value_text.setText(lm.getLcbChangePoints().elementAt(0).toString());
+	        toolbar.add(weight_value_text);
+	        // tell the user how many LCBs there are
+	        String status_text = "There are " + lm.getVisibleLcbCount() + " LCBs with minimum weight " + lm.getMinLCBWeight();
+	        fireHintMessageEvent(status_text);
+        }
+    }
+
+    /**
+     * Add a HintMessageListener to the list of listeners for the model.
+     * 
+     * @param l listener to add
+     */
+    public void addHintMessageListener(HintMessageListener l) {
+    	hintMessageListeners.add(HintMessageListener.class, l);
+    }
+
+    /**
+     * Remove a HintMessageListener from the list of listeners for this model.
+     * 
+     * @param l listener to remove
+     */
+    public void removeHintMessageListenerListener(HintMessageListener l) {
+    	hintMessageListeners.remove(HintMessageListener.class, l);
+    }
+    
+    /**
+     * Invoke {@link HintMessageListener.messageChanged(ModelEvent)} on
+     * this model's collection of HintMessageListener.
+     */
+    protected void fireHintMessageEvent(String message) {
+        Object[] listeners = hintMessageListeners.getListenerList();
+        for (int i = 0; i < listeners.length; i++) {
+            if (listeners[i]==HintMessageListener.class) {
+                ((HintMessageListener)listeners[i+1]).messageChanged(new HintMessageEvent(this,message));
+            }
+        }
+    }
+    
+    // CHANGE LISTENER METHODS
+
+    public void stateChanged(ChangeEvent e)
+    {
+        if (e.getSource() == weight_slider)
+        {
+
+            if (!(model instanceof LcbViewerModel))
+                return;
+
+            LcbViewerModel model = (LcbViewerModel) this.model;
+
+            // compute the updated list of LCBs
+            int w_int = weight_slider.getValue();
+            int min_weight = ((Integer) model.getLcbChangePoints().elementAt(w_int)).intValue();
+            weight_value_text.setText(model.getLcbChangePoints().elementAt(w_int).toString());
+
+            if (weight_slider.getValueIsAdjusting())
+            {
+                if (model instanceof LcbViewerModel)
+                {
+                    LcbViewerModel lm = (LcbViewerModel) model;
+                    if (!sliderAdjustmentInProcess)
+                    {
+                        oldFillBoxes = lm.getFillLcbBoxes();
+                    }
+                    lm.setFillLcbBoxes(true);
+                }
+                
+                if (!sliderAdjustmentInProcess)
+                {
+                    oldDrawMatches = model.getDrawMatches();
+                }
+                model.setDrawMatches(false);
+                sliderAdjustmentInProcess = true;
+            }
+            else
+            {
+                if (model instanceof LcbViewerModel)
+                {
+                    ((LcbViewerModel) model).setFillLcbBoxes(oldFillBoxes);
+                }
+                model.setDrawMatches(oldDrawMatches);
+                sliderAdjustmentInProcess = false;
+            }
+            
+            model.updateLCBweight(min_weight, weight_slider.getValueIsAdjusting());
+
+            if (weight_slider.getValueIsAdjusting())
+            {
+                String status_text = "There are " + model.getVisibleLcbCount() + " LCBs with minimum weight " + min_weight;
+                fireHintMessageEvent(status_text);
+            }
+        }
+    }
+
+    // ACTION LISTENER METHODS
+    
+    /**
+     * 
+     */
+    public void actionPerformed(ActionEvent e)
+    {
+    	if (e.getActionCommand().equals("Home"))
+        {
+            model.zoomAndMove(0, Integer.MIN_VALUE);
+            
+            //			zoom( 100, 100 );
+        }
+        else if (e.getActionCommand().equals("ShiftLeft") || e.getActionCommand().equals("ScrollLeft"))
+        {
+            if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0)
+                // big jump left
+                model.zoomAndMove(100, -50);
+            else
+                // scroll left
+                model.zoomAndMove(100, -10);
+        }
+        else if (e.getActionCommand().equals("ShiftRight") || e.getActionCommand().equals("ScrollRight"))
+        {
+            if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0)
+            {
+                // big jump right
+                model.zoomAndMove(100, 50);
+            }
+            else
+            {
+                // scroll right
+                model.zoomAndMove(100, 10);
+            }
+        }
+        else if (e.getActionCommand().equals("ZoomIn"))
+        {
+            model.zoomAndMove(200, 0);
+        }
+        else if (e.getActionCommand().equals("ZoomOut"))
+        {
+            model.zoomAndMove(50, 0);
+        }
+        else if (e.getActionCommand().equals("DCJ"))
+        {
+        	if( model instanceof LcbViewerModel )
+        	{
+        		//org.gel.mauve.dcjx.DCJDistance.launchWindow(model);
+        		org.gel.mauve.dcjx.DCJDistance.launchWindow(model);
+        	}
+        }
+        else if (e.getActionCommand().equals("Recombination"))
+        {
+        	if( model instanceof XmfaViewerModel )
+        	{
+        		try{
+        			final JFileChooser fc = new JFileChooser();
+        			int ret = fc.showOpenDialog(this);
+                    if (ret == JFileChooser.APPROVE_OPTION)
+                    {
+                    	SwingWorker sw = new SwingWorker() {
+							public Object construct() {
+								try{
+									return WeakArgModelBuilder.buildModel(fc.getSelectedFile(), (XmfaViewerModel)model);
+								}catch(Exception e){
+									e.printStackTrace();
+									throw new RuntimeException("Failed to load Weak ARG xml data");
+								}
+							}
+						};
+						sw.start();
+                    }        			
+        		}catch(Exception ee){
+        			ee.printStackTrace();
+        		}
+        	}
+        }
+        else if (e.getActionCommand().equals("ScoreAssembly"))
+        {
+        	if( model instanceof LcbViewerModel ){
+        		org.gel.mauve.assembly.ScoreAssembly.launchWindow(mauveFrame);
+        	}
+        }
+        else if (e.getActionCommand().equals("GRIMM"))
+        {
+        	if( model instanceof LcbViewerModel )
+        	{
+        		LcbViewerModel lvm = (LcbViewerModel)model;
+        		lvm.launchGrimmMGR();
+        	}
+        }
+        else if (e.getActionCommand().equals("Zoom"))
+        {
+            if (model.getMode() == ViewerMode.ZOOM)
+            {
+                model.setMode(ViewerMode.NORMAL);
+            }
+            else
+            {
+                model.setMode(ViewerMode.ZOOM);
+            }
+        }
+        else if (e.getActionCommand().equals("ZoomInMode"))
+        {
+            setCursor(MauveFrame.zoom_in_cursor);
+        }
+        else if (e.getActionCommand().equals("ZoomOutMode"))
+        {
+            setCursor(MauveFrame.zoom_out_cursor);
+        }
+    }
+    public Dimension getPreferredSize()
+    {
+    	Dimension min_size = new Dimension();
+    	Dimension max_size = new Dimension();    	
+    	// add up the minimum and maximum sizes of each sequence panel
+    	for( int seqI = 0; seqI < model.getSequenceCount(); seqI++ ){
+    		min_size.height += this.getNewPanel(seqI).getMinimumSize().height;
+    		max_size.height += this.getNewPanel(seqI).getMaximumSize().height;
+    	}
+    	// set the preferred height to be no less than the minimum
+    	// and no more than the maximum
+    	Dimension preferred_size = this.getParent().getSize();
+    	Dimension super_size = super.getPreferredSize();
+    	preferred_size.width = super_size.width;
+    	if( preferred_size.height < min_size.height )
+    		preferred_size.height = min_size.height;
+    	else if( preferred_size.height > max_size.height )
+    		preferred_size.height = max_size.height;
+    	else      						// TODO: clean up this hack!!
+    		preferred_size.height -= 5;	// the height is just a little too big
+    	return preferred_size;
+    }
+
+    /**
+     * @see javax.swing.Scrollable#getScrollableTracksViewportHeight()
+     */
+    public boolean getScrollableTracksViewportHeight()
+    {
+        // Don't match component height to viewport height.
+        return false;
+    }
+
+    boolean scrollableTracksViewportWidth = true;	// default to true
+    /**
+     * Match component width to viewport width?
+     */ 
+    public void setScrollableTracksViewportWidth(boolean matchWidth)
+    {
+    	scrollableTracksViewportWidth = matchWidth;
+    }
+    /**
+     * @see javax.swing.Scrollable#getScrollableTracksViewportWidth()
+     */
+    public boolean getScrollableTracksViewportWidth()
+    {
+        // Match component width to viewport width.
+        return scrollableTracksViewportWidth;
+    }
+
+    /**
+     * @see javax.swing.Scrollable#getPreferredScrollableViewportSize()
+     */
+    public Dimension getPreferredScrollableViewportSize()
+    {
+        return getPreferredSize();
+    }
+
+    /**
+     * @see javax.swing.Scrollable#getScrollableBlockIncrement(java.awt.Rectangle,
+     *      int, int)
+     */
+    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
+    {
+        return 10;
+    }
+
+    /**
+     * @see javax.swing.Scrollable#getScrollableUnitIncrement(java.awt.Rectangle,
+     *      int, int)
+     */
+    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
+    {
+        return 1;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.awt.print.Printable#print(java.awt.Graphics,
+     *      java.awt.print.PageFormat, int)
+     */
+    public int print(Graphics g, PageFormat pageFormat, int pageIndex)
+    {
+        if (pageIndex > 0)
+        {
+            return NO_SUCH_PAGE;
+        }
+        else
+        {
+            Graphics2D g2d = (Graphics2D) g;
+            
+            if (printingScale == -1)
+            {
+                printingScale = pageFormat.getImageableWidth() / getWidth();
+            }
+            
+            g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
+            g2d.scale(printingScale, printingScale);
+            PrintUtilities.disableDoubleBuffering(this);
+            
+            g2d.setRenderingHint(MauveRenderingHints.KEY_SIMILARITY_DENSITY, new Double(72d / (printingResolution * printingScale)));
+            paint(g2d);
+
+            PrintUtilities.enableDoubleBuffering(this);
+            return PAGE_EXISTS;
+        }
+    }
+    
+    public void print()
+    {
+        PrinterJob printJob = PrinterJob.getPrinterJob();
+        printJob.setPrintable(this, pageFormat);
+        
+        if (printJob.printDialog())
+        {
+            try
+            {
+                printingScale = -1;
+                printingResolution = determineResolution(printJob);
+                model.firePrintingStartEvent();
+                validate();	// ensure that any layout changes get propagated
+                printJob.print();
+            }
+            catch (PrinterException pe)
+            {
+                MyConsole.err().println("Error printing: " + pe);
+            }
+            model.firePrintingEndEvent();
+        }
+    }
+    
+    /**
+     * @param printJob
+     */
+    private static int determineResolution(PrinterJob printJob)
+    {
+        PrintService ps = printJob.getPrintService();
+        // If we can't tell, assume 300 dpi.
+        if (ps == null || !ps.isAttributeCategorySupported(PrinterResolution.class) || ps.isDocFlavorSupported(DocFlavor.SERVICE_FORMATTED.PRINTABLE))
+        {
+            return 300;
+        }
+        
+        // Try 300 dpi.
+        PrinterResolution pr = new PrinterResolution(300,300,PrinterResolution.DPI);
+        if (ps.isAttributeValueSupported(pr, DocFlavor.SERVICE_FORMATTED.PRINTABLE, null))
+        {
+            return 300;
+        }
+            
+        // Fourth down: punt!
+        return 72;
+    }
+
+    public void pageSetup()
+    {
+        PrinterJob junkJob = PrinterJob.getPrinterJob();
+        pageFormat = junkJob.pageDialog(pageFormat);
+    }
+
+    public void colorChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void weightChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void drawingSettingsChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+    
+    public void referenceChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void modeChanged(ModelEvent event)
+    {
+        if (model.getMode() == ViewerMode.NORMAL)
+        {
+            zoom_button.setSelected(false);
+            setCursor(null);
+            KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(ctrlDetector);
+        }
+        else if (model.getMode() == ViewerMode.ZOOM)
+        {
+            setCursor(MauveFrame.zoom_in_cursor);
+
+            // See javadoc for CtrlKeyDetector for an explanation of the next line.
+            KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(ctrlDetector);
+        }
+    }
+
+    
+    /**
+     * CtrlKeyDetector is used to capture the press and release of the Ctrl key
+     * so that the cursor for zooming in and out can be changed.  This is necessary
+     * because the normal key event handling routines do not provide access to events
+     * where just the Ctrl key and no other keys are pressed or released.  
+     */
+    class CtrlKeyDetector implements KeyEventDispatcher {
+        public boolean dispatchKeyEvent(KeyEvent e) {
+            if( e.getKeyCode() == KeyEvent.VK_CONTROL ) {
+                switch( e.getID() ) {
+                	case KeyEvent.KEY_PRESSED:
+                        setCursor(MauveFrame.zoom_out_cursor);
+                	    break;
+                	case KeyEvent.KEY_RELEASED:
+                        setCursor(MauveFrame.zoom_in_cursor);
+                	    break;
+                }
+            }
+            return false;
+        }
+    }
+
+    public void viewableRangeChanged(ModelEvent event)
+    {
+        // Ignored
+    }
+
+    public void viewableRangeChangeStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void viewableRangeChangeEnd(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void genomeVisibilityChanged(ModelEvent event)
+    {
+    	// update constraints, setting invisible genomes to weighty 0
+    	for(int i = 0; i < model.getSequenceCount(); i++)
+    	{
+    		gbc.weighty = model.getGenomeByViewingIndex(i).getVisible() ? gbc_weighty : 0;
+    		gbl.setConstraints(getNewPanel(i), gbc);
+    	}
+	}
+
+    public void modelReloadStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+    
+    public void modelReloadEnd(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void genomesReordered(ModelEvent event)
+    {
+        // TODO Auto-generated method stub
+        
+    }
+
+    public void printingStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void printingEnd(ModelEvent event)
+    {
+        // Ignored.
+    }
+    public void attributesChanged(ModelEvent event) {}
+
+}
+
+/**
+ * Generic class to propagate action events to a listener
+ */
+
+class GenericAction extends AbstractAction
+{
+
+    GenericAction(ActionListener al, String command)
+    {
+        super(command);
+        this.al = al;
+        putValue(ACTION_COMMAND_KEY, command);
+    }
+
+    ActionListener al;
+
+    public void actionPerformed(ActionEvent e)
+    {
+        al.actionPerformed(e);
+    }
+}
+
+
diff --git a/src/org/gel/mauve/gui/SequenceNavigator.java b/src/org/gel/mauve/gui/SequenceNavigator.java
new file mode 100644
index 0000000..5f0c809
--- /dev/null
+++ b/src/org/gel/mauve/gui/SequenceNavigator.java
@@ -0,0 +1,745 @@
+package org.gel.mauve.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Toolkit;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Vector;
+
+import javax.swing.*;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.SeqFeatureData;
+import org.gel.mauve.gui.navigation.NavigationPanel;
+import org.gel.mauve.gui.navigation.SearchResultPanel;
+import org.gel.mauve.gui.sequence.RRSequencePanel;
+import org.gel.mauve.gui.sequence.SeqPanel;
+
+import org.biojava.bio.seq.Feature;
+
+
+/**
+ * A gui that allows a user to search genome features by annotation.  Allows multiple
+ * constraints.  Results are shown in a tree, and selecting the feature in the tree
+ * scrolls to that position in the genome.  Results can be added to previous search
+ * results, or previous results can be cleared.
+ * 
+ * @author rissman
+ *
+ */
+public class SequenceNavigator extends JSplitPane implements ActionListener, 
+		KeyListener, MauveConstants {
+
+	/**
+	 * gui components needed throughout the class
+	 */
+	protected Component parent_component;	/**< The parent component of the panel, if it disappears, so will the panel */
+	protected RearrangementPanel rrpanel;	/**< The panel displaying data under navigation */
+	protected BaseViewerModel data_model;	/**< The data model being displayed and navigated */
+	/**
+	 * parent frame containing this panel
+	 */
+	protected JFrame frame;
+	
+	/**
+	 * list of genomes with searchable features
+	 */
+	protected JComboBox genomes;
+	
+	/**
+	 * each NavPanel in the list represents a constraint on the current search
+	 */
+	protected LinkedList nav_panels;
+	
+	/**
+	 * when pressed, a search is performed
+	 */
+	protected JButton search;
+	protected JButton cancel;
+	protected JButton add;
+	
+	/**
+	 * when pressed, resets search to one constraint with no values entered
+	 */
+	protected JButton reset;
+	
+	/**
+	 * panel that contains all NavPanels
+	 */
+	protected JPanel nav_panel_holder;
+	
+	/**
+	 * panel that shows results
+	 */
+	protected SearchResultPanel result_pane;
+	
+	/**
+	 * if checked, previous search results are cleared when a new search is performed
+	 */
+	protected JCheckBox clear;
+	
+	/**
+	 * allows results to be scrolled through
+	 */
+	protected JScrollPane result_scroller;
+	
+	/**
+	 * allows constraints to be scrolled through
+	 */
+	protected JScrollPane nav_scroll;
+	protected LinkedList window_listeners;
+	protected WindowAdapter adapt;
+	
+	/**
+	 * if there is no frame containing the alignment, so when it is removed,
+	 * this SequenceNavigator can be closed
+	 */
+	protected AncestorListener ancestListener;
+	
+	/**
+	 * availabe genome sequences to search
+	 */
+	protected Vector genome_choices;
+	
+	/**
+	 * sets maximum height programmatically given to the frame
+	 */
+	public static int MAX_HEIGHT = 400;
+	
+	/**
+	 *sets maximum height and width programmatically given to the frame.
+	 **/
+	 public static int MAX_WIDTH = 850;
+
+	
+	/**
+	 * initializes hashtable and hashset inherited from NavigationConstants
+	 */
+	static {
+		READ_TO_ACTUAL.put(NAME, LOC_NAME);
+		READ_TO_ACTUAL.put(PRODUCT, PRODUCT_NAME);
+		READ_TO_ACTUAL.put(ID, ID_NUMBER);
+		READ_TO_ACTUAL.put(GO, GO_FEATS);
+		ANNOTATION_KEYS.add ("biovar");
+		ANNOTATION_KEYS.add ("codon_start");
+		ANNOTATION_KEYS.add ("db_xref");
+		ANNOTATION_KEYS.add ("function");
+		ANNOTATION_KEYS.add ("gene");
+		ANNOTATION_KEYS.add ("insertion_seq");
+		ANNOTATION_KEYS.add ("internal_data");
+		ANNOTATION_KEYS.add ("locus_tag");
+		ANNOTATION_KEYS.add ("mol_type");
+		ANNOTATION_KEYS.add ("note");
+		ANNOTATION_KEYS.add ("organism");
+		ANNOTATION_KEYS.add ("product");
+		ANNOTATION_KEYS.add ("protein_id");
+		ANNOTATION_KEYS.add ("pseudo");
+		ANNOTATION_KEYS.add ("strain");
+		ANNOTATION_KEYS.add ("transl_except");
+		ANNOTATION_KEYS.add ("transl_table");
+		ANNOTATION_KEYS.add ("translation");
+	}
+	
+	/**
+	 * mechanism for only doing one gui operation at a time/locking
+	 */
+	private Boolean current_search = Boolean.FALSE;
+	
+	
+	/**
+	 * Creates new GenomeNavigator
+	 * 
+	 * @param frame the MauveFrame this navigator should be associated with
+	 */
+	public SequenceNavigator (MauveFrame frame) {
+		super ();
+		parent_component = frame.getRootPane();
+		data_model = frame.getModel();
+		nav_panels = new LinkedList ();
+		rrpanel = frame.getRearrangementPanel ();
+		initGUI ();
+	}
+	public SequenceNavigator (Component parent, RearrangementPanel rrpanel, BaseViewerModel dataModel) 
+	{
+		super ();
+		parent_component = parent;
+		data_model = dataModel;
+		this.rrpanel = rrpanel;
+		nav_panels = new LinkedList ();
+		initGUI ();
+	}
+	
+	/**
+	 *Does initial gui initialization
+	 *
+	 */
+	private void initGUI () {
+		makeFrame ();
+		//setOneTouchExpandable (true);
+		setResizeWeight (0);
+		JPanel left = new JPanel (new BorderLayout (BORDER, BORDER));
+		//makes panel for choosing genome
+		JPanel top1 = new JPanel ();
+		top1.setLayout(new BoxLayout (top1, BoxLayout.X_AXIS));
+		top1.add (new JLabel ("Choose Genome:"));
+		genomes = new JComboBox ();
+		loadGenomeList ();
+		top1.add (genomes);
+		left.add (top1, BorderLayout.NORTH);	
+		left.setBorder (BorderFactory.createEmptyBorder(3, 3, 3, 3));
+		JPanel middle = new JPanel (new BorderLayout ());
+		nav_panel_holder = new JPanel ();
+		middle.setBorder (BorderFactory.createTitledBorder(
+				BorderFactory.createCompoundBorder (BorderFactory.createLineBorder (Color.black),
+						BorderFactory.createEmptyBorder(BORDER, BORDER, 0, BORDER)), 
+						"Find features with the following qualifying information:"));
+		nav_panel_holder.setLayout (new BoxLayout (nav_panel_holder, BoxLayout.Y_AXIS));
+		result_scroller = result_pane.getScrollPane ();
+		nav_scroll = new JScrollPane (nav_panel_holder);
+		new NavigationPanel (this);
+		middle.add (nav_scroll, BorderLayout.CENTER);
+		left.add (middle, BorderLayout.CENTER);
+		makeBottomPanel (left);
+		setLeftComponent (left);
+		setRightComponent (result_scroller);
+		frame.getContentPane ().add (this);
+		Dimension preferred = middle.getPreferredSize ();
+		preferred.width += 6;
+		preferred.height += 6;
+		left.setMinimumSize (new Dimension (preferred.width,
+				left.getPreferredSize ().height));
+		left.setMaximumSize(new Dimension (preferred.width, -1));
+		frame.pack ();
+		reloadGUI ();
+		moveFromBehind ();
+	}
+	
+	/**
+	 * sets up the frame that contains this SequenceNavigation panel 
+	 * and its subcomponents
+	 *
+	 */
+	private void makeFrame () {
+		window_listeners = new LinkedList ();
+		frame = new JFrame ("Sequence Navigator");
+		frame.setIconImage(MauveFrame.mauve_icon.getImage());
+		((JPanel) frame.getContentPane ()).setBorder (
+				BorderFactory.createEmptyBorder(10, 10, 10, 10));
+		adapt = new WindowAdapter () {
+			public void windowClosing (WindowEvent e) {
+				frame.setVisible (false);
+			}
+		};
+		ancestListener = new AncestorListener(){
+			public void ancestorMoved(AncestorEvent ae){}
+			public void ancestorAdded(AncestorEvent ae){}
+			public void ancestorRemoved(AncestorEvent ae){
+				frame.setVisible(false);
+			}
+		};
+		if(parent_component instanceof Frame)
+			((Frame)parent_component).addWindowListener(adapt);
+		else if(parent_component instanceof JComponent)
+			((JComponent)parent_component).addAncestorListener(ancestListener);
+		/*mauve_frame.addWindowListener(new WindowAdapter () {
+			public void windowClosing (WindowEvent e) {
+				frame.setVisible (false);
+			}
+		});*/
+	}
+	
+	/**
+	 * initializes bottom part of left side of gui
+	 * 
+	 * @param holder		The panel in which to put the bottom component
+	 */
+	private void makeBottomPanel (JPanel holder) {
+		search = new JButton ("Search");
+		cancel = new JButton ("Close");
+		add = new JButton ("Add Constraint");
+		reset = new JButton ("Reset Constraints");
+		JPanel all = new JPanel (new BorderLayout ());
+		clear = new JCheckBox ("Clear previous results when adding new");
+		clear.setSelected(true);
+		JPanel bottom = new JPanel ();
+		bottom.add(add);
+		bottom.add(reset);
+		bottom.add (search);
+		bottom.add (cancel);
+		search.addActionListener (this);
+		search.addKeyListener (this);
+		cancel.addActionListener (this);
+		add.addActionListener (this);
+		reset.addActionListener (this);
+		JPanel temp = new JPanel ();
+		temp.add (clear);
+		all.add(temp, BorderLayout.NORTH);
+		all.add(bottom, BorderLayout.SOUTH);
+		//setBorder (BorderFactory.createEmptyBorder (10, 10, 10, 10));
+		holder.add (all, BorderLayout.SOUTH);
+	}
+	
+	/**
+	 * finds the best place on screen to place the frame-
+	 * takes into account size of mauve frame
+	 *
+	 */
+	//not sure how well this works in all cases, has done what I need so far
+	protected void moveFromBehind () {
+		Dimension needed = frame.getSize();
+		int area = 0;
+		Dimension total = Toolkit.getDefaultToolkit().getScreenSize();
+		Dimension taken = parent_component.getSize ();
+		int extra = total.width - taken.width;
+		int x = 0;
+		int y = 0;
+		if (extra >= needed.width)
+			x = taken.width;
+		else {
+			area = extra * needed.height;
+			extra = total.height - taken.height;
+			if (extra >= needed.height)
+				y = taken.height;
+			else if (area > extra * needed.width)
+				x = total.width - needed.width;
+			else
+				y = total.height - needed.height;
+		}
+		frame.setLocation(x, y);
+	}
+	
+	/**
+	 * loads list of genomes user is viewing and should be able to search
+	 *
+	 */
+	public void loadGenomeList () {
+		genome_choices = SeqFeatureData.userSelectableGenomes (data_model, true, true);
+		genomes.setRenderer (GenomeCellRenderer.getListCellRenderer ());
+		genomes.setModel (new DefaultComboBoxModel (genome_choices));
+		result_pane = new SearchResultPanel (SeqFeatureData.userSelectableGenomes (
+				data_model, false, false), this);
+	}
+	
+	/**
+	 * determines whether current results should be cleared
+	 * 
+	 * @return		True if they should, false otherwise
+	 */
+	public boolean shouldClear () {
+		return clear.isSelected ();
+	}
+	
+	/**
+	 * adds a newly constructed navigation panel to the gui
+	 */
+	public void addNavigationPanel (NavigationPanel pane) {
+		nav_panels.addFirst (pane);
+		nav_panel_holder.add (pane);
+		pane.setMaximumSize (pane.getPreferredSize());
+		reloadGUI ();
+			
+	}
+	
+	/**
+	 * removes unwanted navigation panel from gui
+	 */
+	public void removeNavigationPanel (NavigationPanel pane) {
+		if (nav_panels.size() != 1) {
+			nav_panels.remove(pane);
+			nav_panel_holder.remove(pane);
+			reloadGUI ();
+		}
+	}
+	
+	/**
+	 * resets search so only one navigation panel is present
+	 */
+	public void reset () {
+		Object [] panels = nav_panels.toArray();
+		for (int i = 0; i < panels.length - 1; i++)
+			removeNavigationPanel ((NavigationPanel) panels [i]);
+	}
+	
+	/**
+	 * Shows the navigator so a user may select a genome
+	 * and a position to go to within the genome.
+	 *
+	 */
+	public void showNavigator () {
+		frame.setVisible (true);
+	}
+	
+	
+	/**
+	 * action listener for buttons on this panel
+	 * 
+	 * @param e If the source of the action is the search button,
+	 * 			checks for valid input and performs search
+	 * 			If the source is the cancel button, hides the Navigator
+	 * 			If the source is the add button, adds a panel for the use to
+	 * 			enter a new constraint in
+	 * 			If the source is reset, removes all the input panel except the first
+	 */
+	public void actionPerformed (final ActionEvent e) {
+		if (e.getSource () == search) {
+			Thread t = new Thread () {
+				public void run () {
+					synchronized (current_search) {
+						if (current_search == Boolean.FALSE) {
+							current_search = Boolean.TRUE;
+						}
+						else
+							return;
+					}
+					try {
+						doNavigation ();
+					} catch (Exception e) {
+						e.printStackTrace ();
+					}
+					current_search = Boolean.FALSE;
+				}
+			};
+			//threads.add(t);
+			t.start ();
+		}
+		if (current_search == Boolean.FALSE) {
+			if (e.getSource () == add)
+				new NavigationPanel (SequenceNavigator.this);
+			else if (e.getSource() == cancel)
+				frame.setVisible (false);
+			else if (e.getSource () == reset)
+				reset ();
+		}
+	}
+	
+	/**
+	 * necessary to call for some reason whenever adding or removing
+	 * NavigationPanels-- automatically called from addNavigationPanel and
+	 * removeNavigationPanel
+	 *
+	 */
+	public void reloadGUI () {
+		Dimension size = frame.getSize ();
+		expandIfNecessary (nav_scroll, size);
+		expandIfNecessary (result_scroller, size);
+		size.width = (size.width > MAX_WIDTH) ? MAX_WIDTH : size.width;
+		size.height = (size.height > MAX_HEIGHT) ? MAX_HEIGHT : size.height;
+		frame.setSize(size);
+		revalidate ();
+		repaint ();
+	}
+	
+	
+	/**
+	 * Expands the frame holding the gui as much as necessary without
+	 * allowing it to increase over the maximum programmatically 
+	 * allowable size
+	 * 
+	 * @param pane			Holds the component that might need more size
+	 * @param size			The size of the frame before the resize
+	 */
+	public static void expandIfNecessary (JScrollPane pane, Dimension size) {
+		JViewport port = pane.getViewport ();
+		Dimension preferred = port.getView ().getPreferredSize ();
+		Dimension actual = port.getSize ();
+		if (preferred.width > actual.width)
+			size.width += preferred.width - actual.width;
+		if (preferred.height > actual.height)
+			size.height += preferred.height - actual.height;
+	}
+
+	/**
+	 * dummy method - implemented as part of key listener interface
+	 */
+	public void keyPressed (KeyEvent e) {
+	}
+	
+	/**
+	 * dummy method - implemented as part of key listener interface
+	 */
+	public void keyReleased (KeyEvent e) {
+	}
+	
+	/**
+	 * converts enters typed into the input panels to 
+	 * action events representing a user desire to perform search
+	 */
+	public void keyTyped (KeyEvent e) {
+		if (e.getKeyChar () == '\n') {
+			ActionEvent ae = new ActionEvent (search, ActionEvent.ACTION_PERFORMED, null);
+			actionPerformed (ae);
+		}
+	}
+	
+	/**
+	 * scrolls the gui to a specific part of the list of constraints
+	 * 
+	 * @param panel			The NavigationPanel that should be visible on screen
+	 */
+	public void makeConstraintVisible (NavigationPanel panel) {
+		nav_scroll.getViewport ().scrollRectToVisible (panel.getBounds ());
+	}
+
+	/**
+	 * Finds out if the data entered into the input panels is valid,
+	 * and how many valid constraints are possible.
+	 * Input is considered valid if there are no breaks in input;
+	 * if all fields are filled in.  Will allow empty constraints at the bottom
+	 * 
+	 * @return			The number of valid constraints
+	 */
+	public int getValidCount () {
+		int count = 0;
+		boolean hole = false;
+		Object [] items = nav_panels.toArray ();
+		for (int i = items.length - 1; i >= 0; i--) {
+			NavigationPanel pan = (NavigationPanel) items [i];
+			if (pan.dataValid ()) {
+				if (hole) {
+					JOptionPane.showMessageDialog(frame, "Missing or invalid data.\n" +
+							"Can't perform search.", "Navigation Error", JOptionPane.ERROR_MESSAGE);
+					return 0;
+				}
+				else
+					count++;
+			}
+			else
+				hole = true;
+		}
+		return count;
+	}
+	
+	
+	/**
+	 * A simple wizard style routine that allows a user to choose a numeric
+	 * sequence position from a specific sequence to navigate to, and performs
+	 * navigation
+	 *
+	 * @param parentComponent	The parent in the GUI heirarchy
+	 * @param dataModel		A viewer data model containing sequence data of interest
+	 * @param rrpanel		The rearrangement panel to adjust when the user selects a seq coordinate
+	 */
+	public static void goToSeqPos (Component parentComponent, BaseViewerModel dataModel, RearrangementPanel rrpanel) {
+		Genome [] chosen = SeqFeatureData.userSelectedGenomes (parentComponent, 
+				dataModel, false, false);
+		if (chosen != null) {
+			long pos = -1;
+			do {
+				try {
+					String input = JOptionPane.showInputDialog (parentComponent, 
+					"Enter sequence coordinate to jump to...");
+					if (input != null)
+						pos = Long.parseLong (input);
+					else
+						break;
+				} catch (NumberFormatException e) {
+					JOptionPane.showMessageDialog(parentComponent, "Invalid position entered",
+							"Invalid Data", JOptionPane.ERROR_MESSAGE);
+				}
+			} while (pos == -1);
+			if (pos != -1)
+				goToPosition (pos, chosen [0], rrpanel);
+		}
+	}
+	
+	
+	/**
+	 * A wizard style simplified feature searcher that allows the user
+	 * to find a feature by name and highlights the first feature
+	 * in the mauve gui that matches the desired name
+	 *
+	 */
+	public void goToFeatureByName () {
+		Genome [] chosen = SeqFeatureData.userSelectedGenomes (
+				parent_component, data_model, true, true);
+		if (chosen != null) {
+			String input = JOptionPane.showInputDialog (parent_component, 
+			"Enter name of desired feature. . .");
+			if (input != null) {
+				String [][] data = new String [1][3];
+				data [0][FIELD] = LOC_NAME;
+				data [0][VALUE] = input;
+				data [0][EXACT] = Boolean.toString(false);
+				int count = 0;
+				LinkedList first = null;
+				LinkedList [] tree_data = SeqFeatureData.findFeatures (chosen, data);
+				for (int i = 0; i < chosen.length; i++) {
+					Object genome = tree_data [i].removeFirst ();
+					SeqFeatureData.removeLocationDuplicates (tree_data [i]);
+					count += tree_data [i].size();
+					tree_data [i].addFirst (genome);
+					if (count == 1) {
+						first = tree_data [i];
+					}
+				}
+				if (count == 1)
+					displayFeature ((Feature) first.get(1),	(Genome) first.getFirst());
+				else if (count == 0) {
+					JOptionPane.showMessageDialog(parent_component, 
+							"No Features were found with specified name.");
+				}
+				else {
+					frame.setVisible(true);
+					result_pane.displayFeatures(tree_data);
+				}
+			}
+		}
+		
+	}
+	
+	/**
+	 * converts user input to features to display
+	 * and centers view on first matching feature
+	 *
+	 */
+	protected void doNavigation () {
+		int index = genomes.getSelectedIndex();
+		int valid = getValidCount ();
+		if (valid > 0) {
+			result_pane.waitForResults ();
+			String [][] criteria = new String [valid][3];
+			for (int i = 0; i < criteria.length; i++)
+				criteria [i] = ((NavigationPanel) nav_panels.get(
+						nav_panels.size() - 1 - i)).getSearchCriteria ();
+			showResultTree (SeqFeatureData.convertIndexToSequence (
+					genome_choices, index), criteria);
+		}
+	}
+	
+	
+	/**
+	 * Performs the final narrowing down of features to those that match the
+	 * given constraints, and displays a tree of matching features
+	 * 
+	 * @param nomes			The genomes the data is from
+	 * @param data			A two dimensional array; contains the constraints to
+	 * 						search by
+	 */
+	public void showResultTree (final Genome [] nomes, final String [][] data) {
+        SwingUtilities.invokeLater(new Runnable(){
+            public void run(){
+		result_pane.displayFeatures (SeqFeatureData.findFeatures (nomes, data));
+            }
+        });
+	}
+
+	/**
+	 * Centers the view on a specific feature of a genome
+	 * 
+	 * @param feat			The feature to center on
+	 * @param genome		The genome containing the feature
+	 */	
+	public void displayFeature (Feature feat, Genome genome) {
+		try {
+			adjustZoom(feat);
+			goToPosition(SeqFeatureData.centerOfFeature(feat), genome, rrpanel);
+			data_model.highlightRange(genome,
+					feat.getLocation().getMin(), feat.getLocation().getMax());
+		} catch (Exception e) {
+			e.printStackTrace ();
+		}		
+	}
+
+	/**
+	 * Adjust zoom so desired feature is a decent size and is all viewable
+	 * @param feat
+	 */
+	public void adjustZoom (Feature feat) {
+		int length = feat.getLocation().getMax () - feat.getLocation ().getMin();
+		long vis_length = ((Genome) genomes.getItemAt(
+				genomes.getItemCount() == 1 ? 0 : 1)).getViewLength ();
+		double percent = 0;
+		double new_vis = 0;
+		int count = 0;
+		if (length < vis_length) {
+			new_vis = length * 10;
+			if (new_vis < vis_length) {
+				percent = ((double) vis_length) / new_vis;
+				percent *= 100;
+			}
+		}
+		else {
+			new_vis = length + 1/3*((double) length);
+			percent = vis_length/new_vis * 100;
+			while (percent < 1) {
+				percent *= 2;
+				count++;
+			}
+		}
+		if (percent != 0) {
+			data_model.zoomAndMove ((int) percent, 0);
+			while (count > 0) {
+				data_model.zoomAndMove (50, 0);
+				count--;
+			}
+		}
+	}
+	
+	/**
+	 * Centers view on a specific sequence position
+	 * 
+	 * @param position 		the position to center on
+	 * @param chosen   		the genome to find the position in
+	 * @param mf			the mauve frame containing the p
+	 */
+	public static void goToPosition (long position, Genome chosen, RearrangementPanel rrpanel) {
+		Object [] panels = rrpanel.newPanels.toArray ();
+		for (int i = 0; i < panels.length; i++) {
+			RRSequencePanel panel = (RRSequencePanel) ((SeqPanel) 
+					panels [i]).getSequencePanel ();
+			if (panel.isForGenome (chosen)) {
+				panel.goTo (position);
+				break;
+			}
+		}
+	}
+	
+	/**
+	 * Centers view on a specific sequence position
+	 * 
+	 * @param position 		the position to center on
+	 * @param chosen   		the genome to find the position in
+	 */
+	public void goToPosition (long position, Genome chosen) {
+		goToPosition (position, chosen, rrpanel);
+	}
+	
+	/**
+	 * Gets all the feature keys present in any of the sequences
+	 * 
+	 * @return		A vector of strings representing all the fields in
+	 * 				all the currently viewed sequences
+	 */
+	public Vector getGenomeKeys () {
+		Vector readable = new Vector ();
+		Iterator itty = ANNOTATION_KEYS.iterator();
+		while (itty.hasNext())
+			readable.add(((String) itty.next ()).replace ('_', ' '));
+		Collections.sort (readable);
+		return readable;
+	}
+	
+	public void dispose () {
+		if(parent_component instanceof Frame)
+			((Frame)parent_component).removeWindowListener (adapt);
+		else if(parent_component instanceof JComponent)
+			((JComponent)parent_component).removeAncestorListener(ancestListener);
+		frame.dispose ();
+	}
+	
+}
diff --git a/src/org/gel/mauve/gui/SplashScreen.java b/src/org/gel/mauve/gui/SplashScreen.java
new file mode 100644
index 0000000..c97bc16
--- /dev/null
+++ b/src/org/gel/mauve/gui/SplashScreen.java
@@ -0,0 +1,91 @@
+package org.gel.mauve.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Toolkit;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JWindow;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.border.Border;
+
+import org.gel.mauve.MyConsole;
+
+/**
+ * @author darling
+ * 
+ * Show a splash screen composed of an image and a message Code adapted from
+ * SplashWindow3 at
+ * http://www.javaworld.com/javaworld/javatips/jw-javatip104.html
+ * 
+ */
+class SplashScreen extends JWindow {
+	public SplashScreen (String filename, String message, Frame f, int waitTime) {
+		super (f);
+		JPanel bg_panel = new JPanel ();
+		Border panel_border = BorderFactory.createLineBorder (Color.gray, 4);
+		bg_panel.setBackground (Color.white);
+		bg_panel.setBorder (panel_border);
+		getContentPane ().add (bg_panel);
+		JLabel il = new JLabel (new ImageIcon (SplashScreen.class
+				.getResource (filename)));
+		JLabel tl = new JLabel (message, SwingConstants.CENTER);
+		bg_panel.setLayout (new BorderLayout ());
+		bg_panel.add (il, BorderLayout.NORTH);
+		bg_panel.add (tl, BorderLayout.SOUTH);
+		pack ();
+		Dimension screenSize = Toolkit.getDefaultToolkit ().getScreenSize ();
+		Dimension labelSize = il.getPreferredSize ();
+		setLocation (screenSize.width / 2 - (labelSize.width / 2),
+				screenSize.height / 2 - (labelSize.height / 2));
+
+		addMouseListener (new MouseAdapter () {
+			public void mousePressed (MouseEvent e) {
+				setVisible (false);
+				dispose ();
+			}
+		});
+		final int pause = waitTime;
+		final Runnable closerRunner = new Runnable () {
+			public void run () {
+				setVisible (false);
+				dispose ();
+			}
+		};
+
+		Runnable waitRunner = new Runnable () {
+			public void run () {
+				try {
+					Thread.sleep (pause);
+				} catch (InterruptedException e) {
+					MyConsole.err ().print (
+							"Interrupted waiting for splash start.");
+					e.printStackTrace (MyConsole.err ());
+				}
+
+				try {
+					SwingUtilities.invokeAndWait (closerRunner);
+				} catch (InterruptedException e) {
+					MyConsole.err ().print ("Interrupted splash screen.");
+					e.printStackTrace (MyConsole.err ());
+				} catch (InvocationTargetException e) {
+					MyConsole.err ().print (
+							"Error in splash screen invocation.");
+					e.printStackTrace (MyConsole.err ());
+				}
+			}
+		};
+		setVisible (true);
+		Thread splashThread = new Thread (waitRunner, "SplashThread");
+		splashThread.start ();
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/StyleMenu.java b/src/org/gel/mauve/gui/StyleMenu.java
new file mode 100644
index 0000000..2803244
--- /dev/null
+++ b/src/org/gel/mauve/gui/StyleMenu.java
@@ -0,0 +1,285 @@
+package org.gel.mauve.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.gui.sequence.FeaturePanel;
+
+public class StyleMenu extends JMenu implements ActionListener {
+
+    JCheckBoxMenuItem jMenuViewStyleSimilarityRanges = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleLcbOutlines = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleSimilarityPlot = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleSolidBlocks = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleLcbStrikethroughLines = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleLcbConnectingLines = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleChromosomeBoundaries = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleMouseHighlighting = new JCheckBoxMenuItem();
+    JCheckBoxMenuItem jMenuViewStyleDrawAttributes = new JCheckBoxMenuItem();
+    
+    JMenu annotationMenu = new JMenu();
+    JRadioButtonMenuItem annotationMenuShowAnnotation = new JRadioButtonMenuItem();
+    JRadioButtonMenuItem annotationMenuShowAnnotationBelow = new JRadioButtonMenuItem();
+    JRadioButtonMenuItem annotationMenuHideAnnotation = new JRadioButtonMenuItem();
+    
+    BaseViewerModel model;
+    RearrangementPanel rrpanel;
+
+    public StyleMenu()
+    {
+    	annotationMenuShowAnnotation.setToolTipText("Always show annotated features if available");
+    	annotationMenuShowAnnotation.setVisible(true);
+    	annotationMenuShowAnnotation.setText("Always show");
+    	annotationMenuShowAnnotation.setMnemonic('a');
+    	annotationMenuShowAnnotation.setActionCommand("ShowAnnotation");
+    	annotationMenuShowAnnotation.addActionListener(this);
+
+    	annotationMenuShowAnnotationBelow.setSelected(true);
+    	annotationMenuShowAnnotationBelow.setToolTipText("Only show annotated features when the view spans less than 1Mbp");
+    	annotationMenuShowAnnotationBelow.setVisible(true);
+    	annotationMenuShowAnnotationBelow.setText("Only show less than 500Kbp");
+    	annotationMenuShowAnnotationBelow.setMnemonic('a');
+    	annotationMenuShowAnnotationBelow.setActionCommand("ShowAnnotationBelow");
+    	annotationMenuShowAnnotationBelow.addActionListener(this);
+
+    	annotationMenuHideAnnotation.setToolTipText("Never show annotated features");
+    	annotationMenuHideAnnotation.setVisible(true);
+    	annotationMenuHideAnnotation.setText("Never show");
+    	annotationMenuHideAnnotation.setMnemonic('h');
+    	annotationMenuHideAnnotation.setActionCommand("HideAnnotation");
+    	annotationMenuHideAnnotation.addActionListener(this);
+    	
+    	ButtonGroup group = new ButtonGroup();
+    	group.add(annotationMenuShowAnnotation);
+    	group.add(annotationMenuShowAnnotationBelow);
+    	group.add(annotationMenuHideAnnotation);
+
+    	annotationMenu.add(annotationMenuShowAnnotation);
+    	annotationMenu.add(annotationMenuShowAnnotationBelow);
+    	annotationMenu.add(annotationMenuHideAnnotation);
+    	annotationMenu.setText("Annotated features");
+    	annotationMenu.setMnemonic('A');
+    	annotationMenu.setToolTipText("Controls for when annotated features are displayed");
+    	
+    	jMenuViewStyleSimilarityRanges.setToolTipText("Draw sequence similarity values as a range with mean value darkened");
+    	jMenuViewStyleSimilarityRanges.setVisible(true);
+    	jMenuViewStyleSimilarityRanges.setText("Similarity ranges");
+    	jMenuViewStyleSimilarityRanges.setMnemonic('i');
+    	jMenuViewStyleSimilarityRanges.setActionCommand("ToggleSimilarityRanges");
+    	jMenuViewStyleSimilarityRanges.addActionListener(this);
+
+        jMenuViewStyleLcbOutlines.setToolTipText("Draw outlines around each Locally Collinear Block");
+        jMenuViewStyleLcbOutlines.setVisible(true);
+        jMenuViewStyleLcbOutlines.setText("LCB outlines");
+        jMenuViewStyleLcbOutlines.setMnemonic('o');
+        jMenuViewStyleLcbOutlines.setActionCommand("ToggleLcbBounds");
+        jMenuViewStyleLcbOutlines.addActionListener(this);
+
+        jMenuViewStyleSimilarityPlot.setToolTipText("Draw a similarity plot indicating average similarity of each region");
+        jMenuViewStyleSimilarityPlot.setVisible(true);
+        jMenuViewStyleSimilarityPlot.setText("Similarity plot");
+        jMenuViewStyleSimilarityPlot.setMnemonic('S');
+        jMenuViewStyleSimilarityPlot.setActionCommand("ToggleDrawMatches");
+        jMenuViewStyleSimilarityPlot.addActionListener(this);
+
+        jMenuViewStyleSolidBlocks.setToolTipText("Draw solid colors for each locally collinear block");
+        jMenuViewStyleSolidBlocks.setVisible(true);
+        jMenuViewStyleSolidBlocks.setText("Solid LCB coloring");
+        jMenuViewStyleSolidBlocks.setMnemonic('L');
+        jMenuViewStyleSolidBlocks.setActionCommand("ToggleFillBoxes");
+        jMenuViewStyleSolidBlocks.addActionListener(this);
+
+        jMenuViewStyleLcbStrikethroughLines.setToolTipText("Draw connecting lines that strike through LCBs");
+        jMenuViewStyleLcbStrikethroughLines.setVisible(true);
+        jMenuViewStyleLcbStrikethroughLines.setText("LCB strikethrough lines");
+        jMenuViewStyleLcbStrikethroughLines.setMnemonic('t');
+        jMenuViewStyleLcbStrikethroughLines.setActionCommand("ToggleStrikethrough");
+        jMenuViewStyleLcbStrikethroughLines.addActionListener(this);
+
+        jMenuViewStyleLcbConnectingLines.setToolTipText("Draw lines connecting homologous LCBs across genomes");
+        jMenuViewStyleLcbConnectingLines.setVisible(true);
+        jMenuViewStyleLcbConnectingLines.setText("LCB connecting lines");
+        jMenuViewStyleLcbConnectingLines.setMnemonic('l');
+        jMenuViewStyleLcbConnectingLines.setActionCommand("ToggleLCBlines");
+        jMenuViewStyleLcbConnectingLines.addActionListener(this);
+    	
+        jMenuViewStyleChromosomeBoundaries.setToolTipText("Draw red lines at chromosome or contig boundaries");
+        jMenuViewStyleChromosomeBoundaries.setVisible(true);
+        jMenuViewStyleChromosomeBoundaries.setText("Chromosome/contig boundaries");
+        jMenuViewStyleChromosomeBoundaries.setMnemonic('c');
+        jMenuViewStyleChromosomeBoundaries.setActionCommand("ToggleChromosomeBoundaries");
+        jMenuViewStyleChromosomeBoundaries.addActionListener(this);
+
+        jMenuViewStyleMouseHighlighting.setToolTipText("Define whether the mouse cursor should show orthologous regions");
+        jMenuViewStyleMouseHighlighting.setVisible(true);
+        jMenuViewStyleMouseHighlighting.setText("Show mouse highlighting");
+        jMenuViewStyleMouseHighlighting.setMnemonic('m');
+        jMenuViewStyleMouseHighlighting.setActionCommand("ToggleMouseCursor");
+        jMenuViewStyleMouseHighlighting.addActionListener(this);
+        
+        jMenuViewStyleDrawAttributes.setToolTipText("Should attributes such as genome-wide histograms be drawn");
+        jMenuViewStyleDrawAttributes.setVisible(true);
+        jMenuViewStyleDrawAttributes.setText("Draw attributes (histograms)");
+        jMenuViewStyleDrawAttributes.setMnemonic('a');
+        jMenuViewStyleDrawAttributes.setActionCommand("ToggleDrawAttributes");
+        jMenuViewStyleDrawAttributes.addActionListener(this);
+
+        add(jMenuViewStyleLcbOutlines);
+        add(jMenuViewStyleSimilarityPlot);
+        add(jMenuViewStyleSimilarityRanges);
+        add(jMenuViewStyleSolidBlocks);
+        add(jMenuViewStyleLcbStrikethroughLines);
+        add(jMenuViewStyleLcbConnectingLines);
+        add(jMenuViewStyleChromosomeBoundaries);
+        add(jMenuViewStyleMouseHighlighting);
+        add(jMenuViewStyleDrawAttributes);
+        add(annotationMenu);
+    }
+    
+    /**
+     * Sets the viewer model and display panel that the StyleMenu will configure.
+     * Also adds keystroke shortcuts to the display panel, and enables or
+     * disables the menu according to the model type being displayed.
+     * @param model
+     * @param rrpanel
+     */
+    public void setTarget(BaseViewerModel model, RearrangementPanel rrpanel)
+    {
+    	this.model = model;
+    	this.rrpanel = rrpanel;
+    	if( model instanceof LcbViewerModel || model instanceof XmfaViewerModel )
+    	{
+    		setEnabled(true);
+            rrpanel.addKeyMapping("typed r", "ToggleStrikethrough", this);
+            rrpanel.addKeyMapping("typed L", "ToggleLCBlines", this);
+            rrpanel.addKeyMapping("typed q", "ToggleLcbBounds", this);
+            rrpanel.addKeyMapping("typed w", "ToggleFillBoxes", this);
+            rrpanel.addKeyMapping("typed e", "ToggleDrawMatches", this);
+
+            jMenuViewStyleSimilarityRanges.setSelected(true);
+            jMenuViewStyleLcbConnectingLines.setSelected(true);
+            jMenuViewStyleLcbOutlines.setSelected(false);
+            jMenuViewStyleLcbStrikethroughLines.setSelected(true);
+            jMenuViewStyleSimilarityPlot.setSelected(true);
+            jMenuViewStyleSolidBlocks.setSelected(false);
+            jMenuViewStyleChromosomeBoundaries.setSelected(true);
+            jMenuViewStyleMouseHighlighting.setSelected(true);
+    	}else
+    		setEnabled(false);
+        rrpanel.addKeyMapping("typed h", "ToggleDrawAttributes", this);
+        jMenuViewStyleDrawAttributes.setSelected(model.getDrawAttributes());
+    }
+    
+    public void actionPerformed(ActionEvent e)
+    {
+        if (e.getActionCommand().equals("ToggleSimilarityRanges"))
+        {
+            if (model != null && model instanceof XmfaViewerModel)
+            {
+            	XmfaViewerModel xm = (XmfaViewerModel)model;
+                xm.setDrawSimilarityRanges(!xm.getDrawSimilarityRanges());
+                jMenuViewStyleDrawAttributes.setSelected(xm.getDrawSimilarityRanges());
+            }
+        }
+        if (e.getActionCommand().equals("ToggleLCBlines"))
+        {
+        	if(rrpanel != null)
+        	{
+        		rrpanel.lcbLinePanel.setHidden(!rrpanel.lcbLinePanel.getHidden());
+        		jMenuViewStyleLcbConnectingLines.setSelected(!rrpanel.lcbLinePanel.getHidden());
+        	}
+        }
+        else if (e.getActionCommand().equals("ToggleStrikethrough"))
+        {
+        	if(rrpanel != null)
+        	{
+	        	rrpanel.lcbLinePanel.draw_strikethrough = !rrpanel.lcbLinePanel.draw_strikethrough;
+	        	rrpanel.lcbLinePanel.repaint();
+	        	jMenuViewStyleLcbStrikethroughLines.setSelected(rrpanel.lcbLinePanel.draw_strikethrough);
+        	}
+        }
+        else if (e.getActionCommand().equals("ToggleLcbBounds"))
+        {
+            if (model instanceof LcbViewerModel)
+            {
+                LcbViewerModel lv = (LcbViewerModel) model;
+                lv.setDrawLcbBounds(!lv.getDrawLcbBounds());
+                jMenuViewStyleLcbOutlines.setSelected(lv.getDrawLcbBounds());
+            }
+        }
+        else if (e.getActionCommand().equals("ToggleFillBoxes"))
+        {
+            if (model != null)
+            {
+                if (model instanceof LcbViewerModel)
+                {
+                    LcbViewerModel lv = (LcbViewerModel) model;
+                    lv.setFillLcbBoxes(!lv.getFillLcbBoxes());
+                    jMenuViewStyleSolidBlocks.setSelected(lv.getFillLcbBoxes());
+                }
+            }
+        }
+        else if (e.getActionCommand().equals("ToggleDrawMatches"))
+        {
+            if (model != null)
+            {
+                model.setDrawMatches(!model.getDrawMatches());
+                jMenuViewStyleSimilarityPlot.setSelected(model.getDrawMatches());
+            }
+        }
+        else if (e.getActionCommand().equals("ToggleChromosomeBoundaries"))
+        {
+            if (model != null)
+            {
+                model.setDrawChromosomeBoundaries(!model.getDrawChromosomeBoundaries());
+                jMenuViewStyleChromosomeBoundaries.setSelected(model.getDrawChromosomeBoundaries());
+            }
+        }
+        else if (e.getActionCommand().equals("ToggleMouseCursor"))
+        {
+            if (model != null)
+            {
+                model.setDrawMouseCursor(!model.getDrawMouseHighlighting());
+                jMenuViewStyleMouseHighlighting.setSelected(model.getDrawMouseHighlighting());
+            }
+        }
+        else if (e.getActionCommand().equals("ToggleDrawAttributes"))
+        {
+            if (model != null)
+            {
+                model.setDrawAttributes(!model.getDrawAttributes());
+                jMenuViewStyleDrawAttributes.setSelected(model.getDrawAttributes());
+            }
+        }
+        else if (e.getActionCommand().equals("ShowAnnotation"))
+        {
+            if (model != null)
+            {
+                model.setDrawAnnotationThreshold(Integer.MAX_VALUE);
+            }
+        }
+        else if (e.getActionCommand().equals("ShowAnnotationBelow"))
+        {
+            if (model != null)
+            {
+                model.setDrawAnnotationThreshold(FeaturePanel.DEFAULT_MAX_FEATURE_DISPLAY_RANGE);
+            }
+        }
+        else if (e.getActionCommand().equals("HideAnnotation"))
+        {
+            if (model != null)
+            {
+                model.setDrawAnnotationThreshold(0);
+            }
+        }
+    	
+    }
+}
diff --git a/src/org/gel/mauve/gui/SwingWorker.java b/src/org/gel/mauve/gui/SwingWorker.java
new file mode 100644
index 0000000..bb3ce6f
--- /dev/null
+++ b/src/org/gel/mauve/gui/SwingWorker.java
@@ -0,0 +1,137 @@
+package org.gel.mauve.gui;
+
+import javax.swing.SwingUtilities;
+
+/**
+ * This is the 3rd version of SwingWorker (also known as SwingWorker 3), an
+ * abstract class that you subclass to perform GUI-related work in a dedicated
+ * thread. For instructions on and examples of using this class, see:
+ * 
+ * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
+ * 
+ * Note that the API changed slightly in the 3rd version: You must now invoke
+ * start() on the SwingWorker after creating it.
+ */
+public abstract class SwingWorker {
+	private Object value; // see getValue(), setValue()
+
+	/**
+	 * Class to maintain reference to current worker thread under separate
+	 * synchronization control.
+	 */
+	private static class ThreadVar {
+		private Thread thread;
+
+		ThreadVar (Thread t) {
+			thread = t;
+		}
+
+		synchronized Thread get () {
+			return thread;
+		}
+
+		synchronized void clear () {
+			thread = null;
+		}
+	}
+
+	private ThreadVar threadVar;
+
+	/**
+	 * Get the value produced by the worker thread, or null if it hasn't been
+	 * constructed yet.
+	 */
+	protected synchronized Object getValue () {
+		return value;
+	}
+
+	/**
+	 * Set the value produced by worker thread
+	 */
+	private synchronized void setValue (Object x) {
+		value = x;
+	}
+
+	/**
+	 * Compute the value to be returned by the <code>get</code> method.
+	 */
+	public abstract Object construct ();
+
+	/**
+	 * Called on the event dispatching thread (not on the worker thread) after
+	 * the <code>construct</code> method has returned.
+	 */
+	public void finished () {
+	}
+
+	/**
+	 * A new method that interrupts the worker thread. Call this method to force
+	 * the worker to stop what it's doing.
+	 */
+	public void interrupt () {
+		Thread t = threadVar.get ();
+		if (t != null) {
+			t.interrupt ();
+		}
+		threadVar.clear ();
+	}
+
+	/**
+	 * Return the value created by the <code>construct</code> method. Returns
+	 * null if either the constructing thread or the current thread was
+	 * interrupted before a value was produced.
+	 * 
+	 * @return the value created by the <code>construct</code> method
+	 */
+	public Object get () {
+		while (true) {
+			Thread t = threadVar.get ();
+			if (t == null) {
+				return getValue ();
+			}
+			try {
+				t.join ();
+			} catch (InterruptedException e) {
+				Thread.currentThread ().interrupt (); // propagate
+				return null;
+			}
+		}
+	}
+
+	/**
+	 * Start a thread that will call the <code>construct</code> method and
+	 * then exit.
+	 */
+	public SwingWorker () {
+		final Runnable doFinished = new Runnable () {
+			public void run () {
+				finished ();
+			}
+		};
+
+		Runnable doConstruct = new Runnable () {
+			public void run () {
+				try {
+					setValue (construct ());
+				} finally {
+					threadVar.clear ();
+				}
+
+				SwingUtilities.invokeLater (doFinished);
+			}
+		};
+
+		Thread t = new Thread (doConstruct);
+		threadVar = new ThreadVar (t);
+	}
+
+	/**
+	 * Start the worker thread.
+	 */
+	public void start () {
+		Thread t = threadVar.get ();
+		if (t != null) {
+			t.start ();
+		}
+	}
+}
diff --git a/src/org/gel/mauve/gui/dnd/DnDList.java b/src/org/gel/mauve/gui/dnd/DnDList.java
new file mode 100644
index 0000000..0526e73
--- /dev/null
+++ b/src/org/gel/mauve/gui/dnd/DnDList.java
@@ -0,0 +1,264 @@
+package org.gel.mauve.gui.dnd;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DragGestureEvent;
+import java.awt.dnd.DragGestureListener;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceEvent;
+import java.awt.dnd.DragSourceListener;
+import java.awt.dnd.DropTarget;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JList;
+
+import org.gel.mauve.MyConsole;
+
+/**
+ * An extension of {@link JList}that supports drag and drop to rearrange its
+ * contents and to move objects in and out of the list. The objects in the list
+ * will be passed either as a String by calling the object's <tt>toString()</tt>
+ * object, or if your drag and drop target accepts the
+ * {@link TransferableObject#DATA_FLAVOR}data flavor then the actual object
+ * will be passed.
+ * 
+ * <p>
+ * I'm releasing this code into the Public Domain. Enjoy.
+ * </p>
+ * <p>
+ * <em>Original author: Robert Harder, rharder at usa.net</em>
+ * </p>
+ * 
+ * @author Robert Harder
+ * @author rharder at usa.net
+ * @version 1.1
+ */
+public class DnDList extends JList implements DropTargetListener,
+		DragSourceListener, DragGestureListener {
+
+	private DropTarget dropTarget = null;
+
+	private DragSource dragSource = null;
+
+	private int sourceIndex = -1;
+
+	private int dropIndex = -1;
+
+	private Object sourceObject;
+
+	/**
+	 * Constructs a default {@link DnDList}using a {@link DefaultListModel}.
+	 * 
+	 * @since 1.1
+	 */
+	public DnDList () {
+		super (new DefaultListModel ());
+		initComponents ();
+	} // end constructor
+
+	/**
+	 * Constructs a {@link DnDList}using the passed list model that must be
+	 * extended from {@link DefaultListModel}.
+	 * 
+	 * @param model
+	 *            The model to use
+	 * @since 1.1
+	 */
+	public DnDList (DefaultListModel model) {
+		super (model);
+		initComponents ();
+	} // end constructor
+
+	/**
+	 * Constructs a {@link DnDList}by filling in a {@link DefaultListModel}
+	 * with the passed array of objects.
+	 * 
+	 * @param data
+	 *            The data from which to construct a list
+	 * @since 1.1
+	 */
+	public DnDList (Object [] data) {
+		this ();
+		((DefaultListModel) getModel ()).copyInto (data);
+	} // end constructor
+
+	/**
+	 * Constructs a {@link DnDList}by filling in a {@link DefaultListModel}
+	 * with the passed {@link Vector}of objects.
+	 * 
+	 * @param data
+	 *            The data from which to construct a list
+	 * @since 1.1
+	 */
+	public DnDList (Vector data) {
+		this ();
+		((DefaultListModel) getModel ()).copyInto (data.toArray ());
+	} // end constructor
+
+	private void initComponents () {
+		dropTarget = new DropTarget (this, this);
+		dragSource = new DragSource ();
+		dragSource.createDefaultDragGestureRecognizer (this,
+				DnDConstants.ACTION_MOVE, this);
+	} // end initComponents
+	
+	public void setDropActive (boolean active) {
+		dropTarget.setActive(active);
+	}
+
+	/* ******** D R A G G E S T U R E L I S T E N E R M E T H O D S ******** */
+
+	public void dragGestureRecognized (DragGestureEvent event) {
+		final Object selected = getSelectedValue ();
+		if (selected != null) {
+			sourceIndex = getSelectedIndex ();
+			Transferable transfer = new TransferableObject (
+					new TransferableObject.Fetcher () {
+						/**
+						 * This will be called when the transfer data is
+						 * requested at the very end. At this point we can
+						 * remove the object from its original place in the
+						 * list.
+						 */
+						public Object getObject () {
+							((DefaultListModel) getModel ())
+									.remove (sourceIndex);
+							return selected;
+						} // end getObject
+					}); // end fetcher
+
+			// as the name suggests, starts the dragging
+			dragSource.startDrag (event, DragSource.DefaultLinkDrop, transfer,
+					this);
+		} else {
+			// System.out.println( "nothing was selected");
+		}
+	} // end dragGestureRecognized
+
+	/* ******** D R A G S O U R C E L I S T E N E R M E T H O D S ******** */
+
+	public void dragDropEnd (DragSourceDropEvent evt) {
+	}
+
+	public void dragEnter (DragSourceDragEvent evt) {
+	}
+
+	public void dragExit (DragSourceEvent evt) {
+	}
+
+	public void dragOver (DragSourceDragEvent evt) {
+	}
+
+	public void dropActionChanged (DragSourceDragEvent evt) {
+	}
+
+	/* ******** D R O P T A R G E T L I S T E N E R M E T H O D S ******** */
+
+	public void dragEnter (DropTargetDragEvent evt) {
+		evt.acceptDrag (DnDConstants.ACTION_MOVE);
+	}
+
+	public void dragExit (DropTargetEvent evt) {
+	}
+
+	public void dragOver (DropTargetDragEvent evt) {
+	}
+
+	public void dropActionChanged (DropTargetDragEvent evt) {
+		evt.acceptDrag (DnDConstants.ACTION_MOVE);
+	}
+
+	public void drop (DropTargetDropEvent evt) {
+		Transferable t = evt.getTransferable ();
+
+		if (t.isDataFlavorSupported (DataFlavor.javaFileListFlavor)) {
+			evt.acceptDrop (DnDConstants.ACTION_MOVE);
+			Object obj;
+			try {
+				obj = t.getTransferData (DataFlavor.javaFileListFlavor);
+			} catch (UnsupportedFlavorException e) {
+				// Unexpected, because we just checked whether it is supported.
+				throw new RuntimeException (e);
+			} catch (IOException e) {
+				MyConsole.err ().println (
+						"Drag and drop data no longer available");
+				e.printStackTrace (MyConsole.err ());
+				evt.rejectDrop ();
+				return;
+			}
+
+			List listobj = (List) obj;
+
+			// See where in the list we dropped the element.
+			int dropIndex = locationToIndex (evt.getLocation ());
+			DefaultListModel model = (DefaultListModel) getModel ();
+
+			Iterator iterator = listobj.iterator ();
+			if (dropIndex < 0) {
+				while (iterator.hasNext ()) {
+					model.addElement (((File) iterator.next ()).getPath ());
+				}
+			}
+			// Else is it moving down the list?
+			else if (sourceIndex >= 0 && dropIndex > sourceIndex) {
+				while (iterator.hasNext ()) {
+					model.add (dropIndex - 1, ((File) iterator.next ())
+							.getPath ());
+				}
+			} else {
+				while (iterator.hasNext ()) {
+					model.add (dropIndex, ((File) iterator.next ()).getPath ());
+				}
+			}
+			evt.getDropTargetContext ().dropComplete (true);
+		} else if (t.isDataFlavorSupported (DataFlavor.stringFlavor)) {
+			evt.acceptDrop (DnDConstants.ACTION_MOVE);
+			Object obj;
+			try {
+				obj = t.getTransferData (DataFlavor.stringFlavor);
+			} catch (UnsupportedFlavorException e) {
+				// Unexpected, because we just checked whether it is supported.
+				throw new RuntimeException (e);
+			} catch (IOException e) {
+				MyConsole.err ().println (
+						"Drag and drop data no longer available");
+				e.printStackTrace (MyConsole.err ());
+				evt.rejectDrop ();
+				return;
+			}
+
+			// See where in the list we dropped the element.
+			int dropIndex = locationToIndex (evt.getLocation ());
+			DefaultListModel model = (DefaultListModel) getModel ();
+
+			if (dropIndex < 0) {
+				model.addElement (obj);
+			}
+			// Else is it moving down the list?
+			else if (sourceIndex >= 0 && dropIndex > sourceIndex) {
+				model.add (dropIndex - 1, obj);
+			} else {
+				model.add (dropIndex, obj);
+			}
+
+			evt.getDropTargetContext ().dropComplete (true);
+		} else
+		// Else we can't handle this
+		{
+			evt.rejectDrop ();
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/dnd/FileDrop.java b/src/org/gel/mauve/gui/dnd/FileDrop.java
new file mode 100644
index 0000000..8b8823f
--- /dev/null
+++ b/src/org/gel/mauve/gui/dnd/FileDrop.java
@@ -0,0 +1,572 @@
+package org.gel.mauve.gui.dnd;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DropTarget;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.HierarchyListener;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.TooManyListenersException;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.border.Border;
+
+/**
+ * This class makes it easy to drag and drop files from the operating system to
+ * a Java program. Any <tt>Component</tt> can be dropped onto, but only
+ * <tt>JComponent</tt> s will indicate the drop event with a changed border.
+ * <p/>To use this class, construct a new <tt>FileDrop</tt> by passing it the
+ * target component and a <tt>Listener</tt> to receive notification when
+ * file(s) have been dropped. Here is an example: <p/><code><pre>
+ *   
+ *    
+ *     
+ *           JPanel myPanel = new JPanel();
+ *           new FileDrop( myPanel, new FileDrop.Listener()
+ *           {   public void filesDropped( File[] files )
+ *               {   
+ *                   // handle file drop
+ *                   ...
+ *               }   // end filesDropped
+ *           }); // end FileDrop.Listener
+ *      
+ *     
+ *    
+ * </pre></code> <p/>You can specify the border that will appear when files are being
+ * dragged by calling the constructor with a <tt>Border</tt>. Only
+ * <tt>JComponent</tt> s will show any indication with a border. <p/>You can
+ * turn on some debugging features by passing a <tt>PrintStream</tt> object
+ * (such as <tt>System.out</tt>) into the full constructor. A <tt>null</tt>
+ * value will result in no extra debugging information being output. <p/>
+ * 
+ * <p>
+ * I'm releasing this code into the Public Domain. Enjoy.
+ * </p>
+ * <p>
+ * <em>Original author: Robert Harder, rharder at usa.net</em>
+ * </p>
+ * 
+ * @author Robert Harder
+ * @author rharder at usa.net
+ * @version 1.0
+ */
+public class FileDrop {
+	private transient Border normalBorder;
+
+	private transient DropTargetListener dropListener;
+
+	/** Discover if the running JVM is modern enough to have drag and drop. */
+	private static Boolean supportsDnD;
+
+	// Default border color
+	private static Color defaultBorderColor = new Color (0f, 0f, 1f, 0.25f);
+
+	/**
+	 * Constructs a {@link FileDrop}with a default light-blue border and, if
+	 * <var>c </var> is a {@link Container}, recursively sets all elements
+	 * contained within as drop targets, though only the top level container
+	 * will change borders.
+	 * 
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final Component c, final Listener listener) {
+		this (null, // Logging stream
+				c, // Drop target
+				BorderFactory
+						.createMatteBorder (2, 2, 2, 2, defaultBorderColor), // Drag
+				// border
+				true, // Recursive
+				listener);
+	} // end constructor
+
+	/**
+	 * Constructor with a default border and the option to recursively set drop
+	 * targets. If your component is a <tt>Container</tt>, then each of its
+	 * children components will also listen for drops, though only the parent
+	 * will change borders.
+	 * 
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param recursive
+	 *            Recursively set children as drop targets.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final Component c, final boolean recursive,
+			final Listener listener) {
+		this (null, // Logging stream
+				c, // Drop target
+				BorderFactory
+						.createMatteBorder (2, 2, 2, 2, defaultBorderColor), // Drag
+				// border
+				recursive, // Recursive
+				listener);
+	} // end constructor
+
+	/**
+	 * Constructor with a default border and debugging optionally turned on.
+	 * With Debugging turned on, more status messages will be displayed to
+	 * <tt>out</tt>. A common way to use this constructor is with
+	 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value
+	 * for the parameter <tt>out</tt> will result in no debugging output.
+	 * 
+	 * @param out
+	 *            PrintStream to record debugging info or null for no debugging.
+	 * @param out
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final PrintStream out, final Component c,
+			final Listener listener) {
+		this (
+				out, // Logging stream
+				c, // Drop target
+				BorderFactory
+						.createMatteBorder (2, 2, 2, 2, defaultBorderColor),
+				false, // Recursive
+				listener);
+	} // end constructor
+
+	/**
+	 * Constructor with a default border, debugging optionally turned on and the
+	 * option to recursively set drop targets. If your component is a
+	 * <tt>Container</tt>, then each of its children components will also
+	 * listen for drops, though only the parent will change borders. With
+	 * Debugging turned on, more status messages will be displayed to
+	 * <tt>out</tt>. A common way to use this constructor is with
+	 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value
+	 * for the parameter <tt>out</tt> will result in no debugging output.
+	 * 
+	 * @param out
+	 *            PrintStream to record debugging info or null for no debugging.
+	 * @param out
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param recursive
+	 *            Recursively set children as drop targets.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final PrintStream out, final Component c,
+			final boolean recursive, final Listener listener) {
+		this (out, // Logging stream
+				c, // Drop target
+				BorderFactory
+						.createMatteBorder (2, 2, 2, 2, defaultBorderColor), // Drag
+				// border
+				recursive, // Recursive
+				listener);
+	} // end constructor
+
+	/**
+	 * Constructor with a specified border
+	 * 
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param dragBorder
+	 *            Border to use on <tt>JComponent</tt> when dragging occurs.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final Component c, final Border dragBorder,
+			final Listener listener) {
+		this (null, // Logging stream
+				c, // Drop target
+				dragBorder, // Drag border
+				false, // Recursive
+				listener);
+	} // end constructor
+
+	/**
+	 * Constructor with a specified border and the option to recursively set
+	 * drop targets. If your component is a <tt>Container</tt>, then each of
+	 * its children components will also listen for drops, though only the
+	 * parent will change borders.
+	 * 
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param dragBorder
+	 *            Border to use on <tt>JComponent</tt> when dragging occurs.
+	 * @param recursive
+	 *            Recursively set children as drop targets.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final Component c, final Border dragBorder,
+			final boolean recursive, final Listener listener) {
+		this (null, c, dragBorder, recursive, listener);
+	} // end constructor
+
+	/**
+	 * Constructor with a specified border and debugging optionally turned on.
+	 * With Debugging turned on, more status messages will be displayed to
+	 * <tt>out</tt>. A common way to use this constructor is with
+	 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value
+	 * for the parameter <tt>out</tt> will result in no debugging output.
+	 * 
+	 * @param out
+	 *            PrintStream to record debugging info or null for no debugging.
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param dragBorder
+	 *            Border to use on <tt>JComponent</tt> when dragging occurs.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final PrintStream out, final Component c,
+			final Border dragBorder, final Listener listener) {
+		this (out, // Logging stream
+				c, // Drop target
+				dragBorder, // Drag border
+				false, // Recursive
+				listener);
+	} // end constructor
+
+	/**
+	 * Full constructor with a specified border and debugging optionally turned
+	 * on. With Debugging turned on, more status messages will be displayed to
+	 * <tt>out</tt>. A common way to use this constructor is with
+	 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value
+	 * for the parameter <tt>out</tt> will result in no debugging output.
+	 * 
+	 * @param out
+	 *            PrintStream to record debugging info or null for no debugging.
+	 * @param c
+	 *            Component on which files will be dropped.
+	 * @param dragBorder
+	 *            Border to use on <tt>JComponent</tt> when dragging occurs.
+	 * @param recursive
+	 *            Recursively set children as drop targets.
+	 * @param listener
+	 *            Listens for <tt>filesDropped</tt>.
+	 * @since 1.0
+	 */
+	public FileDrop (final PrintStream out, final Component c,
+			final Border dragBorder, final boolean recursive,
+			final Listener listener) {
+
+		if (supportsDnD ()) { // Make a drop listener
+			dropListener = new DropTargetListener () {
+				public void dragEnter (DropTargetDragEvent evt) {
+					log (out, "FileDrop: dragEnter event.");
+
+					// Is this an acceptable drag event?
+					if (isDragOk (out, evt)) {
+						// If it's a Swing component, set its border
+						if (c instanceof JComponent) {
+							JComponent jc = (JComponent) c;
+							normalBorder = jc.getBorder ();
+							log (out, "FileDrop: normal border saved.");
+							jc.setBorder (dragBorder);
+							log (out, "FileDrop: drag border set.");
+						} // end if: JComponent
+
+						// Acknowledge that it's okay to enter
+						// evt.acceptDrag(
+						// dnd.DnDConstants.ACTION_COPY_OR_MOVE );
+						evt.acceptDrag (DnDConstants.ACTION_COPY);
+						log (out, "FileDrop: event accepted.");
+					} // end if: drag ok
+					else { // Reject the drag event
+						evt.rejectDrag ();
+						log (out, "FileDrop: event rejected.");
+					} // end else: drag not ok
+				} // end dragEnter
+
+				public void dragOver (DropTargetDragEvent evt) { // This is
+					// called
+					// continually
+					// as long
+					// as the
+					// mouse is
+					// over the drag target.
+				} // end dragOver
+
+				public void drop (DropTargetDropEvent evt) {
+					log (out, "FileDrop: drop event.");
+					try { // Get whatever was dropped
+						Transferable tr = evt.getTransferable ();
+
+						// Is it a file list?
+						if (tr
+								.isDataFlavorSupported (DataFlavor.javaFileListFlavor)) {
+							// Say we'll take it.
+							// evt.acceptDrop (
+							// dnd.DnDConstants.ACTION_COPY_OR_MOVE );
+							evt.acceptDrop (DnDConstants.ACTION_COPY);
+							log (out, "FileDrop: file list accepted.");
+
+							// Get a useful list
+							List fileList = (List) tr
+									.getTransferData (DataFlavor.javaFileListFlavor);
+
+							// Convert list to array
+							File [] filesTemp = new File [fileList.size ()];
+							fileList.toArray (filesTemp);
+							final File [] files = filesTemp;
+
+							// Alert listener to drop.
+							if (listener != null)
+								listener.filesDropped (files);
+
+							// Mark that drop is completed.
+							evt.getDropTargetContext ().dropComplete (true);
+							log (out, "FileDrop: drop complete.");
+						} // end if: file list
+						else {
+							log (out, "FileDrop: not a file list - abort.");
+							evt.rejectDrop ();
+						} // end else: not a file list
+					} // end try
+					catch (IOException io) {
+						log (out, "FileDrop: IOException - abort:");
+						io.printStackTrace (out);
+						evt.rejectDrop ();
+					} // end catch IOException
+					catch (UnsupportedFlavorException ufe) {
+						log (out,
+								"FileDrop: UnsupportedFlavorException - abort:");
+						ufe.printStackTrace (out);
+						evt.rejectDrop ();
+					} // end catch: UnsupportedFlavorException
+					finally {
+						// If it's a Swing component, reset its border
+						if (c instanceof JComponent) {
+							JComponent jc = (JComponent) c;
+							jc.setBorder (normalBorder);
+							log (out, "FileDrop: normal border restored.");
+						} // end if: JComponent
+					} // end finally
+				} // end drop
+
+				public void dragExit (DropTargetEvent evt) {
+					log (out, "FileDrop: dragExit event.");
+					// If it's a Swing component, reset its border
+					if (c instanceof JComponent) {
+						JComponent jc = (JComponent) c;
+						jc.setBorder (normalBorder);
+						log (out, "FileDrop: normal border restored.");
+					} // end if: JComponent
+				} // end dragExit
+
+				public void dropActionChanged (DropTargetDragEvent evt) {
+					log (out, "FileDrop: dropActionChanged event.");
+					// Is this an acceptable drag event?
+					if (isDragOk (out, evt)) { // evt.acceptDrag(
+						// dnd.DnDConstants.ACTION_COPY_OR_MOVE );
+						evt.acceptDrag (DnDConstants.ACTION_COPY);
+						log (out, "FileDrop: event accepted.");
+					} // end if: drag ok
+					else {
+						evt.rejectDrag ();
+						log (out, "FileDrop: event rejected.");
+					} // end else: drag not ok
+				} // end dropActionChanged
+			}; // end DropTargetListener
+
+			// Make the component (and possibly children) drop targets
+			makeDropTarget (out, c, recursive);
+		} // end if: supports dnd
+		else {
+			log (out, "FileDrop: Drag and drop is not supported with this JVM");
+		} // end else: does not support DnD
+	} // end constructor
+
+	private static boolean supportsDnD () { // Static Boolean
+		if (supportsDnD == null) {
+			boolean support = false;
+			try {
+				Class.forName ("java.awt.dnd.DnDConstants");
+				support = true;
+			} // end try
+			catch (Exception e) {
+				support = false;
+			} // end catch
+			supportsDnD = new Boolean (support);
+		} // end if: first time through
+		return supportsDnD.booleanValue ();
+	} // end supportsDnD
+
+	private void makeDropTarget (final PrintStream out, final Component c,
+			boolean recursive) {
+		final DropTarget dt = new DropTarget ();
+		try {
+			dt.addDropTargetListener (dropListener);
+		} catch (TooManyListenersException e) {
+			// We don't expect this to happen ever.
+			throw new RuntimeException (e);
+		}
+
+		// Listen for hierarchy changes and remove the drop target when the
+		// parent gets cleared out.
+		c.addHierarchyListener (new HierarchyListener () {
+			public void hierarchyChanged (HierarchyEvent evt) {
+				// log( out, "FileDrop: Hierarchy changed." );
+				Component parent = c.getParent ();
+				if (parent == null) {
+					c.setDropTarget (null);
+					// log( out, "FileDrop: Drop target cleared from component."
+					// );
+				} // end if: null parent
+				else {
+					new DropTarget (c, dropListener);
+					// log( out, "FileDrop: Drop target added to component." );
+				} // end else: parent not null
+			} // end hierarchyChanged
+		}); // end hierarchy listener
+		if (c.getParent () != null)
+			new DropTarget (c, dropListener);
+
+		if (recursive && (c instanceof Container)) {
+			// Get the container
+			Container cont = (Container) c;
+
+			// Get it's components
+			Component [] comps = cont.getComponents ();
+
+			// Set it's components as listeners also
+			for (int i = 0; i < comps.length; i++)
+				makeDropTarget (out, comps[i], recursive);
+		} // end if: recursively set components as listener
+	} // end dropListener
+
+	/** Determine if the dragged data is a file list. */
+	private boolean isDragOk (final PrintStream out,
+			final DropTargetDragEvent evt) {
+		boolean ok = false;
+
+		// Get data flavors being dragged
+		DataFlavor [] flavors = evt.getCurrentDataFlavors ();
+
+		// See if any of the flavors are a file list
+		int i = 0;
+		while (!ok && i < flavors.length) { // Is the flavor a file list?
+			if (flavors[i].equals (DataFlavor.javaFileListFlavor))
+				ok = true;
+			i++;
+		} // end while: through flavors
+
+		// If logging is enabled, show data flavors
+		if (out != null) {
+			if (flavors.length == 0)
+				log (out, "FileDrop: no data flavors.");
+			for (i = 0; i < flavors.length; i++)
+				log (out, flavors[i].toString ());
+		} // end if: logging enabled
+
+		return ok;
+	} // end isDragOk
+
+	/** Outputs <tt>message</tt> to <tt>out</tt> if it's not null. */
+	private static void log (PrintStream out, String message) { // Log message
+		// if requested
+		if (out != null)
+			out.println (message);
+	} // end log
+
+	/**
+	 * Removes the drag-and-drop hooks from the component and optionally from
+	 * the all children. You should call this if you add and remove components
+	 * after you've set up the drag-and-drop. This will recursively unregister
+	 * all components contained within <var>c </var> if <var>c </var> is a
+	 * {@link Container}.
+	 * 
+	 * @param c
+	 *            The component to unregister as a drop target
+	 * @since 1.0
+	 */
+	public static boolean remove (Component c) {
+		return remove (null, c, true);
+	} // end remove
+
+	/**
+	 * Removes the drag-and-drop hooks from the component and optionally from
+	 * the all children. You should call this if you add and remove components
+	 * after you've set up the drag-and-drop.
+	 * 
+	 * @param out
+	 *            Optional {@link PrintStream}for logging drag and drop
+	 *            messages
+	 * @param c
+	 *            The component to unregister
+	 * @param recursive
+	 *            Recursively unregister components within a container
+	 * @since 1.0
+	 */
+	public static boolean remove (PrintStream out, Component c,
+			boolean recursive) { // Make sure we support dnd.
+		if (supportsDnD ()) {
+			log (out, "FileDrop: Removing drag-and-drop hooks.");
+			c.setDropTarget (null);
+			if (recursive && (c instanceof Container)) {
+				Component [] comps = ((Container) c).getComponents ();
+				for (int i = 0; i < comps.length; i++)
+					remove (out, comps[i], recursive);
+				return true;
+			} // end if: recursive
+			else
+				return false;
+		} // end if: supports DnD
+		else
+			return false;
+	} // end remove
+
+	/* ******** I N N E R I N T E R F A C E L I S T E N E R ******** */
+
+	/**
+	 * Implement this inner interface to listen for when files are dropped. For
+	 * example your class declaration may begin like this: <code><pre>
+	 *   
+	 *    
+	 *     
+	 *           public class MyClass implements FileDrop.Listener
+	 *           ...
+	 *           public void filesDropped( File[] files )
+	 *           {
+	 *               ...
+	 *           }   // end filesDropped
+	 *           ...
+	 *      
+	 *     
+	 *    
+	 * </pre></code>
+	 * 
+	 * @since 1.0
+	 */
+	public interface Listener {
+		/**
+		 * This method is called when files have been successfully dropped.
+		 * 
+		 * @param files
+		 *            An array of <tt>File</tt> s that were dropped.
+		 * @since 1.0
+		 */
+		public abstract void filesDropped (File [] files);
+	} // end inner-interface Listener
+
+} // end class FileDrop
diff --git a/src/org/gel/mauve/gui/dnd/TransferableObject.java b/src/org/gel/mauve/gui/dnd/TransferableObject.java
new file mode 100644
index 0000000..ad93d7a
--- /dev/null
+++ b/src/org/gel/mauve/gui/dnd/TransferableObject.java
@@ -0,0 +1,262 @@
+package org.gel.mauve.gui.dnd;
+
+/**
+ * At last an easy way to encapsulate your custom objects for dragging and
+ * dropping in your Java programs! When you need to create a
+ * {@link java.awt.datatransfer.Transferable}object, use this class to wrap
+ * your object. For example:
+ * 
+ * <pre><code>
+ * 
+ *       ...
+ *       MyCoolClass myObj = new MyCoolClass();
+ *       Transferable xfer = new TransferableObject( myObj );
+ *       ...
+ *  
+ * </code></pre>
+ * 
+ * Or if you need to know when the data was actually dropped, like when you're
+ * moving data out of a list, say, you can use the
+ * {@link TransferableObject.Fetcher}inner class to return your object Just in
+ * Time. For example:
+ * 
+ * <pre><code>
+ * 
+ *       ...
+ *       final MyCoolClass myObj = new MyCoolClass();
+ * 
+ *       TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()
+ *       {   public Object getObject(){ return myObj; }
+ *       }; // end fetcher
+ * 
+ *       Transferable xfer = new TransferableObject( fetcher );
+ *       ...
+ *  
+ * </code></pre>
+ * 
+ * The {@link java.awt.datatransfer.DataFlavor}associated with
+ * {@link TransferableObject}has the representation class
+ * <tt>net.iharder.dnd.TransferableObject.class</tt> and MIME type
+ * <tt>application/x-net.iharder.dnd.TransferableObject</tt>. This data
+ * flavor is accessible via the static {@link #DATA_FLAVOR}property.
+ * 
+ * 
+ * <p>
+ * <em>This code is licensed for public use under the Common Public License version 0.5.</em>
+ * <br/>The Common Public License, developed by IBM and modeled after their
+ * industry-friendly IBM Public License, differs from other common open source
+ * licenses in several important ways:
+ * <ul>
+ * <li>You may include this software with other software that uses a different
+ * (even non-open source) license.</li>
+ * <li>You may use this software to make for-profit software.</li>
+ * <li>Your patent rights, should you generate patents, are protected.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * <em>Copyright (c) 2001 Robert Harder</em>
+ * </p>
+ * 
+ * @author Robert.Harder
+ * @copyright 2001
+ * @version 1.1
+ */
+public class TransferableObject implements java.awt.datatransfer.Transferable
+{
+    /**
+     * The MIME type for {@link #DATA_FLAVOR}is
+     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
+     * 
+     * @since 1.1
+     */
+    public final static String MIME_TYPE = "application/x-mauve.TransferableObject";
+    //    public final static String MIME_TYPE =
+    // "application/x-java-serialized-object";
+
+    /**
+     * The default {@link java.awt.datatransfer.DataFlavor}for
+     * {@link TransferableObject}has the representation class
+     * <tt>net.iharder.dnd.TransferableObject.class</tt> and the MIME type
+     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
+     * 
+     * @since 1.1
+     */
+    public final static java.awt.datatransfer.DataFlavor DATA_FLAVOR = new java.awt.datatransfer.DataFlavor(TransferableObject.class, MIME_TYPE);
+
+    private Fetcher fetcher;
+    private Object data;
+
+    private java.awt.datatransfer.DataFlavor customFlavor;
+
+    /**
+     * Creates a new {@link TransferableObject}that wraps <var>data </var>.
+     * Along with the {@link #DATA_FLAVOR}associated with this class, this
+     * creates a custom data flavor with a representation class determined from
+     * <code>data.getClass()</code> and the MIME type
+     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
+     * 
+     * @param data
+     *            The data to transfer
+     * @since 1.1
+     */
+    public TransferableObject(Object data)
+    {
+        this.data = data;
+        this.customFlavor = new java.awt.datatransfer.DataFlavor(data.getClass(), MIME_TYPE);
+    } // end constructor
+
+    /**
+     * Creates a new {@link TransferableObject}that will return the object that
+     * is returned by <var>fetcher </var>. No custom data flavor is set other
+     * than the default {@link #DATA_FLAVOR}.
+     * 
+     * @see Fetcher
+     * @param fetcher
+     *            The {@link Fetcher}that will return the data object
+     * @since 1.1
+     */
+    public TransferableObject(Fetcher fetcher)
+    {
+        this.fetcher = fetcher;
+    } // end constructor
+
+    /**
+     * Creates a new {@link TransferableObject}that will return the object that
+     * is returned by <var>fetcher </var>. Along with the {@link #DATA_FLAVOR}
+     * associated with this class, this creates a custom data flavor with a
+     * representation class <var>dataClass </var> and the MIME type
+     * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
+     * 
+     * @see Fetcher
+     * @param dataClass
+     *            The {@link java.lang.Class}to use in the custom data flavor
+     * @param fetcher
+     *            The {@link Fetcher}that will return the data object
+     * @since 1.1
+     */
+    public TransferableObject(Class dataClass, Fetcher fetcher)
+    {
+        this.fetcher = fetcher;
+        this.customFlavor = new java.awt.datatransfer.DataFlavor(dataClass, MIME_TYPE);
+    } // end constructor
+
+    /**
+     * Returns the custom {@link java.awt.datatransfer.DataFlavor}associated
+     * with the encapsulated object or <tt>null</tt> if the {@link Fetcher}
+     * constructor was used without passing a {@link java.lang.Class}.
+     * 
+     * @return The custom data flavor for the encapsulated object
+     * @since 1.1
+     */
+    public java.awt.datatransfer.DataFlavor getCustomDataFlavor()
+    {
+        return customFlavor;
+    } // end getCustomDataFlavor
+
+    /* ******** T R A N S F E R A B L E M E T H O D S ******** */
+
+    /**
+     * Returns a two- or three-element array containing first the custom data
+     * flavor, if one was created in the constructors, second the default
+     * {@link #DATA_FLAVOR}associated with {@link TransferableObject}, and
+     * third the {@link java.awt.datatransfer.DataFlavor.stringFlavor}.
+     * 
+     * @return An array of supported data flavors
+     * @since 1.1
+     */
+    public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors()
+    {
+        if (customFlavor != null)
+            return new java.awt.datatransfer.DataFlavor[] { customFlavor, DATA_FLAVOR, java.awt.datatransfer.DataFlavor.stringFlavor }; // end
+                                                                                                                                        // flavors
+                                                                                                                                        // array
+        else
+            return new java.awt.datatransfer.DataFlavor[] { DATA_FLAVOR, java.awt.datatransfer.DataFlavor.stringFlavor }; // end
+                                                                                                                          // flavors
+                                                                                                                          // array
+    } // end getTransferDataFlavors
+
+    /**
+     * Returns the data encapsulated in this {@link TransferableObject}. If the
+     * {@link Fetcher}constructor was used, then this is when the
+     * {@link Fetcher#getObject getObject()}method will be called. If the
+     * requested data flavor is not supported, then the
+     * {@link Fetcher#getObject getObject()}method will not be called.
+     * 
+     * @param flavor
+     *            The data flavor for the data to return
+     * @return The dropped data
+     * @since 1.1
+     */
+    public Object getTransferData(java.awt.datatransfer.DataFlavor flavor) throws java.awt.datatransfer.UnsupportedFlavorException, java.io.IOException
+    {
+        // Native object
+        if (flavor.equals(DATA_FLAVOR))
+            return fetcher == null ? data : fetcher.getObject();
+
+        // String
+        if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor))
+            return fetcher == null ? data.toString() : fetcher.getObject().toString();
+
+        if (flavor.equals(customFlavor))
+            return fetcher == null ? data : fetcher.getObject();
+
+        // We can't do anything else
+        throw new java.awt.datatransfer.UnsupportedFlavorException(flavor);
+    } // end getTransferData
+
+    /**
+     * Returns <tt>true</tt> if <var>flavor </var> is one of the supported
+     * flavors. Flavors are supported using the <code>equals(...)</code>
+     * method.
+     * 
+     * @param flavor
+     *            The data flavor to check
+     * @return Whether or not the flavor is supported
+     * @since 1.1
+     */
+    public boolean isDataFlavorSupported(java.awt.datatransfer.DataFlavor flavor)
+    {
+        // Native object
+        if (flavor.equals(DATA_FLAVOR))
+            return true;
+
+        // String
+        if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor))
+            return true;
+
+        if (flavor.equals(customFlavor))
+            return true;
+
+        // We can't do anything else
+        return false;
+    } // end isDataFlavorSupported
+
+    /* ******** I N N E R I N T E R F A C E F E T C H E R ******** */
+
+    /**
+     * Instead of passing your data directly to the {@link TransferableObject}
+     * constructor, you may want to know exactly when your data was received in
+     * case you need to remove it from its source (or do anyting else to it).
+     * When the {@link #getTransferData getTransferData(...)}method is called
+     * on the {@link TransferableObject}, the {@link Fetcher}'s
+     * {@link #getObject getObject()}method will be called.
+     * 
+     * @author Robert Harder
+     * @copyright 2001
+     * @version 1.1
+     * @since 1.1
+     */
+    public static interface Fetcher
+    {
+        /**
+         * Return the object being encapsulated in the
+         * {@link TransferableObject}.
+         * 
+         * @return The dropped object
+         * @since 1.1
+         */
+        public abstract Object getObject();
+    } // end inner interface Fetcher
+
+} // end class TransferableObject
diff --git a/src/org/gel/mauve/gui/navigation/AnnotationContainsFilter.java b/src/org/gel/mauve/gui/navigation/AnnotationContainsFilter.java
new file mode 100644
index 0000000..a7ad89f
--- /dev/null
+++ b/src/org/gel/mauve/gui/navigation/AnnotationContainsFilter.java
@@ -0,0 +1,120 @@
+package org.gel.mauve.gui.navigation;
+
+import java.util.Locale;
+
+import org.biojava.bio.Annotation;
+import org.biojava.bio.AnnotationType;
+import org.biojava.bio.CardinalityConstraint;
+import org.biojava.bio.CollectionConstraint;
+import org.biojava.bio.PropertyConstraint;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.OptimizableFilter;
+
+/**
+ * Allows features to be filtered based on  whether they contain an annotation of a 
+ * particular type with an either exact or partial value match.  Filtering is case
+ * insensitive
+ * 
+ * @author Anna I Rissman
+ *
+ */
+public class AnnotationContainsFilter extends FeatureFilter.ByAnnotationType {
+
+	/**
+	 * The type of annotation searched for
+	 */
+	protected String key;
+
+	/**
+	 * The value the annotation should have
+	 */
+	protected String value;
+
+	/**
+	 * If exact is true, the annotation must exactly match the value given,
+	 * otherwise features will be filtered on substring matches
+	 */
+	protected boolean exact;
+
+	/**
+	 * Creates a feature filter
+	 * @param key		The type of annotation searched for
+	 * @param value		The value the annotation should have
+	 * @param exact		Whether the match is exact or not
+	 */
+	public AnnotationContainsFilter (String key, String value, boolean exact) {
+		this.key = key;
+		this.value = value;
+		this.exact = exact;
+
+		AnnotationType.Impl type = new AnnotationType.Impl ();
+		type.setConstraint (key, new CollectionConstraint.Contains (
+				new PropertyConstraint () {
+
+					public boolean accept (Object value) {
+						if (!(value instanceof String))
+							value = value.toString ();
+						if (AnnotationContainsFilter.this.exact
+								&& ((String) value).length () != AnnotationContainsFilter.this.value
+										.length ())
+							return false;
+						boolean ret = ((String) value).toLowerCase ().indexOf (
+								AnnotationContainsFilter.this.value) > -1;
+						if (AnnotationContainsFilter.this.exact) {
+							ret = ret
+									&& ((String) value).length () == AnnotationContainsFilter.this.value
+											.length ();
+						}
+						return ret;
+					}
+
+					public boolean subConstraintOf (
+							PropertyConstraint subConstraint) {
+						return false;
+					}
+
+				}, CardinalityConstraint.ONE));
+		super.setType (type);
+	}
+
+	/**
+	 * Returns a string representing the key in the right case (upper, lower, or
+	 * capitalized
+	 * 
+	 * @param key
+	 *            The key the annotation should contain
+	 * @param note
+	 *            The annotation in question
+	 * @return A string representing the key as it appears
+	 */
+	public static String getKeyIgnoreCase (String key, Annotation note) {
+		key = key.toLowerCase ();
+		if (note.containsProperty (key))
+			return key;
+		else if (note.containsProperty (key.toUpperCase ()))
+			return key.toUpperCase ();
+		key = key.toUpperCase ().charAt (0) + key.substring (1, key.length ());
+		if (note.containsProperty (key))
+			return key;
+		else
+			return null;
+	}
+
+	/**
+	 * gets the value of an annotation with a specified key; is case insensitive
+	 * 
+	 * @param key	The key of the desired annotation
+	 * @param note  The annotation to search
+	 * @return
+	 */
+	public static Object getValueIgnoreCase (String key, Annotation note) {
+		key = getKeyIgnoreCase (key, note);
+		if (key != null)
+			return note.getProperty (key);
+		else
+			return null;
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/navigation/NavigationPanel.java b/src/org/gel/mauve/gui/navigation/NavigationPanel.java
new file mode 100644
index 0000000..177a315
--- /dev/null
+++ b/src/org/gel/mauve/gui/navigation/NavigationPanel.java
@@ -0,0 +1,203 @@
+package org.gel.mauve.gui.navigation;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Vector;
+
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.gui.SequenceNavigator;
+
+/**
+ * A panel that allows a user to set up a constraint for searching features 
+ * 
+ * @author rissman
+ * 
+ */
+public class NavigationPanel extends JPanel implements ActionListener,
+		MauveConstants {
+
+	/**
+	 * Contains choices for annotation keys to search for.  Multiple values can be entered
+	 * and should be comma-separated
+	 */
+	protected JComboBox nav_chooser;
+
+	/**
+	 * the value to search for.
+	 */
+	protected JTextField input;
+
+	/**
+	 * reference to the SequenceNavigator this panel is associated with
+	 */
+	protected SequenceNavigator navigator;
+
+	/**
+	 * If selected, this constraint should be an exact match
+	 */
+	protected JRadioButton equals;
+
+	/**
+	 * If selected, returned features should have annotations with values that
+	 * contain the specified input
+	 */
+	protected JRadioButton contains;
+
+	/**
+	 * If this button is pressed, this panel is removed from the SequenceNavigator
+	 */
+	protected JButton remove;
+
+	/**
+	 * Strings representing button names and actions
+	 */
+	protected static final String EQUALS = "equals";
+
+	protected static final String CONTAINS = "contains";
+
+	/**
+	 * vector of fields available to navigate by
+	 */
+	public Vector nav_methods;
+
+	/**
+	 * constructs a new NavigationPanel and adds it to the SequenceNavigator so
+	 * a user may add an additional constraint
+	 * 
+	 * @param nav
+	 *            The SequenceNavigator that will hold this panel
+	 */
+	public NavigationPanel (SequenceNavigator nav) {
+		super ();
+		navigator = nav;
+		setNavigationChoices ();
+		new BoxLayout (this, BoxLayout.X_AXIS);
+		initGUI ();
+	}
+
+	/**
+	 * initializes gui components
+	 * 
+	 */
+	protected void initGUI () {
+		nav_chooser = new JComboBox (nav_methods);
+		nav_chooser.setEditable (true);
+		Font usual = nav_chooser.getFont ();
+		Font large = new Font (usual.getName (), usual.getStyle (), 12);
+		Font small = new Font (usual.getName (), usual.getStyle (), 10);
+		nav_chooser.setFont (large);
+		nav_chooser.addActionListener (this);
+		input = new JTextField (10);
+		input.setFont (large);
+		input.addKeyListener (navigator);
+		JPanel radios = new JPanel (new BorderLayout ());
+		equals = new JRadioButton (EQUALS);
+		equals.setFont (small);
+		equals.setActionCommand (EQUALS);
+		ButtonGroup exact_match = new ButtonGroup ();
+		exact_match.add (equals);
+		radios.add (equals, BorderLayout.NORTH);
+		contains = new JRadioButton (CONTAINS);
+		contains.setFont (small);
+		contains.setActionCommand (CONTAINS);
+		exact_match.add (contains);
+		contains.setSelected (true);
+		radios.add (contains, BorderLayout.SOUTH);
+		remove = new JButton ("Remove");
+		remove.setFont (large);
+		remove.addActionListener (this);
+		int height = input.getPreferredSize ().height + 2;
+		nav_chooser.setPreferredSize (new Dimension (nav_chooser
+				.getPreferredSize ().width, height));
+		remove.setPreferredSize (new Dimension (
+				remove.getPreferredSize ().width, height));
+		input.setPreferredSize (new Dimension (input.getPreferredSize ().width,
+				height));
+		equals.setPreferredSize (new Dimension (
+				equals.getPreferredSize ().width, height - 4));
+		contains.setPreferredSize (new Dimension (
+				contains.getPreferredSize ().width, height - 4));
+		add (nav_chooser);
+		add (radios);
+		add (input);
+		add (remove);
+		navigator.addNavigationPanel (this);
+	}
+
+	/**
+	 * checks if the user has entered valid information to search by
+	 * 
+	 * @return True if the data entered is valid
+	 */
+	public boolean dataValid () {
+		boolean valid = true;
+		String field = (String) nav_chooser.getSelectedItem ();
+		if (field == null || field.length () == 0) {
+			nav_chooser.grabFocus ();
+			valid = false;
+		} else {
+			String choice = input.getText ();
+			if (choice == null || choice.length () == 0) {
+				input.grabFocus ();
+				valid = false;
+			}
+		}
+		if (!valid)
+			navigator.makeConstraintVisible (this);
+		return valid;
+	}
+
+	/**
+	 * returns the selections the user made using this navigation panel
+	 * 
+	 * @return String array length 3. The 0 position is field name, the 1 the
+	 *         value the field should have, and 2 is a String value representing
+	 *         a boolean--true if the user wants an exact match, false otherwise
+	 */
+	public String [] getSearchCriteria () {
+		String [] data = new String [3];
+		data[FIELD] = (String) nav_chooser.getSelectedItem ();
+		data[VALUE] = input.getText ();
+		data[EXACT] = Boolean.toString (equals.isSelected ());
+		return data;
+	}
+
+	/**
+	 * sets choices available to search by- includes all field names in all
+	 * genomes and pre-made choice groupings
+	 * 
+	 */
+	protected void setNavigationChoices () {
+		nav_methods = new Vector ();
+		nav_methods.add (NAME);
+		nav_methods.add (ID);
+		nav_methods.add (PRODUCT);
+		nav_methods.add (GO);
+		nav_methods.addAll (navigator.getGenomeKeys ());
+	}
+
+	/**
+	 * responds when a user elects to remove this navigation panel
+	 * 
+	 * @param e
+	 *            The action event representing the button being clicked
+	 */
+	public void actionPerformed (ActionEvent e) {
+		if (e.getSource () == remove)
+			navigator.removeNavigationPanel (this);
+		else
+			input.grabFocus ();
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/navigation/SearchResultPanel.java b/src/org/gel/mauve/gui/navigation/SearchResultPanel.java
new file mode 100644
index 0000000..17561fd
--- /dev/null
+++ b/src/org/gel/mauve/gui/navigation/SearchResultPanel.java
@@ -0,0 +1,524 @@
+package org.gel.mauve.gui.navigation;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.TreeModelListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+import org.biojava.bio.Annotation;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.symbol.Location;
+import org.gel.mauve.Genome;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.SeqFeatureData;
+import org.gel.mauve.gui.SequenceNavigator;
+
+/**
+ * A Gui component that shows the results of a given query separated by genome.
+ * Each result represents a feature that matched the search constraints.  If a result
+ * is selected, the Genome sequence is scrolled to that feature
+ * 
+ * @author rissman
+ * 
+ */
+public class SearchResultPanel extends JPanel implements TreeModel,
+		TreeCellRenderer, TreeSelectionListener, MauveConstants {
+
+	/**
+	 * Tree that contains search results (features)
+	 */
+	protected JTree tree;
+
+	/**
+	 * model that represents the data to be displayed
+	 */
+	protected DefaultTreeModel model;
+
+	/**
+	 * renders each value in the tree
+	 */
+	protected DefaultTreeCellRenderer renderer;
+
+	/**
+	 * reference to SequenceNavigator this panel is associated with
+	 */
+	protected SequenceNavigator navigator;
+
+	/**
+	 * reference to root node of tree
+	 */
+	protected DefaultMutableTreeNode root;
+
+	/**
+	 * allows results to be scrolled through
+	 */
+	protected JScrollPane scroller;
+
+	/**
+	 * represents display state - if there are no results, value is 0, 1 if
+	 * there is 1 result, any number higher than 1 means multiple results
+	 */
+	protected int result_state;
+
+	/**
+	 * true when a search is in progress, false otherwise
+	 */
+	protected boolean searching;
+
+	/**
+	 * String representing no results
+	 */
+	public static final String NO_RESULTS = "No results to display. . .";
+
+	/**
+	 * represents genome with no found features
+	 */
+	public static final String MATCHLESS = "No features found. . .";
+	
+	/**
+	 * represents tree while searching
+	 */
+	protected static final String SEARCHING = "Searching. . .";
+
+	/**
+	 * represents "dummy" root node of tree
+	 */
+	protected static final String ROOT = "Root";
+
+	/**
+	 * icon shown if a genome contained results
+	 */
+	public static final Icon PLUS_ICON = SignedIcon.getSignedIcon (true);
+
+	/**
+	 * icon shown if a genome did not contain results
+	 */
+	public static final Icon MINUS_ICON = SignedIcon.getSignedIcon (false);
+
+	/**
+	 * contains all the data the tree should display
+	 */
+	protected Hashtable genome_data;
+
+	/**
+	 * contains genomes mapped to their index
+	 */
+	protected Object [] genome_indexes;
+
+	/**
+	 * Constructs new SearchResultPanel
+	 * 
+	 * @param genomes
+	 *            The genomes that the mauve frame displays
+	 * @param nav
+	 *            The SequenceNavigator it belongs to
+	 */
+	public SearchResultPanel (Vector genomes, SequenceNavigator nav) {
+		super (new FlowLayout (FlowLayout.LEFT));
+		navigator = nav;
+		genome_data = new Hashtable ();
+		genome_indexes = new Object [genomes.size ()];
+		for (int i = 0; i < genomes.size (); i++) {
+			genome_data.put (genomes.get (i), new LinkedList ());
+			genome_indexes[i] = genomes.get (i);
+		}
+		initGUI ();
+	}
+
+	/**
+	 * initializes gui components
+	 * 
+	 */
+	protected void initGUI () {
+		UIManager.put ("Tree.hash", new ColorUIResource (Color.BLACK));
+		tree = new JTree ();
+		tree.setRootVisible (false);
+		tree.setBackground (navigator.getBackground ());
+		tree.getSelectionModel ().setSelectionMode (
+				TreeSelectionModel.SINGLE_TREE_SELECTION);
+		model = (DefaultTreeModel) tree.getModel ();
+		renderer = (DefaultTreeCellRenderer) tree.getCellRenderer ();
+		renderer.setBackgroundNonSelectionColor (navigator.getBackground ());
+		renderer.setClosedIcon (PLUS_ICON);
+		renderer.setOpenIcon (MINUS_ICON);
+		renderer.setLeafIcon (null);
+		tree.setCellRenderer (this);
+		tree.setModel (this);
+		tree.setEditable (false);
+		tree.addTreeSelectionListener (this);
+		tree.setToggleClickCount (1);
+		root = new DefaultMutableTreeNode (ROOT);
+		add (tree);
+		scroller = new JScrollPane (this);
+		scroller.getVerticalScrollBar ().setUnitIncrement (
+				renderer.getPreferredSize ().height);
+	}
+
+	/**
+	 * returns a reference to the ScrollPane that contains this result_panel
+	 * 
+	 * @return
+	 */
+	public JScrollPane getScrollPane () {
+		return scroller;
+	}
+
+	/**
+	 * displays the data passed into the method
+	 * 
+	 * @param data
+	 *            An array of linked lists. Each list should start with a genome
+	 *            object, followed by all the Features to display in that genome
+	 */
+	public void displayFeatures (Object [] data) {
+		resetData (data);
+		int prev_result = result_state;
+		Object [] first = new Object [3];
+		for (int i = 0; i < genome_indexes.length; i++) {
+			LinkedList list = (LinkedList) genome_data.get (genome_indexes[i]);
+			if (result_state == 0 && list.size () > 0) {
+				first[2] = (Feature) list.get (0);
+				first[1] = genome_indexes[i];
+				first[0] = ROOT;
+			}
+			result_state += list.size ();
+			if (result_state > 1)
+				break;
+		}
+		searching = false;
+		model.nodeStructureChanged (root);
+		Object [] path = new Object [(result_state == 0) ? 1 : 2];
+		path[0] = ROOT;
+		if (result_state > 0) {
+			for (int i = 0; i < getChildCount (ROOT); i++) {
+				Object kid = getChild (ROOT, i);
+				path[1] = kid;
+				tree.expandPath (new TreePath (path));
+			}
+			if (prev_result == 0)
+				tree.setSelectionPath (new TreePath (first));
+		} else {
+			tree.expandPath (new TreePath (path));
+			try {
+				Thread.currentThread ().wait (100);
+			} catch (Exception e) {
+				// ok probably. . .
+			}
+		}
+		navigator.reloadGUI ();
+	}
+
+	/**
+	 * sets display to reflect a search is being performed
+	 *
+	 */
+	public void waitForResults () {
+		searching = true;
+				SwingUtilities.invokeLater(new Runnable(){
+			public void run(){
+				model.nodeStructureChanged (root);
+			}
+		});
+	}
+
+	/**
+	 * Resets the display to include the new data
+	 * 
+	 * @param data
+	 *            The data that should be added. Each array index is a linked
+	 *            list containing first the genome it is for, then all the
+	 *            features to display for that sequence
+	 */
+	protected void resetData (Object [] data) {
+		Enumeration keys = null;
+		if (result_state != 0 && navigator.shouldClear ()) {
+			keys = genome_data.keys ();
+			while (keys.hasMoreElements ()) {
+				LinkedList remove = (LinkedList) genome_data.get (keys
+						.nextElement ());
+				remove.clear ();
+			}
+			result_state = 0;
+		}
+		for (int i = 0; i < data.length; i++) {
+			LinkedList new_data = (LinkedList) data[i];
+			Object key = new_data.remove (0);
+			LinkedList old = (LinkedList) genome_data.get (key);
+			old.addAll (new_data);
+			SeqFeatureData.removeLocationDuplicates (old);
+		}
+	}
+
+	/**
+	 * returns a string including the feature's name and location
+	 * 
+	 * @param feat
+	 *            The feature whose information should be displayed
+	 * @return The display string for this feature
+	 */
+	public static String getDisplayText (Feature feat) {
+		String data = "";
+		Annotation note = feat.getAnnotation ();
+		StringTokenizer fields = SeqFeatureData.separateFields (LOC_NAME);
+		while (fields.hasMoreTokens () && data.length () == 0) {
+			String field = fields.nextToken ();
+			if (note.containsProperty (field))
+				data += (String) note.getProperty (field);
+		}
+		Location loci = feat.getLocation ();
+		data += " (" + loci.getMin () + "-" + loci.getMax () + ")";
+		data.trim ();
+		return data;
+	}
+
+	/**
+	 * When the selection in the tree changes, recenters mauve frame gui on
+	 * selected feature.
+	 */
+	public void valueChanged (TreeSelectionEvent event) {
+		if (event.getNewLeadSelectionPath () != null) {
+			Object [] path = event.getNewLeadSelectionPath ().getPath ();
+			if (path != null && path.length == 3 && path[2] != MATCHLESS)
+				navigator.displayFeature ((Feature) path[2], (Genome) path[1]);
+		}
+	}
+
+	/**
+	 * facilitates painting of tree- converts feature to its name
+	 */
+	public Component getTreeCellRendererComponent (JTree tree, Object val,
+			boolean selected, boolean expanded, boolean leaf, int row,
+			boolean focus) {
+		JLabel label = (JLabel) renderer.getTreeCellRendererComponent (tree,
+				val, selected, expanded, leaf, row, focus);
+		if (val instanceof Feature)
+			label.setText (getDisplayText ((Feature) val));
+		return label;
+	}
+
+	/**
+	 * passes on listeners to real tree model
+	 */
+	public void addTreeModelListener (TreeModelListener listen) {
+		model.addTreeModelListener (listen);
+	}
+
+	/**
+	 * Finds the child of the given object
+	 * 
+	 * @param parent
+	 *            The object whose child should be found
+	 * @param index
+	 *            The index of the desired child
+	 * @return An object representing the child node
+	 */
+	public Object getChild (Object parent, int index) {
+		if (parent == ROOT) {
+			if (searching)
+				return SEARCHING;
+			else if (result_state == 0)
+				return NO_RESULTS;
+			else
+				return genome_indexes[index];
+		}
+		LinkedList children = (LinkedList) genome_data.get (parent);
+		if (children != null) {
+			try {
+				return children.get (index);
+			} catch (IndexOutOfBoundsException e) {
+				return MATCHLESS;
+			}
+		} else
+			return null;
+
+	}
+
+	/**
+	 * Returns the number of children the object has
+	 * 
+	 * @param parent
+	 *            The object representing the parent
+	 * @return The number of children associated with the parent
+	 */
+	public int getChildCount (Object parent) {
+		if (parent == ROOT) {
+			if (searching || result_state == 0)
+				return 1;
+			else
+				return genome_data.size ();
+		}
+		LinkedList children = (LinkedList) genome_data.get (parent);
+		if (children != null) {
+			int size = ((LinkedList) genome_data.get (parent)).size ();
+			if (size == 0)
+				return 1;
+			else
+				return size;
+		} else
+			return 0;
+	}
+
+	/**
+	 * Returns the index of the child
+	 * 
+	 * @param parent
+	 *            The parent of the child in question
+	 * @param child
+	 *            The child whose index is desired
+	 * @return The index of the child
+	 */
+	public int getIndexOfChild (Object parent, Object child) {
+		if (parent == ROOT) {
+			if (child == SEARCHING) {
+				if (searching)
+					return 0;
+				else
+					return -1;
+			}
+			if (child == NO_RESULTS) {
+				if (result_state == 0)
+					return 0;
+				else
+					return -1;
+			} else {
+				for (int i = 0; i < genome_indexes.length; i++) {
+					if (genome_indexes[i].equals (child))
+						return i;
+				}
+			}
+		} else if (child == MATCHLESS)
+			return 0;
+		else if (genome_data.contains (parent)) {
+			LinkedList kids = (LinkedList) genome_data.get (parent);
+			return kids.indexOf (child);
+		}
+		return -1;
+	}
+
+	/**
+	 * returns undisplayed root of the tree
+	 * 
+	 * @return An object representing the tree root
+	 */
+	public Object getRoot () {
+		return ROOT;
+	}
+
+	/**
+	 * Returns true if the object is a leaf
+	 * 
+	 * @param node
+	 *            The object in question
+	 * @return True if the object has no children, false otherwise
+	 */
+	public boolean isLeaf (Object node) {
+		if (node != ROOT && !genome_data.containsKey (node))
+			return true;
+		else
+			return false;
+	}
+
+	/**
+	 * passes on listener removes to real tree model
+	 */
+	public void removeTreeModelListener (TreeModelListener list) {
+		model.removeTreeModelListener (list);
+	}
+
+	/**
+	 * Passes on events to real tree model
+	 */
+	public void valueForPathChanged (TreePath path, Object val) {
+		model.valueForPathChanged (path, val);
+	}
+
+	/**
+	 * represents an icon that shows whether a given node can be expanded (has
+	 * children)
+	 * 
+	 * @author rissman
+	 * 
+	 */
+	public static class SignedIcon implements Icon {
+
+		/**
+		 * whether this icon should show a plus or minus
+		 */
+		private boolean plus;
+
+		/**
+		 * class constants representing only two instances of this class
+		 */
+		private static SignedIcon PLUS = new SignedIcon (true);
+
+		private static SignedIcon MINUS = new SignedIcon (false);
+
+		/**
+		 * constructs new SignedIcon
+		 * 
+		 * @param p
+		 *            Whether it should show a plus or minus
+		 */
+		private SignedIcon (boolean p) {
+			plus = p;
+		}
+
+		/**
+		 * Entry point to get appropriate signed icon-controls how many are made
+		 * 
+		 * @param plus
+		 *            True if the icon should show a plus sign, false if it
+		 *            should show a minus sign
+		 * @return
+		 */
+		public static SignedIcon getSignedIcon (boolean plus) {
+			return plus ? PLUS : MINUS;
+		}
+
+		public int getIconHeight () {
+			return 16;
+		}
+
+		public int getIconWidth () {
+			return 16;
+		}
+
+		/**
+		 * draws plus or minus sign
+		 */
+		public void paintIcon (Component comp, Graphics g, int x, int y) {
+			g.drawRect (3, 3, 8, 8);
+			g.drawLine (5, 7, 9, 7);
+			if (plus)
+				g.drawLine (7, 5, 7, 9);
+		}
+
+	}
+}
diff --git a/src/org/gel/mauve/gui/sequence/AbstractSequencePanel.java b/src/org/gel/mauve/gui/sequence/AbstractSequencePanel.java
new file mode 100644
index 0000000..e265fca
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/AbstractSequencePanel.java
@@ -0,0 +1,151 @@
+package org.gel.mauve.gui.sequence;
+
+import java.util.List;
+
+import javax.swing.JPanel;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.ModelListener;
+
+
+public abstract class AbstractSequencePanel extends JPanel implements ModelListener
+{
+    // The Genome this panel displays
+    private Genome genome;
+    // Model for this component.
+    protected BaseViewerModel model;
+    // The percentage height of each rearrangement bar. The rest is used to draw highlighting.
+    public final static double BOX_FILL = 0.95;
+    
+    public AbstractSequencePanel(BaseViewerModel model, Genome genome)
+    {
+        this.model = model;
+        this.genome = genome;
+        model.addModelListener(this);
+    }
+    
+    protected final int boxTop()
+    {
+        return (int) (((1.0 - AbstractSequencePanel.BOX_FILL) / 2) * getHeight());
+    }
+
+    protected final int boxHeight()
+    {
+        return (int) (AbstractSequencePanel.BOX_FILL * getHeight());
+    }
+
+    public final long pixelToRightSequenceCoordinate(int pixel)
+    {
+        if (getWidth() == 0)
+            return 0;
+    
+        return (long) ((pixel * getGenome().getViewLength() / (double) getWidth()) + getGenome().getViewStart() + 2);
+    }
+
+    public final long pixelToLeftSequenceCoordinate(int pixel)
+    {
+        if (getWidth() == 0)
+            return 0;
+    
+        return (long) ((pixel * getGenome().getViewLength() / (double) getWidth()) + getGenome().getViewStart());
+    }
+
+    public long pixelToCenterSequenceCoordinate(double pixel)
+    {
+        if (getWidth() == 0)
+            return 0;
+        
+        return (long) ((pixel * getGenome().getViewLength() / getWidth()) + getGenome().getViewStart() + 1);
+    }
+
+    public final long pixelToCenterSequenceCoordinate(int pixel)
+    {
+        if (getWidth() == 0)
+            return 0;
+    
+        return (long) ((pixel * getGenome().getViewLength() / (double) getWidth()) + getGenome().getViewStart() + 1);
+    }
+
+    public final int sequenceCoordinateToRightPixel(long pos)
+    {
+        if (getGenome().getViewLength() == 0)
+            return 0;
+    
+        return (int) ((pos - getGenome().getViewStart()) * getWidth() / getGenome().getViewLength());
+    }
+
+    public final int sequenceCoordinateToLeftPixel(long pos)
+    {
+        if (getGenome().getViewLength() == 0)
+            return 0;
+    
+        return (int) ((pos - getGenome().getViewStart() - 1) * getWidth() / getGenome().getViewLength());
+    }
+
+    public final int sequenceCoordinateToCenterPixel(long pos)
+    {
+        if (getGenome().getViewLength() == 0)
+            return 0;
+    
+        return (int) ((pos - getGenome().getViewStart() - 0.5) * getWidth() / getGenome().getViewLength());
+    }
+
+    /**
+     * Converts a range of pixel coordinates to sequence coordinates.
+     */
+    protected final void pixelRangeToSequenceCoordinates(int start_pixel, int end_pixel, long[] seq_range)
+    {
+        seq_range[0] = pixelToLeftSequenceCoordinate(start_pixel);
+        seq_range[1] = pixelToRightSequenceCoordinate(end_pixel);
+        seq_range[1] = seq_range[0] > seq_range[1] ? seq_range[0] : seq_range[1];
+    }
+
+    /**
+     * Finds all LCBs intersecting a specified range of pixels in the current
+     * view.
+     * 
+     * @param start_pixel
+     *            The first coordinate of the intersection range
+     * @param end_pixel
+     *            The last coordinate of the intersection range
+     */
+    protected final List getLCBPixelRange(int start_pixel, int end_pixel)
+    {
+        long[] seq_range = new long[2];
+        pixelRangeToSequenceCoordinates(start_pixel, end_pixel, seq_range);
+    
+        if (model instanceof LcbViewerModel)
+        {
+            return ((LcbViewerModel) model).getLCBRange(getGenome(), seq_range[0], seq_range[1]);
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    protected final Genome getGenome()
+    {
+        return genome;
+    }
+    
+    // Empty implementations of ModelListenerEvents, for clarity of subclasses.
+    public void colorChanged(ModelEvent event) {}
+    public void weightChanged(ModelEvent event) {}
+    public void drawingSettingsChanged(ModelEvent event) {}
+    public void modeChanged(ModelEvent event) {}
+    public void modelReloadStart(ModelEvent event) {}
+    public void modelReloadEnd(ModelEvent event) {}
+    public void viewableRangeChangeStart(ModelEvent event) {}
+    public void viewableRangeChanged(ModelEvent event) {}
+    public void viewableRangeChangeEnd(ModelEvent event) {}
+    public void printingStart(ModelEvent event) {}
+    public void printingEnd(ModelEvent event) {}
+    public void genomesReordered(ModelEvent event) {}
+    public void referenceChanged(ModelEvent event) {}
+    public void genomeVisibilityChanged(ModelEvent event) {}
+    public void attributesChanged(ModelEvent event) {}
+}
diff --git a/src/org/gel/mauve/gui/sequence/ControlPanel.java b/src/org/gel/mauve/gui/sequence/ControlPanel.java
new file mode 100644
index 0000000..7cfbac3
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/ControlPanel.java
@@ -0,0 +1,263 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.gui.MauveFrame;
+import org.gel.mauve.gui.RearrangementPanel;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JToggleButton;
+import javax.swing.border.AbstractBorder;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.Color;
+
+
+public class ControlPanel extends AbstractSequencePanel implements org.gel.mauve.ModelListener {
+
+    static ImageIcon up_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/Up16.gif"));
+    static ImageIcon upRollover_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/UpRollover16.png"));
+    static ImageIcon down_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/Down16.gif"));
+    static ImageIcon downRollover_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/DownRollover16.png"));
+    static ImageIcon aPlus_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/aPlus16.png"));
+    static ImageIcon r_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/r16.png"));
+    static ImageIcon plus_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/plus16.png"));
+    static ImageIcon plusRollover_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/plusRollover16.png"));
+    static ImageIcon minus_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/minus16.png"));
+    static ImageIcon minusRollover_button_icon = new ImageIcon(MauveFrame.class.getResource("/images/minusRollover16.png"));
+	JButton moveUpButton;
+	JButton moveDownButton;
+	JButton plusMinusButton;
+	JToggleButton setReferenceButton;
+	JButton loadFeaturesButton;
+	final BaseViewerModel model;
+	final Genome genome;
+	final RearrangementPanel rrPanel;
+	Color defaultColor;
+    public ControlPanel(BaseViewerModel m, Genome g, RearrangementPanel rrpanel)
+    {
+    	super(m, g);
+    	this.model = m;
+    	this.genome = g;
+    	this.rrPanel = rrpanel;
+    	
+    	model.addModelListener(this);        
+
+    	Dimension buttonSize = new Dimension(16,16);
+
+        moveUpButton = new JButton(up_button_icon);
+        moveUpButton.setAction(new javax.swing.AbstractAction(){
+    		public void actionPerformed(java.awt.event.ActionEvent ae)
+    		{
+	            int[] new_order = new int[model.getSequenceCount()];
+	            for(int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+	            	new_order[seqI] = seqI;
+	            new_order[genome.getViewIndex()] = new_order[genome.getViewIndex()-1];
+	            new_order[genome.getViewIndex()-1] = genome.getViewIndex();
+	            rrPanel.reorderSequences(new_order);
+    		}
+    	});
+        moveUpButton.setIcon(up_button_icon);
+        moveUpButton.setRolloverIcon(upRollover_button_icon);
+        moveUpButton.setRolloverEnabled(true);
+        moveUpButton.setBorderPainted(false);
+    	moveUpButton.setToolTipText("Move this sequence up");
+    	moveUpButton.setContentAreaFilled(false);
+        moveUpButton.setEnabled(genome.getViewIndex() != 0);
+        moveUpButton.setPreferredSize(buttonSize);
+        moveUpButton.setSize(buttonSize);
+        moveUpButton.setMaximumSize(buttonSize);
+        moveUpButton.setBorder(BorderFactory.createEmptyBorder());
+        
+
+        plusMinusButton = new JButton();
+        plusMinusButton.setAction(new javax.swing.AbstractAction(){
+    		public void actionPerformed(java.awt.event.ActionEvent ae)
+    		{
+    			model.setVisible(genome, !genome.getVisible());
+    		}
+    	});
+        plusMinusButton.setIcon(minus_button_icon);
+        plusMinusButton.setRolloverIcon(minusRollover_button_icon);
+        plusMinusButton.setRolloverEnabled(true);
+        plusMinusButton.setBorderPainted(false);
+    	plusMinusButton.setToolTipText("Hide this sequence from the display");
+    	plusMinusButton.setContentAreaFilled(false);
+        plusMinusButton.setPreferredSize(buttonSize);
+        plusMinusButton.setSize(buttonSize);
+        plusMinusButton.setMaximumSize(buttonSize);
+        plusMinusButton.setBorder(BorderFactory.createEmptyBorder());
+        
+        setReferenceButton = new JToggleButton(r_button_icon);
+    	setReferenceButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e)
+            {
+        		model.setReference(genome);
+            }});
+    	setReferenceButton.setToolTipText("Set reference genome");
+    	setReferenceButton.setActionCommand("SetReference");
+    	setReferenceButton.setBorderPainted(false);
+    	setReferenceButton.setBackground(new java.awt.Color(255,255,255,0));
+//    	setReferenceButton.setContentAreaFilled(false);
+    	setReferenceButton.setSelected(model.getReference() == genome);
+    	setReferenceButton.setRolloverEnabled(true);
+    	setReferenceButton.setSize(buttonSize);
+    	setReferenceButton.setMaximumSize(buttonSize);
+    	setReferenceButton.setBorder(BorderFactory.createEmptyBorder());
+
+        loadFeaturesButton = new JButton(aPlus_button_icon);
+        loadFeaturesButton.setRolloverEnabled(true);
+        loadFeaturesButton.setToolTipText("Load sequence feature data");
+        loadFeaturesButton.setContentAreaFilled(false);
+        loadFeaturesButton.setBorderPainted(false);
+        loadFeaturesButton.setActionCommand("LoadFeatures");
+        loadFeaturesButton.setSize(buttonSize);
+        loadFeaturesButton.setMaximumSize(buttonSize);
+        loadFeaturesButton.setBorder(BorderFactory.createEmptyBorder());
+
+        moveDownButton = new JButton();
+    	moveDownButton.setAction(new javax.swing.AbstractAction(){
+    		public void actionPerformed(java.awt.event.ActionEvent ae)
+    		{
+	            int[] new_order = new int[model.getSequenceCount()];
+	            for(int seqI = 0; seqI < model.getSequenceCount(); seqI++)
+	            	new_order[seqI] = seqI;
+	            new_order[genome.getViewIndex()] = new_order[genome.getViewIndex()+1];
+	            new_order[genome.getViewIndex()+1] = genome.getViewIndex();
+	            rrPanel.reorderSequences(new_order);
+    		}
+    	});
+    	moveDownButton.setIcon(down_button_icon);
+    	moveDownButton.setRolloverIcon(downRollover_button_icon);
+    	moveDownButton.setToolTipText("Move this sequence down");
+    	moveDownButton.setActionCommand("MoveDown");
+    	moveDownButton.setContentAreaFilled(false);
+    	moveDownButton.setBorderPainted(false);
+        moveDownButton.setRolloverEnabled(true);
+        moveDownButton.setSize(buttonSize);
+        moveDownButton.setMaximumSize(buttonSize);
+        moveDownButton.setEnabled(genome.getViewIndex() != (model.getSequenceCount()-1));
+        moveDownButton.setBorder(BorderFactory.createEmptyBorder());
+        
+        if(genome.getVisible())
+        	doVisibleLayout();
+        else
+        	doInvisibleLayout();
+        
+        Dimension minSize = this.getSize();
+        minSize.height = 14*4;	// 16 for each button
+        minSize.width = 18;
+        this.setMinimumSize(minSize);
+    
+        Dimension prefSize = this.getSize();
+        prefSize.height = 20*4;	// 20 for each button
+        prefSize.width = 18;
+        this.setPreferredSize(prefSize);
+        
+        defaultColor = getBackground();
+        if(genome.getViewIndex() % 2 == 0)
+        	setBackground(java.awt.Color.WHITE);
+    }
+    
+    private void doVisibleLayout()
+    {
+    	setLayout(null);
+    	removeAll();
+        GridBagLayout layoutManager = new GridBagLayout();
+        setLayout(layoutManager);
+        GridBagConstraints c = new GridBagConstraints();
+        c.anchor = GridBagConstraints.CENTER;
+        c.fill = GridBagConstraints.NONE;
+        c.gridheight = 1;
+        c.gridwidth = 1;
+        c.gridx = 0;
+        c.insets = new Insets(0, 0, 0, 0);
+        c.ipadx = 0;
+        c.ipady = 0;
+        c.weightx = 1;
+        c.weighty = 0;
+        c.gridy = GridBagConstraints.RELATIVE;
+    	
+    	add(moveUpButton);
+        layoutManager.setConstraints(moveUpButton, c);
+    	add(plusMinusButton);
+        layoutManager.setConstraints(plusMinusButton, c);
+        add(setReferenceButton);
+        layoutManager.setConstraints(setReferenceButton, c);
+//        add(loadFeaturesButton);
+//        layoutManager.setConstraints(loadFeaturesButton, c);
+        add(moveDownButton);
+        layoutManager.setConstraints(moveDownButton, c);
+
+        plusMinusButton.setIcon(minus_button_icon);
+        plusMinusButton.setRolloverIcon(minusRollover_button_icon);
+    	plusMinusButton.setToolTipText("Hide this sequence from the display");
+        invalidate();
+        validate();
+        revalidate();
+    }
+    
+    private void doInvisibleLayout()
+    {
+    	setLayout(null);
+    	removeAll();
+    	GridBagLayout layoutManager = new GridBagLayout();
+        setLayout(layoutManager);
+        GridBagConstraints c = new GridBagConstraints();
+        c.anchor = GridBagConstraints.CENTER;
+        c.fill = GridBagConstraints.NONE;
+        c.gridheight = 1;
+        c.gridwidth = 1;
+        c.gridx = 0;
+        c.insets = new Insets(0, 0, 0, 0);
+        c.ipadx = 0;
+        c.ipady = 0;
+        c.weightx = 1;
+        c.weighty = 0;
+        c.gridy = GridBagConstraints.RELATIVE;
+    	
+    	add(plusMinusButton);
+        layoutManager.setConstraints(plusMinusButton, c);
+
+        plusMinusButton.setIcon(plus_button_icon);
+        plusMinusButton.setRolloverIcon(plusRollover_button_icon);
+    	plusMinusButton.setToolTipText("Show this sequence in the display");
+        invalidate();
+        validate();
+        revalidate();
+    }
+
+    public void genomesReordered(org.gel.mauve.ModelEvent me)
+    {
+        moveUpButton.setEnabled(genome.getViewIndex() != 0);
+        moveDownButton.setEnabled(genome.getViewIndex() != (model.getSequenceCount()-1));
+        if(genome.getViewIndex() % 2 == 0)
+        	setBackground(java.awt.Color.WHITE);
+        else
+        	setBackground(defaultColor);
+    }
+    
+    public void referenceChanged(org.gel.mauve.ModelEvent me)
+    {
+    	setReferenceButton.setSelected(model.getReference() == genome);
+    }
+    
+    public void genomeVisibilityChanged(ModelEvent event) 
+    {
+    	if(genome.getVisible()==true)
+    		doVisibleLayout();
+    	else
+    		doInvisibleLayout();
+
+    }
+}
diff --git a/src/org/gel/mauve/gui/sequence/FeatureFilterer.java b/src/org/gel/mauve/gui/sequence/FeatureFilterer.java
new file mode 100644
index 0000000..f2045df
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/FeatureFilterer.java
@@ -0,0 +1,329 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Set;
+
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.border.TitledBorder;
+
+import org.biojava.bio.gui.sequence.FeatureBlockSequenceRenderer;
+import org.biojava.bio.gui.sequence.FeatureRenderer;
+import org.biojava.bio.gui.sequence.FilteringRenderer;
+import org.biojava.bio.gui.sequence.MultiLineRenderer;
+import org.biojava.bio.gui.sequence.OverlayRendererWrapper;
+import org.biojava.utils.ChangeVetoException;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.FilterCacheSpec;
+import org.gel.mauve.SeqFeatureData;
+import org.gel.mauve.SupportedFormat;
+import org.gel.mauve.gui.MySymbolSequenceRenderer;
+
+/**
+ * Allows a user to select which features should be viewable by feature type
+ * 
+ * @author rissman
+ * 
+ */
+public class FeatureFilterer extends JFrame implements ActionListener {
+
+	/**
+	 * model this filterer is associated with
+	 */
+	protected BaseViewerModel model;
+
+	/**
+	 * contains the overlay renderers associated with this filterer's
+	 * BaseViewerModel
+	 */
+	protected LinkedList multis;
+
+	/**
+	 * maps MultiLineRenderers to associated OverlayRendererWrappers
+	 */
+	protected Hashtable multi_map;
+
+	/**
+	 * maps MultiLineRenderers to associated MySymbolSequenceRenderers
+	 */
+	protected Hashtable multi_to_symbol;
+
+	/**
+	 * maps a string representing feature type to a LinkedList of arrays
+	 * containing a FilterCacheSpec containing a FeatureFilter associated with a
+	 * given type and its associated OverlayRendererWrapper
+	 */
+	protected Hashtable filter_specs;
+
+	/**
+	 * reference to panel that hold checkboxes; needed to add new types
+	 */
+	protected JPanel checks;
+
+	protected JScrollPane scroll;
+	JPanel outer;
+
+	/**
+	 * holds already created filterers to help with factory method of creation
+	 */
+	protected static final Hashtable FILTERERS = new Hashtable ();
+
+	/**
+	 * constructs new FeatureFilterer. Is private to ensure only one instance is
+	 * created per BaseViewerModel
+	 * 
+	 * @param mod
+	 *            The model this filterer is associated with
+	 */
+	protected FeatureFilterer (BaseViewerModel mod) {
+		super ("Feature Filterer");
+		model = mod;
+		filter_specs = new Hashtable ();
+		multis = new LinkedList ();
+		multi_map = new Hashtable ();
+		multi_to_symbol = new Hashtable ();
+		initFeatureTypes ();
+		initGUI ();
+		FILTERERS.put (mod, this);
+	}
+
+	/**
+	 * initializes gui components
+	 */
+	protected void initGUI () {
+		Enumeration keys = filter_specs.keys ();
+		checks = new JPanel (new GridLayout (0, 1));
+		scroll = new JScrollPane (checks);
+		scroll.setMaximumSize (new Dimension (220, 500));
+		while (keys.hasMoreElements ())
+			addCheckBox ((String) keys.nextElement ());
+		getContentPane ().setLayout (new BorderLayout (5, 5));
+		outer = (JPanel) getContentPane ();//new JPanel ();
+		outer.add (scroll);
+		TitledBorder title = BorderFactory
+				.createTitledBorder ("All checked types will be displayed");
+		outer.setBorder (BorderFactory.createCompoundBorder (BorderFactory
+				.createCompoundBorder (BorderFactory.createEmptyBorder (5, 8,
+						10, 8), title), BorderFactory.createEmptyBorder (5, 5,
+				5, 5)));
+		Dimension size = title.getMinimumSize (outer);
+		outer.setPreferredSize (new Dimension (size.width + 26, outer
+				.getPreferredSize ().height));
+		//getContentPane ().add (outer);
+		pack ();
+	}
+
+	protected void addCheckBox (final String type) {
+		SwingUtilities.invokeLater (new Runnable () {
+			public void run () {
+				boolean vis = false;
+				if (isVisible ()) {
+					vis = true;
+					setVisible (false);
+				}
+				JCheckBox item = new JCheckBox (type, true);
+				item.addActionListener (FeatureFilterer.this);
+				checks.add (item);
+				Dimension size = getSize ();
+				size.height = Math.min (size.height
+						+ item.getPreferredSize ().height, 500);
+				setSize (size);
+				if (vis)
+					setVisible (true);
+			}
+		});		
+	}
+
+	/**
+	 * initializes selection of possible renderable features
+	 * 
+	 */
+	protected void initFeatureTypes () {
+		int count = model.getSequenceCount ();
+		for (int i = 0; i < count; i++) {
+			SupportedFormat format = model.getGenomeBySourceIndex (i)
+					.getAnnotationFormat ();
+			if(format == null)
+				continue;	// aed: If no annotation was loaded, then getAnnotationFormat returns null
+			FilterCacheSpec [] specs = format.getFilterCacheSpecs ();
+			for (int j = 0; j < specs.length; j++) {
+				// will eventually be mapped to whether these sorts are
+				// currently being displayed
+				if (specs[j].getFeatureRenderer () != null) {
+					addSpecForType (specs[j]);
+				}
+			}
+		}
+	}
+
+	protected void addSpecForType (FilterCacheSpec spec) {
+		String type = SeqFeatureData.getTypeFromFilterSpec (spec);
+		LinkedList vals = (LinkedList) filter_specs.get (type);
+		if (vals == null) {
+			vals = new LinkedList ();
+			filter_specs.put (type, vals);
+		}
+		vals.add (new Object [] {spec, null});
+	}
+
+	/**
+	 * tracks MultiLineRenderer and their associated MySymbolSequenceRenderers
+	 * used by the model associated with this FeatureFilterer
+	 * 
+	 * @param multi
+	 *            a MultiLineRenderer
+	 * @param my_symbol
+	 *            MySymbolSequenceRenderer used by multi
+	 */
+	public void addMultiRenderer (MultiLineRenderer multi,
+			MySymbolSequenceRenderer my_symbol) {
+		multis.add (multi);
+		multi_map.put (multi, new HashSet ());
+		multi_to_symbol.put (multi, my_symbol);
+	}
+
+	/**
+	 * if this OverlayRendererWrapper contains a FeatureRenderer associated with
+	 * a FilterCacheSpec stored in filter_specs, associates this renderer with
+	 * the appropriate type and FilterCacheSpec
+	 * 
+	 * @param multi
+	 *            the MultiLineRenderer containing over
+	 * @param over
+	 *            the OverlayWrapperRenderer to associate
+	 */
+	public void addOverlayRenderer (MultiLineRenderer multi,
+			OverlayRendererWrapper over) {
+		try {
+			FeatureRenderer rend = ((FeatureBlockSequenceRenderer) ((FilteringRenderer) over
+					.getRenderer ()).getRenderer ()).getFeatureRenderer ();
+			Iterator itty = filter_specs.values ().iterator ();
+			outer: while (itty.hasNext ()) {
+				Iterator it = ((LinkedList) itty.next ()).iterator ();
+				while (it.hasNext ()) {
+					Object [] val = (Object []) it.next ();
+					if (((FilterCacheSpec) val[FlatFileFeatureConstants.FILTER_SPEC_INDEX])
+							.getFeatureRenderer ().equals (rend)) {
+						if (val[FlatFileFeatureConstants.OVERLAY_REND_INDEX] == null) {
+							val[FlatFileFeatureConstants.OVERLAY_REND_INDEX] = over;
+							HashSet set = (HashSet) multi_map.get (multi);
+							if (set == null)
+								System.out
+								.println ("multi not yet added: " + multi);
+							else
+								set.add (over);
+							continue outer;
+						}
+					}
+				}
+			}
+		} catch (ClassCastException e) {
+			e.printStackTrace ();
+		}
+	}
+
+	/**
+	 * called when a user chooses to filter features from the match panel pop-up
+	 * menu
+	 * 
+	 * @param e
+	 *            The action event triggering the method call
+	 */
+	public void actionPerformed (ActionEvent e) {
+		if (e.getSource () instanceof JCheckBox) {
+			JCheckBox box = (JCheckBox) e.getSource ();
+			Iterator itty = ((LinkedList) filter_specs
+					.get (box.getLabel ())).iterator ();
+			while (itty.hasNext ()) {
+				Object [] val = (Object []) itty.next ();
+				OverlayRendererWrapper over = (OverlayRendererWrapper) val[FlatFileFeatureConstants.OVERLAY_REND_INDEX];
+				System.out.println ("overlay: " + over);
+				addOrRemove (over, box.isSelected ());
+			}
+			if (box.isSelected ()) {
+				resetMultiRenderers ();
+			}
+		} else
+			setVisible (true);
+	}
+
+	public void addOrRemove (OverlayRendererWrapper over, boolean add) {
+		Iterator it = multis.iterator ();
+		while (it.hasNext ()) {
+			MultiLineRenderer multi = (MultiLineRenderer) it.next ();
+			System.out.println ("playing with multi: " + multi);
+			if (((HashSet) multi_map.get (multi)).contains (over)) {
+				System.out.println ("more than playing");
+				try {
+					if (add)
+						multi.addRenderer (over);
+					else
+						multi.removeRenderer (over);
+				} catch (ChangeVetoException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+	
+	protected void resetMultiRenderers () {
+		Iterator itty = multis.iterator ();
+		while (itty.hasNext ()) {
+			try {
+				MultiLineRenderer multi = (MultiLineRenderer) itty.next ();
+				MySymbolSequenceRenderer rend = (MySymbolSequenceRenderer) multi_to_symbol
+						.get (multi);
+				multi.removeRenderer (rend);
+				multi.addRenderer (rend);
+			} catch (ChangeVetoException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace ();
+			}
+		}
+	}
+
+	public Set getFeatureTypes () {
+		return filter_specs.keySet ();
+	}
+
+	/**
+	 * used in place of constructor to retrieve correct FeatureFilterer for
+	 * genome
+	 * 
+	 * @param mod
+	 *            The BaseViewerModel this filterer should be associated with
+	 * @return The desired FeatureFilterer
+	 */
+	public static FeatureFilterer getFilterer (BaseViewerModel mod) {
+		FeatureFilterer filter = (FeatureFilterer) FILTERERS.get (mod);
+		if (filter == null)
+			filter = new FeatureFilterer (mod);
+		return filter;
+	}
+
+	/**
+	 * removes a filterer from the cache of filterers; should only be called
+	 * when a BaseViewerModel will no longer be used
+	 * 
+	 * @param mod
+	 *            The model that will no longer be used
+	 */
+	public static void removeFilterer (BaseViewerModel mod) {
+		FILTERERS.remove (mod);
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/sequence/FeaturePanel.java b/src/org/gel/mauve/gui/sequence/FeaturePanel.java
new file mode 100644
index 0000000..573c0c5
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/FeaturePanel.java
@@ -0,0 +1,505 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.BasicStroke;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.EventListener;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.AbstractAction;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JTabbedPane;
+import javax.swing.Timer;
+import javax.swing.ToolTipManager;
+import javax.swing.event.EventListenerList;
+
+import org.biojava.bio.gui.sequence.AbstractBeadRenderer;
+import org.biojava.bio.gui.sequence.FeatureBlockSequenceRenderer;
+import org.biojava.bio.gui.sequence.FilteringRenderer;
+import org.biojava.bio.gui.sequence.MultiLineRenderer;
+import org.biojava.bio.gui.sequence.OverlayRendererWrapper;
+import org.biojava.bio.gui.sequence.RectangularBeadRenderer;
+import org.biojava.bio.gui.sequence.SequenceRenderer;
+import org.biojava.bio.gui.sequence.SequenceViewerEvent;
+import org.biojava.bio.gui.sequence.SequenceViewerListener;
+import org.biojava.bio.gui.sequence.SequenceViewerMotionListener;
+import org.biojava.bio.gui.sequence.TranslatedSequencePanel;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.symbol.Location;
+import org.biojava.bio.symbol.LocationTools;
+import org.biojava.utils.ChangeVetoException;
+import org.gel.mauve.BrowserLauncher;
+import org.gel.mauve.DbXrefFactory;
+import org.gel.mauve.FilterCacheSpec;
+import org.gel.mauve.Genome;
+import org.gel.mauve.GenomeBuilder;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.gui.MySymbolSequenceRenderer;
+import org.gel.mauve.gui.QualifierPanel;
+
+/**
+ * @author Paul Infield-Harm, Aaron Darling (derived from FastBeadDemo by <a
+ *         href="mailto:kdj at sanger.ac.uk">Keith James) </a>
+ * 
+ * A panel that shows features, duh!
+ *  
+ */
+public class FeaturePanel extends AbstractSequencePanel
+{
+	public static final int DEFAULT_WIDTH = 10000;
+	public static final int DEFAULT_HEIGHT = 40;
+    public static final int DEFAULT_MAX_FEATURE_DISPLAY_RANGE = 500000;
+    private TranslatedSequencePanel trans;
+    private Sequence seq;
+    private final GenbankMenuItemBuilder gmib = new GenbankMenuItemBuilder();
+    private final DbXrefMenuItemBuilder dmib = new DbXrefMenuItemBuilder();
+    private final FeaturePopupMenuBuilder fpmb = new FeaturePopupMenuBuilder();
+    
+    public FeaturePanel(Genome genome, BaseViewerModel model)
+    {
+        super(model, genome);
+        setLayout(new BorderLayout());
+        init();
+    }
+
+    // This prevents the display of the translatedSequencePanel at the wrong
+    // scale...
+    public void setBounds(int arg0, int arg1, int arg2, int arg3)
+    {
+        super.setBounds(arg0, arg1, arg2, arg3);
+
+        if (trans != null)
+            adjustScaleAndTranslation();
+    }
+
+    private void clearTransPanel()
+    {
+        if (trans != null)
+        {
+            remove(trans);
+            trans = null;
+        }
+    }
+
+    private void init()
+    {
+        seq = getGenome().getAnnotationSequence();
+
+        if (seq == null)
+        {
+            clearTransPanel();
+            return;
+        }
+
+        if (trans != null)
+        {
+            remove(trans);
+        }
+        
+        trans = new TranslatedSequencePanel();
+        add(trans, BorderLayout.CENTER);
+
+        try
+        {
+            trans.setSequence(seq);
+            trans.setDirection(TranslatedSequencePanel.HORIZONTAL);
+
+            trans.addSequenceViewerMotionListener(new ToolTipMotionListener());
+            trans.addSequenceViewerListener(new ClickListener());
+
+            MultiLineRenderer multi = new MultiLineRenderer();
+            FilterCacheSpec[] specs = getGenome().getAnnotationFormat().getFilterCacheSpecs();
+            FeatureFilterer filterer = FeatureFilterer.getFilterer (model);
+            MySymbolSequenceRenderer my_symbol = new MySymbolSequenceRenderer();
+            filterer.addMultiRenderer (multi, my_symbol);
+            
+            for (int i = 0; i < specs.length; i++)
+            {
+                FilterCacheSpec spec = specs[i];
+                if (spec.getFeatureRenderer() != null)
+                {
+                	makeRenderer (model, multi, spec);
+                }
+            }
+            
+            multi.addRenderer(my_symbol);
+            trans.setRenderer(multi);
+            
+            // set the size of this element
+            Dimension my_size = new Dimension();
+            my_size.height = DEFAULT_HEIGHT;
+            my_size.width = DEFAULT_WIDTH;
+            setNewSize (my_size);
+        }
+        catch (ChangeVetoException e)
+        {
+            JOptionPane.showMessageDialog(this, "Could not render pane", "Rendering error", JOptionPane.ERROR_MESSAGE);
+            clearTransPanel();
+        }
+        
+        // add menu item builders
+        fpmb.addMenuItemBuilder(gmib);
+        fpmb.addMenuItemBuilder(dmib);
+    }
+    
+	/**
+	 * makes renderers for displaying a type of feature
+	 * 
+	 * @param multi
+	 *            the MultiLineRenderer that renders features for this panel
+	 * @param spec
+	 *            the FilterCacheSpec that describes the feature type
+	 * @throws ChangeVetoException
+	 */
+	protected static void makeRenderer (BaseViewerModel model,
+			MultiLineRenderer multi, FilterCacheSpec spec)
+			throws ChangeVetoException {
+		FeatureBlockSequenceRenderer fbr = new FeatureBlockSequenceRenderer ();
+		fbr.setFeatureRenderer (spec.getFeatureRenderer ());
+		fbr.setCollapsing (false);
+		OverlayRendererWrapper over = new OverlayRendererWrapper (
+				new FilteringRenderer (fbr, spec.getFilter (), true));
+		FeatureFilterer.getFilterer (model).addOverlayRenderer (multi, over);
+		multi.addRenderer (over);
+	}
+	
+	public void resizeForMoreFeatures () {
+		Dimension my_size = getSize ();
+		my_size.height += MauveConstants.FEATURE_HEIGHT;
+		setNewSize (my_size);
+	}
+	
+	private void setNewSize (Dimension my_size) {
+		setSize (my_size);
+		setPreferredSize (my_size);
+		setMaximumSize (my_size);
+		setMinimumSize (my_size);
+		trans.setSize (my_size);
+		trans.setPreferredSize (my_size);
+		trans.setMaximumSize (my_size);
+		trans.setMinimumSize (my_size);
+	}
+
+    public FeaturePopupMenuBuilder getFeaturePopupMenuBuilder(){ return fpmb; }
+    public DbXrefMenuItemBuilder getDbXrefMenuItemBuilder(){ return dmib; }
+    public GenbankMenuItemBuilder getGenbankMenuItemBuilder(){ return gmib; }
+    
+    //not being called
+    private SequenceRenderer barRenderer(String type, Color innerColor, double depth, StrandedFeature.Strand strand) throws ChangeVetoException
+    {
+    	FeatureFilter filter = new FeatureFilter.And(new FeatureFilter.ByType(type),new FeatureFilter.StrandFilter(strand));
+        FeatureBlockSequenceRenderer fbr = new FeatureBlockSequenceRenderer();
+        double offset = strand == StrandedFeature.POSITIVE ? 0 : 5;
+        if(strand==StrandedFeature.NEGATIVE)
+        	offset = 10;
+        RectangularBeadRenderer renderer = new RectangularBeadRenderer(depth, offset, Color.BLACK, innerColor, new BasicStroke());
+        renderer.setHeightScaling(false);
+        fbr.setFeatureRenderer(renderer);
+        fbr.setCollapsing(false);
+        return new OverlayRendererWrapper(new FilteringRenderer(fbr, filter, true));
+    }
+
+    private void adjustScaleAndTranslation()
+    {
+        if (getSize().width != 0)
+        {
+            int width = this.getSize().width;
+            double scale = (double) width / (double) getGenome().getViewLength();
+
+            if (getGenome().getViewStart() >= seq.length() ||
+                    getGenome().getViewLength() >= model.getDrawAnnotationThreshold())
+            {
+                // TranslatedSequencePanel can't handle being translated out of
+                // visibility, and we want to limit the viewable range for 
+            	// better performance
+                trans.setVisible(false);
+            }
+            else
+            {
+                trans.setScale(scale);
+                trans.setSymbolTranslation((int) getGenome().getViewStart());
+                trans.setVisible(true);
+            }
+        }
+    }
+
+    
+    private final class DbXrefMenuAction extends AbstractAction
+	{
+    	protected String url;
+    	protected String db_name;
+    	
+    	DbXrefMenuAction( String url, String db_name, String feature_name ){
+    		super("View " + feature_name + " in " + db_name);
+    		this.url = url;
+    		this.db_name = db_name;
+    	}
+    	public void actionPerformed( ActionEvent e ){
+    		try{
+    			BrowserLauncher.openURL(url);
+    		}catch(IOException ioe){}
+    	}
+	}
+
+	private final class GenbankMenuAction extends AbstractAction
+	{
+    	protected int seq_index;
+    	
+    	GenbankMenuAction( int seq_index ){
+    		super("View GenBank annotation for features at " + seq_index);
+    		this.seq_index = seq_index;
+    	}
+
+    	public void actionPerformed( ActionEvent e ){
+
+            Location loc = LocationTools.makeLocation(seq_index, seq_index);
+            System.err.println("Starting with " + seq.countFeatures() + " features");
+            FeatureHolder fh = seq.filter(new FeatureFilter.And(new FeatureFilter.OverlapsLocation(loc), new FeatureFilter.Not(new FeatureFilter.ByType(GenomeBuilder.MAUVE_AGGREGATE))));
+            System.err.println("Filtering leaves " + fh.countFeatures() + " features.");
+            if (fh.countFeatures() == 0)
+                return;
+            
+            JDialog dialog = new JDialog((JFrame) FeaturePanel.this.getTopLevelAncestor(), "Feature Detail", true);
+
+            JTabbedPane tabs = new JTabbedPane();
+            dialog.getContentPane().add(tabs, BorderLayout.CENTER);
+
+            for (Iterator fi = fh.features(); fi.hasNext();)
+            {
+                Feature f = (Feature) fi.next();
+                tabs.add(new QualifierPanel(f));
+            }
+            dialog.setSize(800, 800);
+            dialog.pack();
+            dialog.setVisible(true);
+    	}
+	}
+	
+	public interface FeatureMenuItemBuilder extends EventListener
+	{
+		public JMenuItem[] getItem(SequenceViewerEvent sve, Genome g, BaseViewerModel model);
+	}
+	
+	public class FeaturePopupMenuBuilder
+	{
+	    protected EventListenerList builders = new EventListenerList();
+		public void addMenuItemBuilder(FeatureMenuItemBuilder fmib)
+		{
+			builders.add(FeatureMenuItemBuilder.class, fmib);
+		}
+		public JPopupMenu build(SequenceViewerEvent sve, Genome g, BaseViewerModel model)
+		{
+			Object[] listeners = builders.getListenerList();
+        	JPopupMenu leMenu = new JPopupMenu();
+        	for (int i = listeners.length-2; i>=0; i-=2) {
+                if (listeners[i]==FeatureMenuItemBuilder.class) {
+            		JMenuItem[] items = ((FeatureMenuItemBuilder)listeners[i+1]).getItem(sve, g, model);
+            		for(int j = 0; j < items.length; j++)
+            			leMenu.add(items[j]);
+                }
+            }
+			return leMenu;
+		}		
+		public void removeMenuItemBuilder(FeatureMenuItemBuilder fmib)
+		{
+			builders.remove(FeatureMenuItemBuilder.class, fmib);
+		}
+	}
+	
+	
+	private class GenbankMenuItemBuilder implements FeatureMenuItemBuilder
+	{
+		public JMenuItem[] getItem(SequenceViewerEvent sve, Genome g, BaseViewerModel model)
+		{
+        	JMenuItem gbk_item = new JMenuItem();
+        	gbk_item.setAction( new GenbankMenuAction(sve.getPos()) );
+        	return new JMenuItem[]{gbk_item};
+		}
+	}
+	private class DbXrefMenuItemBuilder implements FeatureMenuItemBuilder
+	{
+		public JMenuItem[] getItem(SequenceViewerEvent sve, Genome g, BaseViewerModel model)
+		{
+			Vector items = new Vector();
+            Object t = sve.getTarget();
+
+            if (t instanceof FeatureHolder)
+            {
+            	// we'll be popping up a menu with DB xref options
+            	// for the user
+
+            	FeatureHolder fh = (FeatureHolder) t;
+                for (Iterator fi = fh.features(); fi.hasNext();)
+                {
+                    Feature f = (Feature) fi.next();
+
+                    if (f.getAnnotation().containsProperty("db_xref"))
+                    {
+                    	String feature_name = f.getType() + " ";
+                    	if( f.getAnnotation().containsProperty("gene") )
+                    		feature_name += f.getAnnotation().getProperty("gene");
+                    	else if( f.getAnnotation().containsProperty("locus_tag") )
+                    		feature_name += f.getAnnotation().getProperty("locus_tag");
+                    	else
+                    		feature_name += f.getLocation();
+                    	String db_xref = f.getAnnotation().getProperty("db_xref").toString();
+                    	if( db_xref.charAt(0) == '[' )
+                    		db_xref = db_xref.substring(1, db_xref.length() - 1 );
+
+                    	// tokenize on ,
+                    	StringTokenizer comma_tok = new StringTokenizer(db_xref, ",");
+                    	// if no tokens then don't bother with a menu
+                    	// just display the GenBank annotation...
+                    	while(comma_tok.hasMoreTokens()){
+                    		String cur_xref = comma_tok.nextToken();
+                    		cur_xref = cur_xref.trim();
+	                    	// for each db xref, try to get its URL
+                    		try{
+		                    	DbXrefFactory dxuf = DbXrefFactory.getInstance();
+		                    	String db_url = dxuf.getDbURL(cur_xref);
+		                    	String db_name = dxuf.getDbName(cur_xref);
+		                    	JMenuItem xref_item = new JMenuItem();
+		                    	xref_item.setAction( new DbXrefMenuAction(db_url,db_name,feature_name) );
+		                    	items.add(xref_item);
+                    		}catch(DbXrefFactory.UnknownDatabaseException ude)
+							{
+                    			System.err.println(ude.getMessage());
+							}
+                    	}
+                    }
+                }
+           }
+           JMenuItem[] items_array = new JMenuItem[items.size()];
+           items.toArray(items_array);
+           return items_array;
+		}
+	}
+
+    private final class ClickListener implements SequenceViewerListener
+    {
+        public void mouseClicked(SequenceViewerEvent sve)
+        {            
+        	JPopupMenu jpm = fpmb.build(sve, getGenome(), model);
+        	jpm.show(sve.getMouseEvent().getComponent(), sve.getMouseEvent().getX(), sve.getMouseEvent().getY());
+
+        }
+
+        public void mousePressed(SequenceViewerEvent sve)
+        {
+            // This space intentionally left blank.
+        }
+
+        public void mouseReleased(SequenceViewerEvent sve)
+        {
+            // This space intentionally left blank.
+        }
+    }
+
+    private final class ToolTipMotionListener implements SequenceViewerMotionListener, ActionListener
+    {
+    	Timer tooltipDelayResetTimer = new Timer(2000, this);
+        public void mouseDragged(SequenceViewerEvent sve)
+        {
+            //This space intentionally left blank.
+        }
+
+        public void mouseMoved(SequenceViewerEvent sve)
+        {
+            Object t = sve.getTarget();
+
+            if (t != null && t instanceof FeatureHolder && 
+            		((FeatureHolder) t).countFeatures() > 0) {
+            	ToolTipManager.sharedInstance().setInitialDelay(1);
+            	ToolTipManager.sharedInstance().setDismissDelay(100000);
+                FeatureHolder fh = (FeatureHolder) t;
+                ToolTipManager.sharedInstance().registerComponent (trans);
+                StringBuffer msg = new StringBuffer("<HTML>");
+                for (Iterator fi = fh.features(); fi.hasNext();)
+                {
+                    Feature f = (Feature) fi.next();
+                    if(f.getAnnotation() == null)
+                    	continue;
+                    msg.append(f.getType() + " ");
+                    msg.append(f.getLocation());
+                    msg.append("<br>");
+
+                    if (f.getAnnotation().containsProperty("gene"))
+                    {
+                    	msg.append(" <b>");
+                    	msg.append(f.getAnnotation().getProperty("gene"));
+                    	msg.append("</b>");
+                    }
+                    else if (f.getAnnotation().containsProperty("locus_tag"))
+                    {
+                    	msg.append(" <b>");
+                    	msg.append(f.getAnnotation().getProperty("locus_tag"));
+                    	msg.append("</b>");
+                    }
+                    	
+                    if (f.getAnnotation().containsProperty("product"))
+                    {
+                        msg.append("   ");
+                        msg.append(f.getAnnotation().getProperty("product"));
+                    }
+
+                    if (fi.hasNext())
+                    {
+                        msg.append("<BR>");
+                    }
+                }
+                trans.setToolTipText(msg.toString());
+                tooltipDelayResetTimer.start();
+            }
+            else
+            {
+                trans.setToolTipText(null);
+                ToolTipManager.sharedInstance().unregisterComponent (trans);
+            }
+        }
+        
+        public void actionPerformed(ActionEvent ae){
+        	ToolTipManager.sharedInstance().setInitialDelay(2000);
+        	ToolTipManager.sharedInstance().setDismissDelay(10000);
+        }
+    }
+
+    public void viewableRangeChanged(ModelEvent event)
+    {
+        if (trans != null)
+        {
+            adjustScaleAndTranslation();
+        }
+    }
+    
+    public void drawingSettingsChanged(ModelEvent event) 
+    {
+        if (getSize().width != 0)
+        {
+	        if (getGenome().getViewStart() >= seq.length() ||
+	                getGenome().getViewLength() >= model.getDrawAnnotationThreshold())
+	        {
+	            trans.setVisible(false);
+	        }
+	        else
+	        {
+	            trans.setVisible(true);
+	        }
+        }
+    }
+}
diff --git a/src/org/gel/mauve/gui/sequence/FlatFileFeatureConstants.java b/src/org/gel/mauve/gui/sequence/FlatFileFeatureConstants.java
new file mode 100644
index 0000000..68cb5e9
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/FlatFileFeatureConstants.java
@@ -0,0 +1,68 @@
+package org.gel.mauve.gui.sequence;
+
+import org.gel.mauve.MauveConstants;
+
+public interface FlatFileFeatureConstants extends MauveConstants {
+
+	/**
+	 * strings representing required information for general flat file feature
+	 * type
+	 */
+	public final static String TYPE_STRING = "type";
+
+	public final static String LABEL_STRING = "label";
+
+	public final static String CONTIG_STRING = "contig";
+
+	public final static String STRAND_STRING = "strand";
+
+	public final static String LEFT_STRING = "left_end";
+
+	public final static String RIGHT_STRING = "right_end";
+
+	public final static String FORWARD = "forward";
+	
+	public final static String COMPLEMENT = "complement";
+
+	/**
+	 * integers that programmatically represent the required information in the
+	 * file
+	 */
+	public final static int TYPE = 0;
+
+	public final static int LABEL = 1;
+
+	public final static int CONTIG = 2;
+
+	public final static int STRAND = 3;
+
+	public final static int LEFT = 4;
+
+	public final static int RIGHT = 5;
+	
+	/**
+	 * known feature types
+	 */
+	public static final String CDS = "cds";
+
+	/**
+	 * Array that converts String to numeric representation of required fields
+	 * in flat file
+	 */
+	public final static String [] FLAT_FEATURE_REQ_INFO = {TYPE_STRING,
+			LABEL_STRING, CONTIG_STRING, STRAND_STRING, LEFT_STRING,
+			RIGHT_STRING};
+	
+	/**
+	 * array index for FilterCacheSpecs in filter_specs
+	 */
+	public static final int FILTER_SPEC_INDEX = 0;
+
+	/**
+	 * array index for OverlayRendererWrappers in filter_specs
+	 */
+	public static final int OVERLAY_REND_INDEX = 1;
+	
+	public static final double NO_OFFSET = -1.0;
+
+}
diff --git a/src/org/gel/mauve/gui/sequence/FlatFileFeatureImporter.java b/src/org/gel/mauve/gui/sequence/FlatFileFeatureImporter.java
new file mode 100644
index 0000000..fd419c5
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/FlatFileFeatureImporter.java
@@ -0,0 +1,318 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.BasicStroke;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import org.biojava.bio.SimpleAnnotation;
+import org.biojava.bio.gui.sequence.MultiLineRenderer;
+import org.biojava.bio.gui.sequence.OverlayRendererWrapper;
+import org.biojava.bio.gui.sequence.RectangularBeadRenderer;
+import org.biojava.bio.seq.FeatureFilter;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.symbol.RangeLocation;
+import org.biojava.utils.ChangeVetoException;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.FilterCacheSpec;
+import org.gel.mauve.Genome;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.gui.GenomeCellRenderer;
+import org.gel.mauve.gui.MauveFrame;
+
+public class FlatFileFeatureImporter extends JFrame implements ActionListener,
+		Comparator, FlatFileFeatureConstants {
+
+	/**
+	 * necessary class gui variable
+	 */
+	protected JFileChooser chooser;
+
+	protected JComboBox genome_choices;
+
+	/**
+	 * Vector of required annotations for general flat file feature type-- can
+	 * be added to for further specificity in subclasses
+	 */
+	public Vector req_fields;
+
+	/**
+	 * parent MauveFrame
+	 */
+	protected MauveFrame mauve;
+	
+	protected BaseViewerModel model;
+
+	protected FeatureFilterer filterer;
+	
+	protected double [] offset;
+
+	/**
+	 * creates a FeatureImporter associated with the specified mauve frame
+	 * 
+	 * @param mauve_frame
+	 */
+	public FlatFileFeatureImporter (MauveFrame mauve_frame) {
+		super ("Import Annotation File");
+		mauve = mauve_frame;
+		model = mauve.getModel ();
+		offset = new double [model.getSequenceCount ()];
+		for (int i = 0; i < offset.length; i++)
+			offset [i] = MauveConstants.FEATURE_HEIGHT + 5;
+		req_fields = new Vector ();
+		req_fields.add (LABEL_STRING);
+		initGUI ();
+	}
+
+	/**
+	 * initializes gui components
+	 * 
+	 */
+	protected void initGUI () {
+		JPanel pane = new JPanel (new BorderLayout (0, 0));
+		chooser = new JFileChooser ();
+		chooser.setApproveButtonText ("Import");
+		chooser.setApproveButtonToolTipText ("Import file");
+		chooser.addActionListener (this);
+		pane.add (chooser);
+		genome_choices = new JComboBox (model.getGenomes ());
+		genome_choices.setRenderer (GenomeCellRenderer.getListCellRenderer ());
+		JPanel north = new JPanel (new FlowLayout (FlowLayout.LEFT, 0, 0));
+		north.add (new JLabel ("Genome to which annotations belong:  "));
+		north.add (genome_choices);
+		north.setBorder (chooser.getBorder ());
+		pane.add (north, BorderLayout.NORTH);
+		getContentPane ().add (pane);
+		pack ();
+	}
+
+	public static int getIndexOfField (String field) {
+		for (int i = 0; i < FLAT_FEATURE_REQ_INFO.length; i++) {
+			if (FLAT_FEATURE_REQ_INFO[i].equals (field))
+				return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Tries to load new annotations from a file
+	 * 
+	 * @param file
+	 *            The file containing the new annotation information
+	 * @param genome
+	 *            The genome to which the annotations belong
+	 */
+	public void importAnnotationFile (File file, Genome genome) {
+		try {
+			BufferedReader in = new BufferedReader (new FileReader (file));
+			StringTokenizer toke = new StringTokenizer (in.readLine ().trim (),
+					"\t");
+			String [] fields = new String [toke.countTokens ()];
+			String [] vals = new String [fields.length];
+			int [] req = new int [FLAT_FEATURE_REQ_INFO.length];
+			String s = null;
+			for (int i = 0; i < fields.length; i++) {
+				s = toke.nextToken ();
+				int ind = getIndexOfField (s);
+				if (ind == -1)
+					fields[i] = s;
+				else
+					req[ind] = i;
+			}
+			s = in.readLine ();
+			StrandedFeature.Template template = new StrandedFeature.Template ();
+			List list = new ArrayList (genome.getChromosomes ());
+			String [] contig_list = new String [list.size ()];
+			Collections.sort (list, this);
+			for (int i = 0; i < list.size (); i++) {
+				contig_list[i] = ((Chromosome) list.get (i)).getName ()
+						.toLowerCase ();
+			}
+			filterer = FeatureFilterer.getFilterer (model);
+			Set types = filterer.getFeatureTypes ();
+			HashSet new_types = new HashSet ();
+			HashSet old_types = new HashSet ();
+			while (s != null) {
+				toke = new StringTokenizer (s, "\t");
+				if (toke.countTokens () != vals.length) {
+					if (s.trim ().length () > 0)
+						System.out.println ("did not process feature: " + s);
+				}
+				else {
+					for (int i = 0; i < vals.length; i++)
+						vals[i] = toke.nextToken ().trim ();
+					int ind = Arrays.binarySearch (contig_list,
+							vals[req[CONTIG]].toLowerCase ());
+					int left = Integer.parseInt (vals[req[LEFT]]);
+					int right = Integer.parseInt (vals[req[RIGHT]]);
+					if (ind > -1) {
+						int shift = (int) ((Chromosome) list.get (ind))
+								.getStart () - 1;
+						left += shift;
+						right += shift;
+					}
+					if (vals[req[STRAND]].toLowerCase ().equals (FORWARD))
+						template.strand = StrandedFeature.POSITIVE;
+					else
+						template.strand = StrandedFeature.NEGATIVE;
+					template.location = new RangeLocation (left, right);
+					template.type = vals[req[TYPE]];
+					if (!types.contains (template.type)) {
+						new_types.add (template.type);
+						addFeatureType (template.type, filterer, genome);
+					}
+					else if (!new_types.contains (template.type))
+						old_types.add (template.type);
+					Hashtable anno = new Hashtable ();
+					anno.put (LABEL_STRING, vals[req[LABEL]]);
+					for (int i = 0; i < fields.length; i++) {
+						if (fields[i] != null)
+							anno.put (fields[i], vals[i]);
+					}
+					template.annotation = new SimpleAnnotation (anno);
+					genome.getAnnotationSequence ().createFeature (template);
+				}
+				s = in.readLine ();
+			}
+			in.close ();
+			System.out.println ("new! " + new_types + "   old " + old_types);
+			//model.fireViewableRangeEvent ();
+			//mauve.getRearrangementPanel ().setVisible (false);
+			addedMoreOfTypes (old_types, filterer, genome);
+			filterer.resetMultiRenderers ();
+			//mauve.getRearrangementPanel ().setVisible (true);//.getNewPanel (genome.getViewIndex ()).feature.updateTransPanel ();
+		} catch (Exception e) {
+			e.printStackTrace ();
+		}
+	}
+
+	protected void addedMoreOfTypes (HashSet types, FeatureFilterer filterer, Genome genome) {
+		Iterator itty = types.iterator ();
+		int seq = genome.getSourceIndex ();
+		while (itty.hasNext ()) {
+			String type = (String) itty.next ();
+			LinkedList vals = (LinkedList) filterer.filter_specs.get (type);
+			Iterator itty2 = vals.iterator ();
+			boolean resize = false;
+			while (itty2.hasNext ()) {
+				Object [] rends = (Object []) itty2.next ();
+/*				
+				MultiGenomeRectangularBeadRenderer rend = ((MultiGenomeRectangularBeadRenderer)
+						((FilterCacheSpec) rends [FILTER_SPEC_INDEX]).getFeatureRenderer ());
+				if (rend.getOffset (genome) == NO_OFFSET) {
+					rend.setOffset (genome, offset [seq]);
+					resize = true;
+				}
+*/
+				OverlayRendererWrapper over = (OverlayRendererWrapper) rends [OVERLAY_REND_INDEX];
+				filterer.addOrRemove (over, false);
+				filterer.addOrRemove (over, true);
+				System.out.println ("there and back again");
+			}
+			if (resize) {
+				offset [seq] += MauveConstants.FEATURE_HEIGHT;
+				mauve.getRearrangementPanel ().getNewPanel (genome.getViewIndex ()).
+						feature.resizeForMoreFeatures ();
+			}
+		}
+	}	
+	
+	protected void addFeatureTypes (HashSet types, FeatureFilterer filterer, Genome genome) {
+		Iterator itty = types.iterator ();
+		while (itty.hasNext ()) {
+			addFeatureType ((String) itty.next (), filterer, genome);
+		}
+	}
+	
+	protected void addFeatureType (String type, FeatureFilterer filterer, Genome genome) {
+		MultiLineRenderer [] multis = new MultiLineRenderer [filterer.multis
+		                                                     .size ()];
+		filterer.multis.toArray (multis);
+		//System.out.println ("another multi " + multis.length);
+		int seq = genome.getSourceIndex ();
+		try {
+			filterer.addCheckBox (type);
+			MultiGenomeRectangularBeadRenderer renderer = new MultiGenomeRectangularBeadRenderer (
+					10.0, 10.0, Color.BLACK, Color.WHITE, new BasicStroke (), model);
+			renderer.setOffset (genome, offset [seq]);
+			renderer.setHeightScaling (false);
+			FilterCacheSpec spec = new FilterCacheSpec (
+					new FeatureFilter.And (new FeatureFilter.ByType (type),
+							new FeatureFilter.StrandFilter (
+									StrandedFeature.NEGATIVE)),
+									new String [] {LABEL_STRING}, renderer);
+			for (int i = 0; i < multis.length; i++) {
+				filterer.addSpecForType (spec);
+				FeaturePanel.makeRenderer (model, multis[i],
+						spec);
+			}
+			renderer = new MultiGenomeRectangularBeadRenderer (10.0, 0, Color.BLACK,
+					Color.WHITE, new BasicStroke (), model);
+			renderer.setOffset (genome, offset [seq]);
+			offset [seq] += MauveConstants.FEATURE_HEIGHT;
+			mauve.getRearrangementPanel ().getNewPanel (genome.getViewIndex ()).
+					feature.resizeForMoreFeatures ();
+			renderer.setHeightScaling (false);
+			spec = new FilterCacheSpec (new FeatureFilter.And (
+					new FeatureFilter.ByType (type),
+					new FeatureFilter.StrandFilter (
+							StrandedFeature.POSITIVE)),
+							new String [] {LABEL_STRING}, renderer);
+			for (int i = 0; i < multis.length; i++) {
+				filterer.addSpecForType (spec);
+				FeaturePanel.makeRenderer (model, multis[i],
+						spec);
+			}
+		} catch (ChangeVetoException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace ();
+		}
+	}
+
+	/**
+	 * called when a user decides to import a annotations from a flat file
+	 */
+	public void actionPerformed (ActionEvent e) {
+		if (e.getActionCommand ().equals (JFileChooser.APPROVE_SELECTION)
+				&& chooser.getSelectedFile () != null) {
+			SwingUtilities.invokeLater (new Runnable () {
+				public void run () {
+					importAnnotationFile (chooser.getSelectedFile (),
+							(Genome) genome_choices.getSelectedItem ());
+				}
+			});
+		}
+		setVisible (false);
+	}
+
+	public int compare (Object arg0, Object arg1) {
+		return ((Chromosome) (arg0)).getName ().toLowerCase ().compareTo (
+				((Chromosome) (arg1)).getName ().toLowerCase ());
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/sequence/HighlightPanel.java b/src/org/gel/mauve/gui/sequence/HighlightPanel.java
new file mode 100644
index 0000000..71e6714
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/HighlightPanel.java
@@ -0,0 +1,99 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.HighlightListener;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.Match;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.XmfaViewerModel;
+
+public class HighlightPanel extends AbstractSequencePanel implements
+		MouseMotionListener, HighlightListener {
+	public HighlightPanel (BaseViewerModel model, Genome genome) {
+		super (model, genome);
+		setOpaque (false);
+
+		if (model instanceof XmfaViewerModel) {
+			((XmfaViewerModel) model).addHighlightListener (this);
+		} else if (model instanceof LcbViewerModel) {
+			((LcbViewerModel) model).addHighlightListener (this);
+		}
+	}
+
+	public void highlightChanged (ModelEvent evt) {
+		repaint ();
+	}
+
+	public void mouseDragged (MouseEvent e) {
+	}
+
+	public void mouseMoved (MouseEvent e) {
+		int box_height = (int) (AbstractSequencePanel.BOX_FILL * getHeight ());
+		int box_top = (getHeight () - box_height) / 2;
+		if (e.getY () >= box_top && e.getY () <= box_top + box_height) {
+			if (model instanceof XmfaViewerModel) {
+				long seqX = pixelToCenterSequenceCoordinate (e.getX ());
+				if (seqX >= 0 && seqX < getGenome ().getLength ()) {
+					model.updateHighlight (getGenome (), seqX);
+				}
+			} else if (model instanceof LcbViewerModel) {
+				long seqX_left = pixelToLeftSequenceCoordinate (e.getX ());
+				long seqX_right = pixelToRightSequenceCoordinate (e.getX () + 1);
+				if (seqX_left >= 0 && seqX_right < getGenome ().getLength ()) {
+					((LcbViewerModel) model).updateHighlight (getGenome (),
+							seqX_left, seqX_right);
+				}
+			}
+		}
+	}
+
+	protected static Color cursor_color = new Color (0, 0, 0);
+
+	protected int half = 7;
+
+	protected int full = 14;
+
+	/**
+	 * paint the sequence display. copies a pre-computed similarity display and
+	 * adds any highlighting
+	 */
+	public void paintComponent (Graphics graphics) {
+		long highlightCoord = Match.NO_MATCH;
+		if(!model.getDrawMouseHighlighting())
+			return;
+		// get the highlighted coordinate in this genome
+		if (model instanceof XmfaViewerModel)
+			highlightCoord = ((XmfaViewerModel) model)
+					.getHighlight (getGenome ());
+		else if (model instanceof LcbViewerModel) {
+			long highlightArray[] = ((LcbViewerModel) model)
+					.getHighlightArray (getGenome ());
+			highlightCoord = highlightArray[0];
+		} else
+			return;
+
+		if (highlightCoord != Match.NO_MATCH) {
+			Graphics2D g = (Graphics2D) graphics;
+			long absHighlightCoord = Math.abs (highlightCoord);
+			if (absHighlightCoord >= getGenome ().getViewStart ()
+					&& absHighlightCoord <= getGenome ().getViewStart ()
+							+ getGenome ().getViewLength ()) {
+				int pixel = sequenceCoordinateToCenterPixel (absHighlightCoord);
+				g.setColor (cursor_color);
+
+				g.drawLine (pixel, 0, pixel, getHeight ());
+				if (highlightCoord > 0) {
+					g.drawRect (pixel - half, 0, full, getHeight () - 1);
+				}
+
+			}
+		}
+	}
+}
diff --git a/src/org/gel/mauve/gui/sequence/HistogramPanel.java b/src/org/gel/mauve/gui/sequence/HistogramPanel.java
new file mode 100644
index 0000000..66d9094
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/HistogramPanel.java
@@ -0,0 +1,68 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Stroke;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.gui.MauveRenderingHints;
+import org.gel.mauve.histogram.ZoomHistogram;
+
+public class HistogramPanel extends AbstractSequencePanel {
+	ZoomHistogram hist;
+	BasicStroke meanStroke = new BasicStroke(2);
+
+	HistogramPanel(BaseViewerModel model, Genome genome, ZoomHistogram zh){
+		super(model, genome);
+		hist = zh;
+		setOpaque (false);
+		setDoubleBuffered(true);
+	};	
+	
+	/**
+	 * paint a pre-computed histogram
+	 */
+	public void paintComponent (Graphics graphics) {
+		if(!model.getDrawAttributes()) return;
+		Graphics2D g2 = (Graphics2D)graphics;
+        Double density = (Double) g2.getRenderingHint(MauveRenderingHints.KEY_SIMILARITY_DENSITY);
+        double increment = density == null ? 1.0 : density.doubleValue();
+        double sim_height = this.getHeight();
+        double prevh = sim_height;
+        double half_inc = increment/2.0;
+        Stroke curStroke = g2.getStroke();
+        g2.setStroke(meanStroke);
+        for (double pixelD = 0; pixelD < getWidth(); pixelD += increment)
+        {
+            long seq_left = pixelToCenterSequenceCoordinate(pixelD);
+            long seq_right = pixelToCenterSequenceCoordinate(pixelD + increment);
+            seq_left = seq_left < 0 ? 0 : seq_left;	// clamp to valid range
+            seq_right = seq_right < getGenome().getLength() ? seq_right : getGenome().getLength();	// clamp to valid range
+            if(seq_right <= 0)	continue;
+            double s = hist.getValueForRange(seq_left, seq_right);
+            double smin = hist.getValueForRange(seq_left, seq_right,-1);
+            double smax = hist.getValueForRange(seq_left, seq_right,1);
+            // normalize to a box_height
+            double height = sim_height - (((double) s + 127d) / 256d * sim_height);
+            double heightmin = sim_height - (((double) smin + 127d) / 256d * sim_height);
+            double heightmax = sim_height - (((double) smax + 127d) / 256d * sim_height);
+
+            double x1=pixelD-half_inc;
+            double x2=pixelD+half_inc;
+            Rectangle2D.Double r2d = new Rectangle2D.Double(x1, heightmax, x2-x1, heightmin-heightmax);
+            g2.setColor(Color.yellow);
+            g2.fill(r2d);
+            g2.setColor(Color.black);
+            Line2D.Double l2d = new Line2D.Double(x1, prevh, x2, height);
+            g2.draw(l2d);
+            prevh = height;
+        }
+        g2.setStroke(curStroke);
+	}
+}
diff --git a/src/org/gel/mauve/gui/sequence/MatchPanel.java b/src/org/gel/mauve/gui/sequence/MatchPanel.java
new file mode 100644
index 0000000..5b0a1a3
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/MatchPanel.java
@@ -0,0 +1,1031 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.MenuItem;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.geom.Rectangle2D;
+import java.util.EventListener;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.event.EventListenerList;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.HighlightListener;
+import org.gel.mauve.LCB;
+import org.gel.mauve.LcbViewerModel;
+import org.gel.mauve.Match;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.backbone.Backbone;
+import org.gel.mauve.backbone.BackboneList;
+import org.gel.mauve.gui.MauveRenderingHints;
+import org.gel.mauve.gui.RearrangementPanel;
+
+public class MatchPanel extends AbstractSequencePanel implements MouseListener, HighlightListener
+{
+    // The largest number of matches which will be shown in a popup menu
+    private static final Color DELETED_COLOR = Color.getHSBColor(0.11f, 1, 1);
+    private static final int MAX_POPUP_MATCHES = 15;
+    // Color for the area on top and bottom around matches.
+    private static final Color highlightAreaColor = new Color(24, 24, 24);
+    
+    // Size of region on top/bottom.
+    private static final int HIGHLIGHT_AREA_HEIGHT = 2;
+
+    // Thickness of LCB border.
+    private static final double LCB_BOUNDARY_WIDTH = 2.25d;
+
+    // Half of thickness of LCB border.
+    private static final double HALF_PEN_WIDTH = 1.125d;
+    
+    // The last visible coordinate (a convenience)
+    private long viewEnd;
+
+    // The buffered image.
+    private Image bufferedImage;
+
+    // Last width and height when bufferedImage was updated.
+    private int lastWidth = -1;
+    private int lastHeight = -1;
+    
+    // The rearrangement panel that contains this sequence panel
+    RearrangementPanel rrpanel;
+    
+    private int depth = 0;
+
+    private final MatchPopupMenuBuilder mpmb = new MatchPopupMenuBuilder();
+    private final MatchDisplayMenuItemBuilder mdmib = new MatchDisplayMenuItemBuilder();
+    private final EditLcbMenuItemBuilder elmib = new EditLcbMenuItemBuilder();
+    private final SetReferenceMenuItemBuilder srmib = new SetReferenceMenuItemBuilder();
+    
+    public MatchPanel(RearrangementPanel rrpanel, BaseViewerModel model, Genome genome)
+    {
+        super(model, genome);
+        this.rrpanel = rrpanel;
+        viewEnd = getGenome().getViewStart() + getGenome().getViewLength() - 1;
+        if (! (model instanceof XmfaViewerModel))
+        {
+            model.addHighlightListener(this);            
+        }
+        mpmb.addMenuItemBuilder(mdmib);
+        mpmb.addMenuItemBuilder(elmib);
+        mpmb.addMenuItemBuilder(srmib);
+    }
+
+    public MatchPopupMenuBuilder getMatchPopupMenuBuilder(){ return mpmb; }
+    public MatchDisplayMenuItemBuilder getMatchDisplayMenuItemBuilder(){ return mdmib; }
+    public EditLcbMenuItemBuilder getEditLcbMenuItemBuilder(){ return elmib; }
+    public SetReferenceMenuItemBuilder getSetReferenceMenuItemBuilder(){ return srmib; }
+
+    // extra methods which aren't implemented
+    /** not implemented */
+    public void mouseEntered(MouseEvent e)
+    {
+    }
+
+    /** not implemented */
+    public void mouseExited(MouseEvent e)
+    {
+    }
+
+    /** not implemented */
+    public void mousePressed(MouseEvent e)
+    {
+    }
+
+    /** not implemented */
+    public void mouseReleased(MouseEvent e)
+    {
+    }
+
+    public void mouseClicked(MouseEvent e)
+    {
+    	model.highlightRange (null, 0, 0);
+        if (e.isPopupTrigger())
+        {
+            mpmb.build(e,rrpanel,model,getGenome()).show(this, e.getX(), e.getY());
+        }
+
+        if (e.getClickCount() == 1)
+        {
+            // if it is a control click then show the popup menu
+            if (e.isControlDown() || ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0))
+            {
+                mpmb.build(e,rrpanel,model,getGenome()).show(this, e.getX(), e.getY());
+            }
+            else
+            {
+
+                // first clear any previous highlights if shift isn't down
+                if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0)
+                {
+                    model.clearMatchHighlights();
+                }
+
+                if (model instanceof XmfaViewerModel)
+                {
+                    XmfaViewerModel xm = (XmfaViewerModel) model;
+                    if (xm.getSim(getGenome()) != null)
+                    {
+                        long seqX = pixelToCenterSequenceCoordinate(e.getX());
+                        xm.alignView(getGenome(), seqX);
+                    }
+                }
+                else
+                if (model instanceof LcbViewerModel)
+                {
+                	LcbViewerModel lm = (LcbViewerModel)model;
+                    long seqX_left = pixelToLeftSequenceCoordinate(e.getX());
+                    long seqX_right = pixelToRightSequenceCoordinate(e.getX() + 1);
+                    lm.alignView(getGenome(), seqX_left, seqX_right);
+                }
+                else
+                {
+                    Vector sortedMatches = getGenome().getSortedMatches();
+                    
+                    int[] match_range = new int[2];
+                    this.getMatchPixelRange(e.getX(), e.getX(), match_range);
+                    // highlight this and the other components of the match
+                    for (int matchI = match_range[0]; matchI <= match_range[1]; matchI++)
+                    {
+                        if (matchI >= sortedMatches.size())
+                        {
+                            break;
+                        }
+                        model.addMatchHighlight((Match) sortedMatches.get(matchI));
+                    }
+                }
+            }
+        }
+    }
+    
+    /**
+     * centers view on selected coordinate of the genome associated with
+     * this MatchPanel and aligns other genomes
+     * 
+     * @param coordinate  The position to go to
+     * @return
+     */
+    public int goTo (long coordinate) {
+    	model.zoomAndCenter(getGenome (), 100, coordinate);
+    	if (model instanceof LcbViewerModel) {
+    		((LcbViewerModel) model).alignView(getGenome (), coordinate);
+    	}
+    	else
+    		System.out.println ("BaseViewerModel -- not moving to align");
+    	return 1;
+    }
+    
+    /**
+     * isForGenome returns true if this RRSequencePanel is associated
+     * with the specified genome, and false otherwise
+     * 
+     * @param comparator - the genome in question
+     */
+    public boolean isForGenome (Genome comparator) {
+    	return getGenome ().equals (comparator);
+    }
+
+	public interface MatchPopupMenuItemBuilder extends EventListener
+	{
+		public JMenuItem[] getItem(MouseEvent evt, final RearrangementPanel rrpanel, final BaseViewerModel model, final Genome g);
+	};
+
+	private class SetReferenceMenuItemBuilder implements MatchPopupMenuItemBuilder
+	{
+		public JMenuItem[] getItem(MouseEvent evt, final RearrangementPanel rrpanel, final BaseViewerModel model, final Genome g)
+		{
+        	JMenuItem setReferenceItem = new JMenuItem("Set reference genome");
+        	setReferenceItem.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e)
+                {
+                    model.setReference(g);
+                }});
+        	return new JMenuItem[]{setReferenceItem};
+		}
+	}
+
+	private class EditLcbMenuItemBuilder implements MatchPopupMenuItemBuilder
+	{
+		public JMenuItem[] getItem(MouseEvent evt, final RearrangementPanel rrpanel, final BaseViewerModel model, final Genome g)
+		{
+	        return new JMenuItem[0];
+		}
+	}
+	
+	private class MatchDisplayMenuItemBuilder implements MatchPopupMenuItemBuilder 
+	{
+		private void addItem(final Match m, Vector items, final BaseViewerModel model, final Genome g)
+		{
+            JMenuItem item = new JMenuItem("Align display to " + m.toString());
+            item.addActionListener(new ActionListener(){
+                public void actionPerformed(ActionEvent e)
+                {
+                    model.alignView(m, g);
+                }});			
+		}
+		public JMenuItem[] getItem(MouseEvent evt, final RearrangementPanel rrpanel, final BaseViewerModel model, final Genome g)
+		{
+			JMenuItem[] itemArray = new JMenuItem[0];
+	        if( !(model instanceof LcbViewerModel) && !(model instanceof XmfaViewerModel))
+	        {
+	        	Vector items = new Vector();
+	            int[] match_range = new int[2];
+	            getMatchPixelRange(evt.getX(), evt.getX(), match_range);
+	            
+	            Vector sortedMatches = getGenome().getSortedMatches();
+		        if (match_range[1] - match_range[0] < MAX_POPUP_MATCHES)
+		        {
+		            for (int matchI = match_range[0]; matchI < match_range[1]; matchI++)
+		            	addItem((Match) sortedMatches.get(matchI), items, model, g);
+		        }
+		        else
+		        {
+		            double pop_interval = (double) (match_range[1] - match_range[0]) / (double) MAX_POPUP_MATCHES;
+		            double popI = match_range[0];
+		            for (int matchI = match_range[0]; matchI < match_range[1]; matchI++)
+		            {
+		                if ((int) popI == matchI)
+			            	addItem((Match) sortedMatches.get(matchI), items, model, g);
+		                popI += pop_interval;
+		            }
+		        }
+		        itemArray = new JMenuItem[items.size()];
+		        items.toArray(itemArray);
+	        }
+	        return itemArray;
+		}
+	}
+
+	public class MatchPopupMenuBuilder
+	{
+	    protected EventListenerList builders = new EventListenerList();
+		public void addMenuItemBuilder(MatchPopupMenuItemBuilder fmib)
+		{
+			builders.add(MatchPopupMenuItemBuilder.class, fmib);
+		}
+		public JPopupMenu build(MouseEvent evt, final RearrangementPanel rrpanel, final BaseViewerModel model, final Genome g)
+		{
+			Object[] listeners = builders.getListenerList();
+        	JPopupMenu leMenu = new JPopupMenu();
+        	for (int i = listeners.length-2; i>=0; i-=2) {
+                if (listeners[i]==MatchPopupMenuItemBuilder.class) {
+                	JMenuItem[] items = ((MatchPopupMenuItemBuilder)listeners[i+1]).getItem(evt, rrpanel, model, g);
+            		for(int j = 0; j < items.length; j++)
+            			leMenu.add(items[j]);
+                }
+            }
+			return leMenu;
+		}
+		public void removeMenuItemBuilder(MatchPopupMenuItemBuilder mpmib)
+		{
+			builders.remove(MatchPopupMenuItemBuilder.class, mpmib);
+		}
+	}
+
+	MatchPopupMenu getPopup(MouseEvent evt)
+    {
+        MatchPopupMenu pop_menu = new MatchPopupMenu(rrpanel, model, getGenome());
+
+        int[] match_range = new int[2];
+        getMatchPixelRange(evt.getX(), evt.getX(), match_range);
+        
+        Vector sortedMatches = getGenome().getSortedMatches();
+        
+        MenuItem setReference = new MenuItem("Set reference genome");
+        setReference.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e)
+            {
+                model.setReference(getGenome());
+            }});
+        pop_menu.add(setReference);
+        
+        if( !(model instanceof LcbViewerModel))
+        {
+	        if (match_range[1] - match_range[0] < MAX_POPUP_MATCHES)
+	        {
+	            for (int matchI = match_range[0]; matchI < match_range[1]; matchI++)
+	                pop_menu.addMatch((Match) sortedMatches.get(matchI));
+	        }
+	        else
+	        {
+	            double pop_interval = (double) (match_range[1] - match_range[0]) / (double) MAX_POPUP_MATCHES;
+	            double popI = match_range[0];
+	            for (int matchI = match_range[0]; matchI < match_range[1]; matchI++)
+	            {
+	                if ((int) popI == matchI)
+	                    pop_menu.addMatch((Match) sortedMatches.get(matchI));
+	                popI += pop_interval;
+	            }
+	        }
+        }
+
+        MenuItem features = new MenuItem ("Filter Features");
+        features.addActionListener (FeatureFilterer.getFilterer (model));
+        pop_menu.add (features);
+        
+        return pop_menu;
+    }
+
+    /**
+     * Finds all match indices which intersect with the specified range of
+     * pixels in the current view.
+     * 
+     * @param start_pixel
+     *            The first coordinate of the intersection range
+     * @param end_pixel
+     *            The last coordinate of the intersection range
+     * @param match_range
+     *            An int array with 2 elements. The resulting range of
+     *            intersecting match indices will be returned as the first and
+     *            second elements in the array.
+     */
+    protected void getMatchPixelRange(int start_pixel, int end_pixel, int[] match_range)
+    {
+        long[] seq_range = new long[2];
+        pixelRangeToSequenceCoordinates(start_pixel, end_pixel, seq_range);
+        model.getMatchRange(getGenome(), seq_range[0], seq_range[1], match_range);
+    }
+
+    /**
+     * paint the sequence display. copies a pre-computed similarity display and
+     * adds any highlighting
+     */
+    public void paintComponent(Graphics graphics)
+    {
+        Graphics2D g2 = (Graphics2D) graphics;
+        
+        // Formatting and printing bypass buffering.
+        Double density = (Double) g2.getRenderingHint(MauveRenderingHints.KEY_SIMILARITY_DENSITY);
+        if (density != null)
+        {
+            formatBoxes((Graphics2D) graphics, density.doubleValue());
+        }
+        else // Normal operations.
+        {
+            if ( bufferedImage == null || lastWidth != getWidth() || lastHeight != getHeight())
+            {
+                updateBuffer();
+            }
+            graphics.drawImage(bufferedImage, 0, 0, getWidth(), getHeight(), 0, 0, bufferedImage.getWidth(null), bufferedImage.getHeight(null), null);
+        }
+    }
+
+    /**
+     * 
+     */
+    private void updateBuffer()
+    {
+        bufferedImage = createImage(getWidth(), getHeight());
+        Graphics g = bufferedImage.getGraphics();
+        formatBoxes((Graphics2D) g, 1.0);
+        lastWidth = getWidth();
+        lastHeight = getHeight();
+    }
+
+    public void markDirty()
+    {
+        bufferedImage = null;
+    }
+    
+
+    /**
+     * Organize the matches into visual boxes to be displayed on screen.
+     * formatBoxes will coalesce small matches into a single box to be
+     * displayed.
+     *
+     * Draws the sequence similarity information, either based on Match objects
+     * (ungapped local alignments) or based on a SimilarityIndex
+     */
+    private void formatBoxes(Graphics2D g2, double similarityIncrement)
+    {
+        double unitsPerPixel = (double) getGenome().getViewLength() / (double) getWidth();
+        int box_height = (int) (AbstractSequencePanel.BOX_FILL * getHeight());
+        int half_height = box_height / 2;
+        double arc_size = (double) half_height / 6d;
+        
+        // be sure the background color matches
+        g2.setColor(getBackground());
+        g2.fillRect(0, 0, getWidth(), getHeight());
+        
+        AffineTransform oldTransform = g2.getTransform();
+        
+        g2.translate(0, boxTop() + 1);
+        
+        if (model instanceof XmfaViewerModel)
+        {
+            drawXmfa(half_height, g2, similarityIncrement);
+        }
+        else if (model instanceof LcbViewerModel)
+        {
+            LcbViewerModel lm = (LcbViewerModel) model;
+            
+            // draw white backgrounds for LCB rectangles
+            if (lm.getDrawLcbBounds())
+            {
+                drawWhiteBackgrounds(half_height, g2, arc_size);
+            }
+            
+            if (lm.getDrawMatches())
+            {
+                drawMatchBoxes(unitsPerPixel, box_height, half_height, g2);
+            }
+
+            drawLcbBounds(unitsPerPixel, box_height, half_height, g2, arc_size);
+        }
+        else
+        {
+            if (model.getDrawMatches())
+            {
+                drawMatchBoxes(unitsPerPixel, box_height, half_height, g2);
+            }
+        }
+
+
+        drawChromosomeBoundaries(g2);
+        
+        // draw the center line
+        
+        g2.setTransform(oldTransform);
+        g2.setStroke(new BasicStroke(0f));
+        g2.setColor(Color.black);
+        g2.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
+    }
+
+    private void drawLcbBounds(double unitsPerPixel, int box_height, int half_height, Graphics2D matchGraphics, double arc_size)
+    {
+        LcbViewerModel lm = (LcbViewerModel) model;
+        
+        int lcbI;
+        // set the pen width
+        matchGraphics.setStroke(new BasicStroke((float) LCB_BOUNDARY_WIDTH));
+        for (lcbI = 0; lcbI < lm.getVisibleLcbCount(); lcbI++)
+        {
+            LCB lcb = lm.getVisibleLcb(lcbI);
+            if (lcb.getRightEnd(getGenome()) < getGenome().getViewStart() - 1)
+                continue;
+            if (lcb.getLeftEnd(getGenome()) > viewEnd + 1)
+                continue;
+
+            int leftPixel = sequenceCoordinateToLeftPixel(lcb.getLeftEnd(getGenome()));
+            int rightPixel = sequenceCoordinateToRightPixel(lcb.getRightEnd(getGenome()));
+            int pixelWidth = rightPixel - leftPixel;
+
+            int lcb_top = lcb.getReverse(getGenome()) ? half_height : 0;
+
+            if (lm.getVisibleLcb(lcbI).color != null)
+                matchGraphics.setColor(lm.getVisibleLcb(lcbI).color);
+
+            java.awt.geom.RoundRectangle2D.Double lcb_rect = new java.awt.geom.RoundRectangle2D.Double(leftPixel + HALF_PEN_WIDTH, lcb_top + HALF_PEN_WIDTH, pixelWidth - LCB_BOUNDARY_WIDTH, half_height - LCB_BOUNDARY_WIDTH, arc_size, arc_size);
+
+            if (lm.getDrawLcbBounds())
+            {
+            	openJdkSafeRect(lcb_rect);
+                System.out.println("Drawing bananas " + lcb_rect.getBounds2D().toString());
+            	matchGraphics.draw(lcb_rect);
+            }
+            
+            if (lm.getFillLcbBoxes())
+            {
+                matchGraphics.fill(lcb_rect);
+            }
+        }
+
+        // now draw deleted LCBs in obnoxious yellow-orange
+        matchGraphics.setColor(Color.getHSBColor(0.11f, 1, 1));
+        matchGraphics.setStroke(new BasicStroke((float) LCB_BOUNDARY_WIDTH));
+        for (lcbI = 0; lcbI < ((LcbViewerModel) model).getDelLcbList().length; lcbI++)
+        {
+            LCB delcb = ((LcbViewerModel) model).getDelLcbList()[lcbI];
+            if (delcb.getRightEnd(getGenome()) < getGenome().getViewStart() - 1)
+                continue;
+            if (delcb.getLeftEnd(getGenome()) > viewEnd + 1)
+                continue;
+
+            int cur_start_pixel = (int) ((double) (delcb.getLeftEnd(getGenome()) - getGenome().getViewStart()) / unitsPerPixel);
+            double lcb_length = delcb.getRightEnd(getGenome()) - delcb.getLeftEnd(getGenome());
+            int cur_pixel_width = (int) Math.ceil(lcb_length / unitsPerPixel);
+
+            java.awt.geom.RoundRectangle2D.Double lcb_rect = new java.awt.geom.RoundRectangle2D.Double(cur_start_pixel + HALF_PEN_WIDTH, HALF_PEN_WIDTH, cur_pixel_width - LCB_BOUNDARY_WIDTH, box_height - LCB_BOUNDARY_WIDTH, arc_size, arc_size);
+
+            matchGraphics.fill(lcb_rect);
+        }
+    }
+    
+    
+    /**
+     * @param g
+     */
+    private void drawChromosomeBoundaries(Graphics2D g)
+    {
+    	if(!model.getDrawChromosomeBoundaries())
+    		return;	// don't draw if we're not supposed to draw!
+        g.setStroke(new BasicStroke(0f));
+        g.setColor(Color.red);
+        Iterator i = getGenome().getChromosomes().iterator();
+
+        while (i.hasNext())
+        {
+            Chromosome c = (Chromosome) i.next();
+
+            if (c.getStart() >= getGenome().getViewStart() && c.getStart() <= getGenome().getViewStart() + getGenome().getViewLength())
+            {
+                int high_pixel = sequenceCoordinateToCenterPixel(c.getStart());
+                g.drawLine(high_pixel, 0, high_pixel, getHeight());
+            }
+
+            // Draw the last end line, too.
+            if (!i.hasNext())
+            {
+                if (c.getEnd() >= getGenome().getViewStart() && c.getEnd() <= getGenome().getViewStart() + getGenome().getViewLength())
+                {
+                    int high_pixel = sequenceCoordinateToCenterPixel(c.getEnd());
+                    g.drawLine(high_pixel, 0, high_pixel, getHeight());
+                }
+            }
+        }
+    }
+
+    /**
+     * @param lcb
+     * @param half_height
+     * @param arc_size
+     * @return
+     */
+    private RoundRectangle2D getLcbRectangle(LCB lcb, int half_height)
+    {
+        double arc_size = (double) half_height / 6d;
+        int leftPixel = sequenceCoordinateToLeftPixel(lcb.getLeftEnd(getGenome()));
+        int rightPixel = sequenceCoordinateToRightPixel(lcb.getRightEnd(getGenome()));
+        int pixelWidth = rightPixel - leftPixel;
+        int lcb_top = lcb.getReverse(getGenome()) ? half_height : 0;
+        RoundRectangle2D.Double lcb_rect = new RoundRectangle2D.Double(leftPixel + HALF_PEN_WIDTH, lcb_top + HALF_PEN_WIDTH, pixelWidth - LCB_BOUNDARY_WIDTH, half_height - LCB_BOUNDARY_WIDTH, arc_size, arc_size);
+    	openJdkSafeRect(lcb_rect);
+        return lcb_rect;
+    }
+
+    /**
+     * @param end_view
+     * @param unitsPerPixel
+     * @param box_height
+     * @param half_height
+     * @param matchGraphics
+     * @param highlightAreaGraphics
+     * @param half_pen_width
+     */
+    private void drawMatchBoxes(double unitsPerPixel, int box_height, int half_height, Graphics2D g2)
+    {
+        // draw boxes for matches instead of similarity values
+
+        int[] match_range = new int[2];
+        model.getMatchRange(getGenome(), getGenome().getViewStart(), viewEnd, match_range);
+        int first_match = match_range[0];
+        long last_match = match_range[1];
+
+        for (int directionI = 0; directionI < 2; directionI++)
+        {
+            boolean reverse_matches = false;
+            if (directionI == 1)
+                reverse_matches = true;
+            int prev_start_pixel = -1;
+            int prev_pixel_width = 0;
+            int cur_start_pixel = 0;
+            int cur_pixel_width = 0;
+            int end_pixel = 0;
+
+            double match_top = reverse_matches ? half_height + HALF_PEN_WIDTH : HALF_PEN_WIDTH;
+
+            Color prev_color = null;
+            boolean prev_reverse = false;
+            Vector sortedMatches = getGenome().getSortedMatches();
+            for (int matchI = first_match; matchI < last_match; matchI++)
+            {
+                Match cur_match = (Match) sortedMatches.get(matchI);
+                // don't draw the match if it's not on the current strand
+                // or if it's part of a 'disabled' LCB.
+                boolean rev = false;
+                if(model instanceof LcbViewerModel && cur_match.lcb >= 0)
+                	rev = ((LcbViewerModel)model).getFullLcbList()[cur_match.lcb].getReverse(getGenome());
+                else
+                	rev = cur_match.getReverse(getGenome());
+                if ( rev == reverse_matches && cur_match.lcb >= 0)
+                {
+                    cur_start_pixel = (int) ((double) (cur_match.getStart(getGenome()) - getGenome().getViewStart()) / unitsPerPixel);
+                    cur_pixel_width = (int) Math.ceil((double) cur_match.getLength(getGenome()) / unitsPerPixel);
+                    if (cur_match.highlighted)
+                    {
+                        Color oldColor = g2.getColor();
+                        g2.setColor(Color.BLACK);
+                        if (reverse_matches)
+                        {
+                            g2.fillRect(cur_start_pixel, getHeight()/2, cur_pixel_width, getHeight()/2);
+                        }
+                        else
+                        {
+                            g2.fillRect(cur_start_pixel, 0, cur_pixel_width, getHeight()/2);
+                        }
+                        g2.setColor(oldColor);
+                    }
+
+                    if (cur_start_pixel > prev_start_pixel)
+                    {
+                        end_pixel = prev_start_pixel + prev_pixel_width - 1;
+                        end_pixel = cur_start_pixel < end_pixel ? cur_start_pixel : end_pixel;
+                        g2.setColor(prev_color);
+                        java.awt.geom.Rectangle2D.Double match_rect = new java.awt.geom.Rectangle2D.Double(prev_start_pixel, match_top, end_pixel - prev_start_pixel + 1, half_height - LCB_BOUNDARY_WIDTH);
+                        g2.fill(match_rect);
+                    }
+
+                    prev_start_pixel = cur_start_pixel;
+                    prev_pixel_width = cur_pixel_width;
+                    prev_reverse = rev;
+                    prev_color = ((Match) sortedMatches.get(matchI)).color;
+                }
+            }
+
+            // draw the last match.
+            g2.setColor(prev_color);
+            if (prev_reverse)
+            {
+                g2.fillRect(prev_start_pixel, half_height, prev_pixel_width, box_height);
+            }
+            else
+            {
+                g2.fillRect(prev_start_pixel, 0, prev_pixel_width, half_height);
+            }
+        } // for directionI
+
+    }
+    /**
+     * Creates a similarity plot fill color for an LCB of a given color
+     * @param lcb_color The color of the LCB bounding rectangle
+     * @return
+     */
+    private static Color getFillColor( Color lcb_color )
+    {
+        return getFillColorChangeHSB(lcb_color, 0f,-0.2f,0.2f);
+    }
+
+    /**
+     * Creates a similarity plot fill color for an LCB of a given color
+     * @param lcb_color The color of the LCB bounding rectangle
+     * @param brighter A floating point value in range [-1,1] by which the brightness will be adjusted
+     * @return
+     */
+    private static Color getFillColorChangeHSB( Color lcb_color, float h, float s, float b )
+    {
+    	float[] hsbvals = Color.RGBtoHSB(lcb_color.getRed(), lcb_color.getGreen(), lcb_color.getBlue(), null);
+    	hsbvals[0] = hsbvals[0] + h > 0 ? hsbvals[0] : 0;
+    	hsbvals[1] = hsbvals[1] + s > 0 ? hsbvals[1] + s : 0;
+    	hsbvals[2] = hsbvals[2] + b < 1 ? hsbvals[2] + b : 1;
+    	return Color.getHSBColor( hsbvals[0], hsbvals[1], hsbvals[2]);
+    }
+
+    /**
+     * @param half_height
+     * @param g
+     * @param half_pen_width
+     */
+    private void drawXmfa(int half_height, Graphics2D g, double increment)
+    {
+        XmfaViewerModel xm = ((XmfaViewerModel) model);
+        g.setStroke(new BasicStroke((float) LCB_BOUNDARY_WIDTH));
+        double sim_height = half_height - LCB_BOUNDARY_WIDTH;
+        Color sim_color = Color.GRAY;
+        
+        BackboneList bb_list = xm.getBackboneList();
+        
+        // Get the first LCB, and start drawing it.
+        LCB lcb = xm.getLeftmostVisibleLCB(getGenome());
+        while(lcb != null && lcb.multiplicity() == 1)
+            lcb = xm.getVisibleRightNeighbor(lcb, getGenome(), 0);
+        	
+        RoundRectangle2D r = null;
+        if (lcb != null)
+        {
+            r =  getLcbRectangle(lcb, half_height);
+            // Draw profile data.
+            if (r.getWidth() >= 1)
+            {
+                if (xm.getDrawLcbBounds())
+                {
+                    // Draw white background.
+                    g.setColor(Color.WHITE);
+                    g.fill(r);
+                }
+            }
+            if( bb_list == null )
+            	sim_color = getFillColor(lcb.color);
+        }
+        
+        LCB deletedLCB = xm.getLeftmostDeletedLCB(getGenome());
+        if (deletedLCB != null)
+        {
+	        if (deletedLCB.getRightEnd(getGenome()) > getGenome().getViewStart())
+	        {
+	            if (lcb.getLeftEnd(getGenome()) < viewEnd)
+	            {
+	                RoundRectangle2D delRec = this.getLcbRectangle(deletedLCB, half_height);
+	                g.setColor(DELETED_COLOR);
+	                g.fill(delRec);
+	            }
+	        }
+        }
+        
+    	// if we have backbone information then find out whether we're
+    	// inside a backbone segment.  if we're not in a bb segment,
+    	// find the next segment since querying repeatedly would be too expensive
+        Backbone bb = null;
+        Backbone next_bb = null;
+        if( bb_list != null )
+        {
+        	long pos = pixelToCenterSequenceCoordinate(0);
+        	bb = bb_list.getBackbone(getGenome(), pos);
+        	if( bb == null )
+        	{
+        		next_bb = bb_list.getNextBackbone(getGenome(), pos);
+        		if( next_bb != null && pixelToCenterSequenceCoordinate(increment) >= next_bb.getLeftEnd(getGenome()) )
+        		{
+        			bb = next_bb;
+        			sim_color = getFillColor(bb.getColor());
+        		}else	// last resort: if we're completely outside backbone set the color to gray
+        			sim_color = getFillColor(Color.GRAY);
+        	}else
+            	sim_color = getFillColor(bb.getColor());
+        }
+        
+        double prevh = half_height*2;
+        for (double pixelD = 0; pixelD < getWidth(); pixelD += increment)
+        {
+            // Determine the range of sequence coordinates with which we are working.
+            long seq_left = pixelToCenterSequenceCoordinate(pixelD);
+            long seq_right = pixelToCenterSequenceCoordinate(pixelD + increment);
+            // Clamp the range.
+            seq_left = seq_left < 0 ? 0 : seq_left;
+            seq_right = seq_right < getGenome().getLength() ? seq_right : getGenome().getLength();
+
+            // Don't draw anything if we're out of range.
+            if (seq_left > getGenome().getLength() || seq_right < 0)
+            {
+                continue;
+            }
+            
+            // If we've moved past end of lcb, find next one.
+            if (lcb != null && seq_left > lcb.getRightEnd(getGenome()))
+            {	
+                // Finish previous rectangle.
+                if (xm.getFillLcbBoxes())
+                {
+                    g.setColor(lcb.color);
+                	openJdkSafeRect(r);
+                    g.fill(r);
+                }
+                else if (xm.getDrawLcbBounds())
+                {
+                    g.setColor(lcb.color);
+                	openJdkSafeRect(r);
+                    g.draw(r);
+                }
+                
+                // Get next LCB, and start drawing it.
+                lcb = xm.getVisibleRightNeighbor(lcb, getGenome(), seq_left);
+                while(lcb != null && lcb.multiplicity() == 1)
+                    lcb = xm.getVisibleRightNeighbor(lcb, getGenome(), seq_left);
+                if (lcb != null)
+                {
+                    r =  getLcbRectangle(lcb, half_height);
+                    // Draw profile data.
+                    if (r.getWidth() >= 1)
+                    {
+                        if (xm.getDrawLcbBounds())
+                        {
+                            // Draw white background.
+                            g.setColor(Color.WHITE);
+                            g.fill(r);
+                        }
+                    }
+                    if(bb_list == null)
+                    	sim_color = getFillColor(lcb.color);
+               }
+            }
+            
+            // scan up to the next deleted LCB
+            if (deletedLCB != null && seq_left > deletedLCB.getRightEnd(getGenome()))
+            {
+                deletedLCB = xm.getDeletedRightNeighbor(deletedLCB, getGenome(), seq_left);
+
+                if (deletedLCB != null && deletedLCB.getRightEnd(getGenome()) > getGenome().getViewStart())
+    	        {
+    	            if (deletedLCB.getLeftEnd(getGenome()) < viewEnd)
+    	            {
+    	                RoundRectangle2D delRec = this.getLcbRectangle(deletedLCB, half_height);
+    	                g.setColor(DELETED_COLOR);
+                    	openJdkSafeRect(r);
+    	                g.fill(delRec);
+    	            }
+    	        }
+            }
+ 
+    		// check whether we're still in range of the backbone
+        	// update the current bb segment as necessary
+            if( bb_list != null )
+            {
+	            if( (bb != null && seq_left > bb.getRightEnd(getGenome())) ||
+	            	(bb == null && next_bb != null && 
+		                next_bb.getLeftEnd(getGenome()) <= seq_left ))
+	            {
+	            	bb = bb_list.getBackbone(getGenome(), seq_left);
+	            	if( bb == null )
+	            	{
+	            		next_bb = bb_list.getNextBackbone(getGenome(), seq_left);
+	            		if( next_bb != null && seq_right >= next_bb.getLeftEnd(getGenome()) )
+	            		{
+	            			bb = next_bb;
+	            			sim_color = getFillColor(bb.getColor());
+	            		}else	// last resort: if we're completely outside backbone set the color to gray
+	            			sim_color = getFillColor(Color.GRAY);
+	            	}else
+	                	sim_color = getFillColor(bb.getColor());
+	            }
+            }
+            
+            // don't draw anything if we're not within a valid LCB
+            if (lcb != null && xm.getDrawMatches())
+            {
+                if (seq_right >= lcb.getLeftEnd(getGenome()) && (deletedLCB == null || seq_right < deletedLCB.getLeftEnd(getGenome())))
+                {
+                    Shape oldClip = g.getClip();
+                    if (xm.getDrawLcbBounds() || xm.getFillLcbBoxes())
+                    {
+                    	openJdkSafeRect(r);
+                        g.setClip(r);
+                    }
+                    boolean reverse = lcb.getReverse(getGenome());
+                    double s = ((XmfaViewerModel) model).getSim(getGenome()).getValueForRange(seq_left, seq_right);
+                    double smin = ((XmfaViewerModel) model).getSim(getGenome()).getValueForRange(seq_left, seq_right,-1);
+                    double smax = ((XmfaViewerModel) model).getSim(getGenome()).getValueForRange(seq_left, seq_right,1);
+                    // normalize to a box_height
+                    double heightmin = (((double) smin + 127d) / 256d * sim_height);
+                    double heightmax = (((double) smax + 127d) / 256d * sim_height);
+                    // normalize to a box_height
+                    double height = (((double) s + 127d) / 256d * sim_height);
+                    double match_top = reverse ? 2*half_height - HALF_PEN_WIDTH - height: HALF_PEN_WIDTH + sim_height - height;
+                    double range_top = reverse ? 2*half_height - HALF_PEN_WIDTH - heightmax: HALF_PEN_WIDTH + sim_height - heightmax;
+                    double range_bot = reverse ? 2*half_height - HALF_PEN_WIDTH - heightmin: HALF_PEN_WIDTH + sim_height - heightmin;
+
+                    if(xm.getDrawSimilarityRanges()){
+	                    // draw a space fill box
+	                    Rectangle2D.Double match_rect = new Rectangle2D.Double(pixelD, range_bot, increment, heightmin);
+	                	g.setColor(getFillColorChangeHSB(sim_color, 0f, -0.4f, 0.5f));
+	                    g.fill(match_rect);
+	
+	                    // draw a sim range box
+	                    match_rect = new Rectangle2D.Double(pixelD, range_top, increment, range_bot - range_top);
+	                	g.setColor(getFillColorChangeHSB(sim_color, 0f, 0f, 0.3f));
+	                    g.fill(match_rect);
+	                    
+	                    // draw a mean value line
+	                    g.setColor(getFillColorChangeHSB(sim_color, 0f, 0f, -0.2f));
+	                    Line2D.Double l2d = new Line2D.Double(pixelD, prevh, pixelD+increment, match_top);
+	                    prevh = match_top;
+	                    g.draw(l2d);
+                    }else{
+                    	// just draw a rectangle filling to the mean value
+                        match_top = reverse ? half_height + HALF_PEN_WIDTH : HALF_PEN_WIDTH + sim_height - height;
+	                    Rectangle2D.Double match_rect = new Rectangle2D.Double(pixelD, match_top, increment, height);
+	                	g.setColor(sim_color);
+	                    g.fill(match_rect);
+                    }
+                    g.setClip(oldClip);
+
+                    
+                }
+            }
+        }
+
+        // If there remains a border to be drawn, draw it.
+        
+        if (lcb != null)
+        {
+            // Finish previous rectangle.
+            if (xm.getFillLcbBoxes())
+            {
+            	openJdkSafeRect(r);
+                g.setColor(lcb.color);
+                g.fill(r);
+            }
+            else if (xm.getDrawLcbBounds())
+            {
+            	openJdkSafeRect(r);
+                g.setColor(lcb.color);
+                g.draw(r);
+            }
+        }
+    }
+    
+    // workaround for an OpenJDK 6 bug which can't handle offscreen coords bigger than 30k
+    private void openJdkSafeRect(RoundRectangle2D r){
+    	double newx = 0.0 > r.getMinX() ? 0 : r.getMinX();
+    	newx = getWidth() < newx ? getWidth() : newx;
+    	double newx2 = 0.0 > r.getMaxX() ? 0.0 : r.getMaxX();
+    	newx2 = getWidth() < newx2 ? getWidth() : newx2;
+    	double newwidth = newx2-newx;
+    	r.setRoundRect(newx, r.getY(), newwidth, r.getHeight(), r.getArcWidth(), r.getArcHeight());
+    }
+
+    private void drawWhiteBackgrounds(int half_height, Graphics2D matchGraphics, double arc_size)
+    {
+        int lcbI;
+        matchGraphics.setColor(Color.white);
+        
+        for (lcbI = 0; lcbI < ((LcbViewerModel) model).getVisibleLcbCount(); lcbI++)
+        {
+
+            LCB lcb = ((LcbViewerModel) model).getVisibleLcb(lcbI);
+
+            // skip this LCB if it's out of viewing range
+            if (lcb.getRightEnd(getGenome()) < getGenome().getViewStart() - 1)
+            {
+                continue;
+            }
+            if (lcb.getLeftEnd(getGenome()) > viewEnd + 1)
+            {
+                continue;
+            }
+            
+
+            int leftPixel = sequenceCoordinateToLeftPixel(lcb.getLeftEnd(getGenome()));
+            int rightPixel = sequenceCoordinateToRightPixel(lcb.getRightEnd(getGenome()));
+            int pixelWidth = rightPixel - leftPixel;
+            int lcb_top = lcb.getReverse(getGenome()) ? half_height : 0;
+
+            java.awt.geom.RoundRectangle2D.Double lcb_rect = new java.awt.geom.RoundRectangle2D.Double(leftPixel + HALF_PEN_WIDTH, lcb_top + HALF_PEN_WIDTH, pixelWidth - LCB_BOUNDARY_WIDTH, half_height - LCB_BOUNDARY_WIDTH, arc_size, arc_size);
+            matchGraphics.fill(lcb_rect);
+        }
+    }
+    
+    
+    public void colorChanged(ModelEvent event)
+    {
+        markDirty();
+        repaint();
+    }
+
+    public void weightChanged(ModelEvent event)
+    {
+        markDirty();
+        repaint();
+    }
+
+    public void highlightChanged(ModelEvent evt)
+    {
+        markDirty();
+        repaint();
+    }
+
+    public void drawingSettingsChanged(ModelEvent event)
+    {
+        markDirty();
+        repaint();
+    }
+
+    public void viewableRangeChanged(ModelEvent event)
+    {
+        viewEnd = getGenome().getViewStart() + getGenome().getViewLength() - 1;
+        markDirty();
+        repaint();
+    }
+
+    public void modelReloadEnd(ModelEvent event)
+    {
+        markDirty();
+        repaint();
+    }
+    
+    public void referenceChanged(ModelEvent event)
+    {
+        markDirty();
+    }
+    public void genomesReordered(ModelEvent event)
+    {
+        markDirty();	// the background color may have changed
+    }
+} 
+
diff --git a/src/org/gel/mauve/gui/sequence/MatchPopupMenu.java b/src/org/gel/mauve/gui/sequence/MatchPopupMenu.java
new file mode 100644
index 0000000..4524a13
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/MatchPopupMenu.java
@@ -0,0 +1,94 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.MenuItem;
+import java.awt.PopupMenu;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.LinkedList;
+
+import javax.swing.JCheckBoxMenuItem;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.Match;
+import org.gel.mauve.gui.RearrangementPanel;
+
+/**
+ * A popup menu showing the sequence coordinates covered by an ungapped
+ * alignment
+ */
+
+class MatchPopupMenu extends PopupMenu implements ActionListener, ItemListener {
+	LinkedList match_list;
+
+	RearrangementPanel rrpanel;
+
+	Genome g;
+
+	BaseViewerModel model;
+
+	int lcb_id;
+
+	int remove_item = Integer.MAX_VALUE;
+
+	int keep_item = Integer.MAX_VALUE;
+
+	static final String remove_string = "Remove this LCB";
+
+	static final String keep_string = "Keep this LCB";
+
+	public MatchPopupMenu (RearrangementPanel rrpanel, BaseViewerModel model,
+			Genome g) {
+		this.rrpanel = rrpanel;
+		this.g = g;
+		match_list = new LinkedList ();
+		addActionListener (this);
+	}
+
+	public void addLCBOptions (int lcb_id) {
+		// remove_item = getItemCount();
+		// add( new MenuItem( remove_string ) );
+		keep_item = getItemCount ();
+		JCheckBoxMenuItem keep_checkbox = new JCheckBoxMenuItem (keep_string);
+		keep_checkbox.setState (false);
+		keep_checkbox.addItemListener (this);
+		// add( keep_checkbox );
+	}
+
+	public void addMatch (Match m) {
+		add (new MenuItem ("Align display to " + m.toString ()));
+		// add( new MenuItem( new String( "Show annotation between " ) +
+		// m.starts[ sequence ] + new String(" and ") + (m.starts[ sequence ] +
+		// m.lengths[ sequence ] - 1) ) );
+		match_list.addLast (m);
+	}
+
+	public void actionPerformed (ActionEvent e) {
+		String act_command = e.getActionCommand ();
+		int itemI = 0;
+		for (; itemI < getItemCount (); itemI++) {
+			if (getItem (itemI).getActionCommand () == act_command)
+				break;
+		}
+		if (itemI == remove_item) {
+			// rrpanel.removeLCB( lcb_id );
+		} else if (itemI == keep_item) {
+			// rrpanel.keepLCB( lcb_id );
+		} else {
+			if (itemI > keep_item) {
+				itemI -= 1;
+			}
+			int matchI = itemI;
+			Match selected_match = (Match) match_list.get (matchI);
+			model.alignView (selected_match, g);
+		}
+	}
+
+	/** this gets called when the "Keep LCB" item is selected */
+	public void itemStateChanged (ItemEvent e) {
+		// rrpanel.toggleKeepLCB( lcb_id );
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/sequence/MultiGenomeRectangularBeadRenderer.java b/src/org/gel/mauve/gui/sequence/MultiGenomeRectangularBeadRenderer.java
new file mode 100644
index 0000000..bbba523
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/MultiGenomeRectangularBeadRenderer.java
@@ -0,0 +1,86 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.biojava.bio.gui.sequence.RectangularBeadRenderer;
+import org.biojava.bio.gui.sequence.SequenceRenderContext;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.utils.ChangeVetoException;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+
+public class MultiGenomeRectangularBeadRenderer extends RectangularBeadRenderer
+		implements FlatFileFeatureConstants {
+	
+	protected Hashtable seq_to_offset;
+	
+	protected HashSet painted;
+
+	protected BaseViewerModel model;
+	
+	public double last_offset = -1;
+	
+	private boolean listeners = true;
+	
+	public MultiGenomeRectangularBeadRenderer (double beadDepth, double beadDisplacement, Paint beadOutline,
+				Paint beadFill, Stroke beadStroke, BaseViewerModel mod) {
+		super (beadDepth, beadDisplacement, beadOutline, beadFill, beadStroke);
+		model = mod;
+		painted = new HashSet (model.getSequenceCount ());
+		seq_to_offset = new Hashtable (model.getSequenceCount ());
+	}
+
+	public synchronized void renderBead (Graphics2D g, Feature feature, SequenceRenderContext context) {
+		try {
+			double offset = ((Double) seq_to_offset.get (feature.getSequence ())).doubleValue ();
+			if  (offset != last_offset) {
+				/*if (painted.contains (feature.getSequence ())) {
+					listeners = false;
+					painted.clear ();
+				}
+				else
+					painted.add (feature.getSequence ());*/
+				listeners = false;
+				if (last_offset != -1)
+					setBeadDisplacement (beadDisplacement - last_offset);
+				//new Exception ().printStackTrace ();
+				System.out.println ("last: " + last_offset + "\ndisp: " + beadDisplacement);
+				last_offset = offset;
+				System.out.println ("offset: "  + offset + "\ndisp: " + beadDisplacement);
+				//Snew Exception ().printStackTrace ();
+				setBeadDisplacement (beadDisplacement + offset);
+				listeners = true;
+			}
+			super.renderBead (g, feature, context);
+		} catch (ChangeVetoException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+	
+	public void setOffset (Genome genome, double offset) {
+		seq_to_offset.put (genome.getAnnotationSequence (), new Double (offset));
+	}
+	
+	public double getOffset (Genome genome) {
+		Object ret = seq_to_offset.get (genome.getAnnotationSequence ());
+		if (ret == null)
+			return NO_OFFSET;
+		return ((Double) ret).doubleValue ();
+	}
+	
+	public boolean hasListeners () {
+		System.out.println ("list: " + listeners);
+		if (listeners)
+			return super.hasListeners ();
+		else
+			return false;
+	}
+
+}
diff --git a/src/org/gel/mauve/gui/sequence/RRSequencePanel.java b/src/org/gel/mauve/gui/sequence/RRSequencePanel.java
new file mode 100644
index 0000000..f740262
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/RRSequencePanel.java
@@ -0,0 +1,193 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Panel;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.JLayeredPane;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.ModelListener;
+import org.gel.mauve.ViewerMode;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.gui.FillLayout;
+import org.gel.mauve.gui.RearrangementPanel;
+import org.gel.mauve.histogram.ZoomHistogram;
+
+public class RRSequencePanel extends JLayeredPane implements ModelListener
+{
+    private MatchPanel matchPanel;
+    private HighlightPanel highlightPanel;
+    public RangeHighlightPanel rangeHighlightPanel;
+    public Panel[] otherPanels = null;
+    HashSet<ZoomHistogram> histograms = new HashSet<ZoomHistogram>();
+
+    public RRSequencePanel(RearrangementPanel rrpanel, BaseViewerModel model, Genome genome)
+    {
+        setLayout(new FillLayout());
+        matchPanel = new MatchPanel(rrpanel, model, genome);
+        add(matchPanel, new Integer(1));
+        highlightPanel = new HighlightPanel(model, genome);
+        add(highlightPanel, new Integer(2));
+        rangeHighlightPanel = new RangeHighlightPanel(model, genome);
+        add(rangeHighlightPanel, new Integer(3));
+        setMinimumSize( new Dimension( 10000, 100 ) );
+        setMaximumSize( new Dimension( 10000, 175 ) );
+        addMouseListener(matchPanel);
+        addMouseListener(rangeHighlightPanel);
+        addMouseMotionListener(highlightPanel);
+        addMouseMotionListener(rangeHighlightPanel);
+        model.addModelListener(this);
+    }
+    
+    public MatchPanel getMatchPanel(){ return matchPanel; }
+    
+    /**
+     * goTo - scrolls viewer to particular coordinate of genome
+     * 
+     * @param position The position in the sequence to view
+     */
+    public int goTo (long position) {
+    	return matchPanel.goTo (position);
+    }
+    
+    /**
+     * isForGenome returns true if this RRSequencePanel is associated
+     * with the specified genome, and false otherwise
+     * 
+     * @param genome - the genome in question
+     */
+    public boolean isForGenome (Genome genome) {
+    	return matchPanel.isForGenome (genome);
+    }
+    
+    public void setBackground(Color bg)
+    {
+    	super.setBackground(bg);
+    	if(matchPanel != null)
+    		matchPanel.setBackground(bg);
+    }
+    
+    public int boxHeight()
+    {
+        return matchPanel.boxHeight();
+    }
+    
+    public int boxTop()
+    {
+        return matchPanel.boxTop();
+    }
+    
+    public int sequenceCoordinateToCenterPixel(long coord)
+    {
+        return matchPanel.sequenceCoordinateToCenterPixel(coord);
+    }
+
+    public void colorChanged(ModelEvent event)
+    {
+        // Ignored
+    }
+
+    public void weightChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+    
+    public void referenceChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void drawingSettingsChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void modeChanged(ModelEvent event)
+    {
+        BaseViewerModel model = (BaseViewerModel) event.getSource();
+        
+        if (model.getMode() == ViewerMode.NORMAL)
+        {
+            addMouseListener(matchPanel);
+            addMouseListener(rangeHighlightPanel);
+            addMouseMotionListener(highlightPanel);
+            addMouseMotionListener(rangeHighlightPanel);
+        }
+        else
+        {
+            removeMouseListener(matchPanel);
+            removeMouseListener(rangeHighlightPanel);
+            removeMouseMotionListener(highlightPanel);
+            removeMouseMotionListener(rangeHighlightPanel);
+        }
+    }
+
+    public void viewableRangeChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void viewableRangeChangeStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void viewableRangeChangeEnd(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void modelReloadStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+    
+    public void modelReloadEnd(ModelEvent event)
+    {
+        // Ignored
+    }
+
+    public void genomesReordered(ModelEvent event)
+    {
+        // Ignored
+    }
+
+    public void genomeVisibilityChanged(ModelEvent event)
+    {
+        // Ignored.
+    }
+    public void printingStart(ModelEvent event)
+    {
+        // Ignored.
+    }
+    public void printingEnd(ModelEvent event)
+    {
+        // Ignored.
+    }
+
+    public void attributesChanged(ModelEvent event) 
+    {
+        // find other panels for the data
+    	BaseViewerModel model = (BaseViewerModel)(event.getSource());
+    	List attribs = model.getGenomeAttributes(matchPanel.getGenome());
+    	Iterator iter = attribs == null ? null : attribs.iterator();
+    	while(iter != null && iter.hasNext()){
+    		Object o = iter.next();
+    		if(o instanceof ZoomHistogram){
+    			// have we already added this ZoomHistogram?
+    			if(histograms.contains(o))
+    				continue;
+    			HistogramPanel hp = new HistogramPanel(model, matchPanel.getGenome(), (ZoomHistogram)o);
+    			add(hp,new Integer(4));
+    			histograms.add((ZoomHistogram)o);
+    		}
+    	}
+    }
+} 
+
diff --git a/src/org/gel/mauve/gui/sequence/RangeHighlightPanel.java b/src/org/gel/mauve/gui/sequence/RangeHighlightPanel.java
new file mode 100644
index 0000000..c187f26
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/RangeHighlightPanel.java
@@ -0,0 +1,92 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.HighlightListener;
+import org.gel.mauve.ModelEvent;
+
+public class RangeHighlightPanel extends AbstractSequencePanel implements
+		MouseMotionListener, MouseListener, HighlightListener {
+	float alpha = .15f;
+
+	Color highlight_color = Color.cyan;
+
+	Point drag_start = null;
+
+	public RangeHighlightPanel (BaseViewerModel model, Genome genome) {
+		super (model, genome);
+		setOpaque (false);
+		model.addHighlightListener (this);
+	}
+
+	public void highlightChanged (ModelEvent evt) {
+		if (model.getRangeHighlightGenome () != null)
+			repaint ();
+	}
+
+	public void mousePressed (MouseEvent e) {
+		if ((e.getModifiers () & InputEvent.SHIFT_MASK) != 0)
+			drag_start = e.getPoint ();
+		else
+			drag_start = null;
+	}
+
+	public void mouseEntered (MouseEvent e) {
+	}
+
+	public void mouseExited (MouseEvent e) {
+	}
+
+	public void mouseClicked (MouseEvent e) {
+		drag_start = null;
+		// if shift was down then clear the highlight
+		if ((e.getModifiers () & InputEvent.SHIFT_MASK) != 0)
+			model.highlightRange (null, 0, 0);
+	}
+
+	public void mouseReleased (MouseEvent e) {
+	}
+
+	public void mouseDragged (MouseEvent e) {
+		if (drag_start != null) {
+			int left_x = drag_start.x < e.getX () ? drag_start.x : e.getX ();
+			int right_x = drag_start.x > e.getX () ? drag_start.x : e.getX ();
+			long left_coord = this.pixelToLeftSequenceCoordinate (left_x);
+			long right_coord = this.pixelToRightSequenceCoordinate (right_x);
+			model.highlightRange (this.getGenome (), left_coord, right_coord);
+		}
+	}
+
+	public void mouseMoved (MouseEvent e) {
+	}
+
+	/**
+	 * draw a translucent highlighting over a range of the display
+	 */
+	public void paintComponent (Graphics graphics) {
+		if (model.getRangeHighlightGenome () == this.getGenome ()) {
+			Graphics2D g2d = (Graphics2D) graphics;
+			Composite tmp_composite = g2d.getComposite ();
+			g2d.setComposite (AlphaComposite.getInstance (
+					AlphaComposite.SRC_OVER, alpha));
+			g2d.setColor (highlight_color);
+			long left_end = model.getRangeHighlightLeft ();
+			long right_end = model.getRangeHighlightRight ();
+			int left_pix = sequenceCoordinateToLeftPixel (left_end);
+			int right_pix = sequenceCoordinateToRightPixel (right_end);
+			g2d.fillRect (left_pix, 0, right_pix - left_pix, getHeight ());
+			g2d.setComposite (tmp_composite);
+		}
+	}
+}
diff --git a/src/org/gel/mauve/gui/sequence/RulerPanel.java b/src/org/gel/mauve/gui/sequence/RulerPanel.java
new file mode 100644
index 0000000..275595c
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/RulerPanel.java
@@ -0,0 +1,90 @@
+package org.gel.mauve.gui.sequence;
+
+import java.awt.BorderLayout;
+
+import org.biojava.bio.gui.sequence.RulerRenderer;
+import org.biojava.bio.gui.sequence.TranslatedSequencePanel;
+import org.biojava.bio.symbol.AbstractSymbolList;
+import org.biojava.bio.symbol.Alphabet;
+import org.biojava.bio.symbol.IntegerAlphabet;
+import org.biojava.bio.symbol.Symbol;
+import org.biojava.utils.ChangeVetoException;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.ModelEvent;
+
+/**
+ * 
+ * Adapter class that allows use of Biojava ruler.
+ * 
+ */
+public class RulerPanel extends AbstractSequencePanel {
+	private TranslatedSequencePanel sequencePanel;
+
+	public RulerPanel (BaseViewerModel model, Genome genome) {
+		super (model, genome);
+		setLayout (new BorderLayout ());
+		sequencePanel = new TranslatedSequencePanel ();
+
+		final long rulerLength = getGenome ().getLength ();
+
+		sequencePanel.setSequence (new AbstractSymbolList () {
+
+			public Alphabet getAlphabet () {
+				return IntegerAlphabet.INSTANCE;
+			}
+
+			public int length () {
+				return (int) rulerLength;
+			}
+
+			public Symbol symbolAt (int index) throws IndexOutOfBoundsException {
+				return IntegerAlphabet.INSTANCE.getSymbol (0);
+			}
+
+		});
+		try {
+			sequencePanel.setRenderer (new RulerRenderer ());
+		} catch (ChangeVetoException e) {
+			// Totally unexpected.
+			throw new RuntimeException (e);
+		}
+		add (sequencePanel, BorderLayout.CENTER);
+
+		adjustScaleAndTranslation ();
+	}
+
+	// This prevents the display of the translatedSequencePanel at the wrong
+	// scale...
+	public void setBounds (int arg0, int arg1, int arg2, int arg3) {
+		super.setBounds (arg0, arg1, arg2, arg3);
+
+		adjustScaleAndTranslation ();
+	}
+
+	/**
+	 * @param start
+	 * @param length
+	 */
+	private void adjustScaleAndTranslation () {
+		if (getWidth () != 0) {
+
+			double scale = (double) getWidth ()
+					/ (double) getGenome ().getViewLength ();
+			if (getGenome ().getViewStart () >= getGenome ().getLength ()) {
+				// TranslatedSequencePanel can't handle being translated out of
+				// visibility.
+				sequencePanel.setVisible (false);
+			} else {
+				sequencePanel.setScale (scale);
+				sequencePanel.setSymbolTranslation ((int) getGenome ()
+						.getViewStart ());
+				sequencePanel.setVisible (true);
+			}
+		}
+	}
+
+	public void viewableRangeChanged (ModelEvent event) {
+		adjustScaleAndTranslation ();
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/sequence/SeqPanel.java b/src/org/gel/mauve/gui/sequence/SeqPanel.java
new file mode 100644
index 0000000..d2a0e11
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/SeqPanel.java
@@ -0,0 +1,317 @@
+/*
+ * Created on Jan 19, 2005
+ */
+package org.gel.mauve.gui.sequence;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DragGestureEvent;
+import java.awt.dnd.DragGestureListener;
+import java.awt.dnd.DragGestureRecognizer;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceEvent;
+import java.awt.dnd.DragSourceListener;
+import java.awt.dnd.DropTarget;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.IOException;
+import java.util.TooManyListenersException;
+
+import javax.swing.JLabel;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Genome;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.MyConsole;
+import org.gel.mauve.ViewerMode;
+import org.gel.mauve.gui.MauveFrame;
+import org.gel.mauve.gui.RearrangementPanel;
+import org.gel.mauve.gui.dnd.TransferableObject;
+
+/**
+ * @author Paul Infield-Harm
+ * 
+ * A panel that shows a ruler, sequence (and eventually features) for a genome
+ * sequence.
+ */
+public class SeqPanel extends AbstractSequencePanel implements MouseListener
+{
+    // Default percentage of space for ruler in this panel
+    static final float RULER_RATIO = .05f;
+    private RRSequencePanel sequence;
+    private RulerPanel ruler;
+    protected FeaturePanel feature;
+    private ControlPanel controls;
+    private JLabel label;
+    
+        
+    private static final DataFlavor REARRANGEMENT_FLAVOR = new DataFlavor(Integer.class, TransferableObject.MIME_TYPE);
+
+    private Dimension min_size;
+    private Dimension my_size;
+    private Dimension my_max_size;
+    
+    private Dimension invisible_size;
+
+    public SeqPanel(BaseViewerModel model, Genome genome, RearrangementPanel rearrangementPanel)
+    {
+        super(model, genome);
+        
+        // set the minimum dimensions
+        min_size = this.getSize();
+        min_size.height = 100;
+        this.setMinimumSize(min_size);
+
+        // calculate the preferred and maximum dimensions, set them below
+        my_size = this.getSize();
+        my_size.height = 115;	// start with 115, add more if a FeaturePanel is used
+        my_size.width = 10000;
+        my_max_size = this.getSize();
+        my_max_size.height = 175;	// start with 175, add more if a FeaturePanel is used
+        my_max_size.width = 10000;
+        
+        invisible_size = this.getSize();
+        invisible_size.width = 10000;
+        invisible_size.height = 20;
+        this.setMinimumSize(invisible_size);        
+        
+        controls = new ControlPanel(model, genome, rearrangementPanel);
+        controls.setOpaque(true);
+
+        ruler = new RulerPanel(model, genome);
+        ruler.setOpaque(true);
+
+        // Add the sequence
+        sequence = new RRSequencePanel(rearrangementPanel, model, genome);
+        sequence.setOpaque(true);
+
+        // Add the feature panel, if desired.
+        if (genome.getAnnotationSequence() != null)
+        {
+            feature = new FeaturePanel(genome, model);
+            my_size.height += FeaturePanel.DEFAULT_HEIGHT;
+            my_max_size.height += FeaturePanel.DEFAULT_HEIGHT;
+        }
+
+
+        // Add message if no annotations found, and this is XMFA.
+        if (genome.getAnnotationSequence() == null)
+        {
+            label = new JLabel(genome.getDisplayName() + " (no annotations loaded)");
+        }
+        else
+        {
+            label = new JLabel(genome.getDisplayName());
+        }
+        label.setMaximumSize(new Dimension(100000, 15 ));
+        
+        if(genome.getViewIndex() % 2 == 1)
+        	setBackground(Color.WHITE);
+        
+        configureLayout();
+    }
+    
+    private GridBagConstraints getDefaultControlPanelGridBagConstraints()
+    {
+    	GridBagConstraints c = new GridBagConstraints();
+        c.anchor = GridBagConstraints.WEST;
+        c.fill = GridBagConstraints.VERTICAL;
+        c.gridx=GridBagConstraints.RELATIVE;
+        c.gridy=GridBagConstraints.RELATIVE;
+        c.gridwidth = 1;
+        c.gridheight=GridBagConstraints.REMAINDER;
+        c.insets = new Insets(0,0,0,0);
+        c.ipadx = 0;
+        c.ipady = 0;
+        c.weighty=0;
+        c.weightx=0;
+        return c;
+    }
+    
+    private GridBagConstraints getDefaultContentPanelGridBagConstraints()
+    {
+    	GridBagConstraints c = new GridBagConstraints();
+        c.anchor = GridBagConstraints.CENTER;
+        c.fill = GridBagConstraints.BOTH;
+        c.gridx=GridBagConstraints.RELATIVE;
+        c.gridy=GridBagConstraints.RELATIVE;
+        c.gridwidth = 1;
+        c.gridheight = 1;
+        c.insets = new Insets(0,0,0,0);
+        c.ipadx = 0;
+        c.ipady = 0;
+        c.weighty=0;
+        c.weightx = 1.0;
+        return c;
+    }
+
+    /**
+     * Don't render the control panel when printing
+     */
+    protected void configureLayout()
+    {
+    	removeAll();
+        GridBagLayout layoutManager = new GridBagLayout();
+        setLayout(layoutManager);        
+        GridBagConstraints c = getDefaultControlPanelGridBagConstraints();
+
+        // add the control panel only if we're not printing 
+        if(!printing)
+        {
+	        add(controls);
+	        layoutManager.setConstraints(controls, c);
+        }
+
+        c = getDefaultContentPanelGridBagConstraints();
+
+        if(printing)
+        {
+        	// treat the width differently if printing
+	        c.gridwidth = GridBagConstraints.REMAINDER;    	
+	        c.weightx = 1.0;
+	        c.gridx = 0;
+        }
+        
+        if( getGenome().getVisible() )
+        {
+        	c.weighty = RULER_RATIO;
+	        add(ruler);
+	        layoutManager.setConstraints(ruler, c);
+	
+	        c.weighty = 2.0;
+	        add(sequence);
+	        layoutManager.setConstraints(sequence, c);
+	
+	        if (getGenome().getAnnotationSequence() != null)
+	        {
+	            add(feature);
+	            c.weighty = 0.1;
+	            layoutManager.setConstraints(feature, c);
+	        }
+	
+	        // Add the name.
+	        c.weighty = 0.1;
+	        add(label);
+	        layoutManager.setConstraints(label, c);
+	        setPreferredSize(my_size);
+	        setMaximumSize(my_max_size);
+	        setMinimumSize(min_size);
+	        setSize(my_size);
+	    }else
+	    {
+	    	// genome not visible...
+	        // just add the name.
+	        c.weighty = 0.1;
+	        add(label);
+	        layoutManager.setConstraints(label, c);
+	        setAllSizes(invisible_size);
+	    }
+    }
+    
+    /**
+     * Sets all current, preferred max and min sizes to d
+     * @param d
+     */
+    private void setAllSizes(Dimension d)
+    {
+        setPreferredSize(d);
+        setMaximumSize(d);    	
+        setMinimumSize(d);
+        setSize(d);
+    }
+    
+    public RRSequencePanel getSequencePanel()
+    {
+        return sequence;
+    }
+    public FeaturePanel getFeaturePanel()
+    {
+        return feature;
+    }
+    public ControlPanel getControlPanel()
+    {
+        return controls;
+    }
+
+    public void genomeVisibilityChanged(ModelEvent me)
+    {
+    	configureLayout();
+    }
+
+    public void setBackground(java.awt.Color bg)
+    {
+    	super.setBackground(bg);
+    	if(sequence != null)
+    		sequence.setBackground(bg);
+    	if(ruler != null)
+    		ruler.setBackground(bg);
+    	if(feature != null)
+    		feature.setBackground(bg);
+    }
+    
+    /******* Get rid of controls when printing ***************/
+    private boolean printing = false;
+    public void printingStart(ModelEvent event)
+    {
+    	printing = true;
+    	configureLayout();
+    }
+    public void printingEnd(ModelEvent event)
+    {
+    	printing = false;
+    	configureLayout();
+    }
+
+    
+    /****************** Mouse listener events ********************/
+    
+    public void mouseClicked(MouseEvent e)
+    {
+        long coord = pixelToCenterSequenceCoordinate(e.getX());
+        
+        if (e.isControlDown())
+        {
+            model.zoomAndCenter(getGenome(), 50, coord);
+        }
+        else
+        {
+            model.zoomAndCenter(getGenome(), 200, coord);
+        }
+        
+    }
+
+    public void mouseEntered(MouseEvent e)
+    {
+        // Ignored.
+    }
+
+    public void mouseExited(MouseEvent e)
+    {
+        // Ignored.
+    }
+
+    public void mousePressed(MouseEvent e)
+    {
+        // Ignored.
+    }
+
+    public void mouseReleased(MouseEvent e)
+    {
+        // Ignored.
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/gui/sequence/ZiggyRectangularBeadRenderer.java b/src/org/gel/mauve/gui/sequence/ZiggyRectangularBeadRenderer.java
new file mode 100644
index 0000000..6216b74
--- /dev/null
+++ b/src/org/gel/mauve/gui/sequence/ZiggyRectangularBeadRenderer.java
@@ -0,0 +1,276 @@
+/*
+ *                    BioJava development code
+ *
+ * This code may be freely distributed and modified under the
+ * terms of the GNU Lesser General Public Licence.  This should
+ * be distributed with the code.  If you do not have a copy,
+ * see:
+ *
+ *      http://www.gnu.org/copyleft/lesser.html
+ *
+ * Copyright for this code is held jointly by the individual
+ * authors.  These should be listed in @author doc comments.
+ *
+ * For more information on the BioJava project and its aims,
+ * or to join the biojava-l mailing list, visit the home page
+ * at:
+ *
+ *      http://www.biojava.org/
+ *
+ */
+
+package org.gel.mauve.gui.sequence;
+
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Iterator;
+
+import org.biojava.bio.gui.sequence.AbstractBeadRenderer;
+import org.biojava.bio.gui.sequence.SequenceRenderContext;
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.FeatureHolder;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.symbol.Location;
+import org.biojava.utils.ChangeEvent;
+import org.biojava.utils.ChangeSupport;
+import org.biojava.utils.ChangeType;
+import org.biojava.utils.ChangeVetoException;
+
+/**
+ * <p><code>RectangularBeadRenderer</code> renders features as simple
+ * rectangles. Their outline and fill <code>Paint</code>,
+ * <code>Stroke</code>, feature depth, Y-axis displacement are
+ * configurable. The height of the rectangle will be equal to half its
+ * width, but not greater than the <code>beadDepth</code> set in the
+ * constructor.</p>
+ *
+ * <p>An alternative bead height behaviour is available where the
+ * rectangle height does not scale with its current width. The
+ * <code>setHeightScaling</code> method should be passed a boolean
+ * value to change this. The default is to use height scaling.</p>
+ *
+ * @author Keith James
+ * @author Aaron Darling
+ */
+public class ZiggyRectangularBeadRenderer extends AbstractBeadRenderer
+{
+    /**
+     * Constant <code>HEIGHTSCALING</code> indicating a change to the
+     * feature height scaling policy.
+     */
+    public static final ChangeType HEIGHTSCALING =
+	new ChangeType("The height scaling policy of the features has changed",
+		       ZiggyRectangularBeadRenderer.class,
+		       "HEIGHTSCALING", SequenceRenderContext.LAYOUT);
+
+    protected Rectangle2D rect;
+    protected boolean scaleHeight;
+
+    /**
+     * Creates a new <code>ZiggyRectangularBeadRenderer</code> with the
+     * default settings.
+     */
+    public ZiggyRectangularBeadRenderer()
+    {
+        super();
+        rect = new Rectangle2D.Double();
+        scaleHeight = true;
+    }
+
+    /**
+     * Creates a new <code>ZiggyRectangularBeadRenderer</code>.
+     *
+     * @param beadDepth a <code>double</code>.
+     * @param beadDisplacement a <code>double</code>.
+     * @param beadOutline a <code>Paint</code>.
+     * @param beadFill a <code>Paint</code>.
+     * @param beadStroke a <code>Stroke</code>.
+     */
+    public ZiggyRectangularBeadRenderer(double beadDepth,
+                                   double beadDisplacement,
+                                   Paint  beadOutline,
+                                   Paint  beadFill,
+                                   Stroke beadStroke)
+    {
+        super(beadDepth, beadDisplacement, beadOutline, beadFill, beadStroke);
+        rect = new Rectangle2D.Double();
+        scaleHeight = true;
+    }
+
+    /* TODO: Implement scaling */
+    public void renderBead(
+    	    Graphics2D g, Feature f, SequenceRenderContext context
+    	  ) {
+    	    Location loc = f.getLocation();
+    	    Iterator i = loc.blockIterator();
+    	    Location last = null;
+    	    if(i.hasNext()) {
+    	      last = (Location) i.next();
+    	      renderLocation(g, last, context);
+    	    }
+    	    while(i.hasNext()) {
+    	      Location next = (Location) i.next();
+    	      renderLink(g, f, last, next, context);
+    	      renderLocation(g, next, context);
+    	      last = next;
+    	    }
+    	  }
+
+    	  private void renderLocation(
+    	    Graphics2D g, Location loc, SequenceRenderContext context
+    	  ) {
+    	    Rectangle2D.Double block = new Rectangle2D.Double();
+    	    double min = context.sequenceToGraphics(loc.getMin());
+    	    double max = context.sequenceToGraphics(loc.getMax()+1);
+    	    if(context.getDirection() == SequenceRenderContext.HORIZONTAL) {
+    	      block.setFrame(
+    	        min, beadDisplacement,
+    	        max - min, beadDepth
+    	      );
+    	    } else {
+    	      block.setFrame(
+    	    		  beadDisplacement, min,
+    	    		  beadDepth, max - min
+    	      );
+    	    }
+    	    if(beadFill != null) {
+    	      g.setPaint(beadFill);
+    	      g.fill(block);
+    	    }
+    	    if(beadOutline != null) {
+    	    	g.setStroke(beadStroke);
+    	    	g.setPaint(beadOutline);
+    	      	g.draw(block);
+    	    }
+    	  }
+
+    	    private StrandedFeature.Strand getStrand(Feature f) {
+    		if (f instanceof StrandedFeature) {
+    		    return ((StrandedFeature) f).getStrand();
+    		} else {
+    		    FeatureHolder fh = f.getParent();
+    		    if (fh instanceof Feature) {
+    			return getStrand((Feature) fh);
+    		    } else {
+    			return StrandedFeature.UNKNOWN;
+    		    }
+    		}
+    	    }
+
+    	  private void renderLink(
+    	    Graphics2D g, Feature f, Location source, Location dest,
+    	    SequenceRenderContext context
+    	  ) {
+    	    Line2D line = new Line2D.Double();
+    	    Point2D startP;
+    	    Point2D midP;
+    	    Point2D endP;
+    	    double half = beadDisplacement + beadDepth * 0.5;
+    	    if(context.getDirection() == SequenceRenderContext.HORIZONTAL) {
+    	      if(getStrand(f) == StrandedFeature.NEGATIVE) {
+    	        double start = context.sequenceToGraphics(dest.getMin());
+    	        double end = context.sequenceToGraphics(source.getMax()+1);
+    	        double mid = (start + end) * 0.5;
+    	        startP = new Point2D.Double(start, half);
+    	        midP   = new Point2D.Double(mid,   beadDisplacement + beadDepth);
+    	        endP   = new Point2D.Double(end,   half);
+    	      } else {
+    	        double start = context.sequenceToGraphics(source.getMax()+1);
+    	        double end = context.sequenceToGraphics(dest.getMin());
+    	        double mid = (start + end) * 0.5;
+    	        startP = new Point2D.Double(start, half);
+    	        midP   = new Point2D.Double(mid,   beadDisplacement);
+    	        endP   = new Point2D.Double(end,   half);
+    	      }
+    	    } else {
+    	      if (getStrand(f) == StrandedFeature.NEGATIVE) {
+    	        double start = context.sequenceToGraphics(dest.getMin()+1);
+    	        double end = context.sequenceToGraphics(source.getMax());
+    	        double mid = (start + end) * 0.5;
+    	        startP = new Point2D.Double(half,       start);
+    	        midP   = new Point2D.Double(beadDisplacement + beadDepth, mid);
+    	        endP   = new Point2D.Double(half,       end);
+    	      } else {
+    	        double start = context.sequenceToGraphics(source.getMax());
+    	        double end = context.sequenceToGraphics(dest.getMin()+1);
+    	        double mid = (start + end) * 0.5;
+    	        startP = new Point2D.Double(half, start);
+    	        midP   = new Point2D.Double(beadDisplacement,  mid);
+    	        endP   = new Point2D.Double(half, end);
+    	      }
+    	    }
+    	    g.setStroke(beadStroke);
+    	    g.setPaint(beadOutline);
+    	    line.setLine(startP, midP);
+    	    g.draw(line);
+    	    line.setLine(midP, endP);
+    	    g.draw(line);
+    	  }
+
+    /**
+     * <code>getDepth</code> calculates the depth required by this
+     * renderer to display its beads.
+     *
+     * @param context a <code>SequenceRenderContext</code>.
+     *
+     * @return a <code>double</code>.
+     */
+    public double getDepth(SequenceRenderContext context)
+    {
+        // Get max depth of delegates using base class method
+        double maxDepth = super.getDepth(context);
+        return Math.max(maxDepth, (beadDepth + beadDisplacement));
+    }
+
+    /**
+     * <code>getHeightScaling</code> returns the state of the height
+     * scaling policy.
+     *
+     * @return a <code>boolean</code> true if height scaling is
+     * enabled.
+     */
+    public boolean getHeightScaling()
+    {
+        return scaleHeight;
+    }
+
+    /**
+     * <code>setHeightScaling</code> sets the height scaling
+     * policy. Default behaviour is for this to be enabled leading to
+     * features being drawn with a height equal to half their width,
+     * subject to a maximum height restriction equal to the
+     * <code>beadDepth</code> set in the constructor. If disabled,
+     * features will always be drawn at the maximum height allowed by
+     * the <code>beadDepth</code> parameter.
+     *
+     * @param isEnabled a <code>boolean</code>.
+     *
+     * @exception ChangeVetoException if an error occurs.
+     */
+    public void setHeightScaling(boolean isEnabled) throws ChangeVetoException
+    {
+        if (hasListeners())
+	{
+	    ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
+	    synchronized(cs)
+	    {
+		ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT,
+						 null, null,
+						 new ChangeEvent(this, HEIGHTSCALING,
+								 new Boolean(scaleHeight),
+								 new Boolean(isEnabled)));
+		cs.firePreChangeEvent(ce);
+                scaleHeight = isEnabled;
+		cs.firePostChangeEvent(ce);
+	    }
+	}
+	else
+	{
+            scaleHeight = isEnabled;
+	}
+    }
+}
diff --git a/src/org/gel/mauve/histogram/HistogramBuilder.java b/src/org/gel/mauve/histogram/HistogramBuilder.java
new file mode 100644
index 0000000..928b4d3
--- /dev/null
+++ b/src/org/gel/mauve/histogram/HistogramBuilder.java
@@ -0,0 +1,122 @@
+package org.gel.mauve.histogram;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Vector;
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.XMFAAlignment;
+import org.gel.mauve.XmfaViewerModel;
+
+public class HistogramBuilder {
+	private static class HistogramLine {
+		int genomeId;
+		int blockId;
+		String tag;
+		float[] data;
+	}
+
+	// reads a file with one or more histograms keyed to genomes
+	// populates the genome attributes of the XMFA model
+	public static void build(RandomAccessFile f, XmfaViewerModel model){
+		if(f==null) return;
+        try{
+        	XMFAAlignment xmfa = model.getXmfa();
+	        String line = null;
+	        // Parse each line into a histogram line
+	        Vector histlines = new Vector();
+	        float maxval = -1;	// for normalization of the histogram
+	        float minval = 1000000;	// for normalization of the histogram
+	        byte[] data = new byte[1000000];
+	        int start = data.length;
+	        int state = 0;	// 0 read blockID, 1 read genomeID, 2 read tag, 3 read data
+            HistogramLine hl = new HistogramLine();
+            int pos = 0;
+            int jj=0;
+            while(pos < f.length()){
+            	int remainder = data.length-start;
+            	int toread = data.length-remainder;
+            	toread = (int) (toread < f.length() - pos ? toread : f.length() - pos);
+            	System.out.println("read " + pos + " / " + f.length());
+            	System.arraycopy(data, start, data, 0, remainder);
+    	        f.read(data,remainder,toread);
+            	start = 0;
+            	pos += toread;            	
+				for(int cur = remainder; cur < toread+remainder; cur++)
+				{
+					switch(data[cur]){
+					case '\n':	
+						start = cur+1; 
+						state=0;
+						jj=0;
+				        histlines.add(hl);
+			            hl = new HistogramLine();
+						break;
+					case '\t':
+						String s = new String(data, start, cur-start);
+						if(state==0){
+							hl.blockId = Integer.parseInt(s);	state++;
+						}else if(state==1){
+							hl.genomeId = Integer.parseInt(s);	state++;	hl.data = new float[(int)xmfa.getLcbLength(hl.blockId)];
+						}else if(state==2){
+							hl.tag = s;							state++;
+						}else if(state==3){
+							float floater = Float.parseFloat(s);
+							maxval = floater > maxval ? floater : maxval;
+							minval = floater < minval ? floater : minval;
+							hl.data[jj++] = floater;
+						}
+						start = cur+1;
+						break;
+						default:	;
+					}
+				}
+            }
+            // get the last one
+            if(jj>0){
+                histlines.add(hl);
+            }
+			// now aggregate the different histogram lines for each genome
+			for(int i=0; i < model.getSequenceCount(); i++){
+				boolean foundData = false;
+				Genome g = model.getGenomeBySourceIndex(i);
+				Boolean gap = new Boolean(false);
+				byte[] histvals = new byte[(int)g.getLength()];
+				for(int j=0; j<histvals.length; j++){
+					histvals[j]=-128;
+				}
+				float maxrenorm = -127;
+				// scan each histogram line, looking for lines relevant to this genome
+				for(int k=0; k<histlines.size();k++){
+					hl = (HistogramLine)histlines.get(k);
+					if(hl==null)	continue;
+					if(hl.genomeId!=i)	continue;
+					foundData = true;					
+					float normval = maxval-minval;
+					for(int j=0; j<hl.data.length; j++){
+						long histpos = xmfa.getCoordinate(model, g, hl.blockId, j, gap);
+						if(gap) continue;
+						float renorm = ((hl.data[j]-minval) / (normval));
+						renorm -= 0.5;
+						renorm *= 255;
+						if(histpos < histvals.length)
+							histvals[(int)histpos] = renorm < -127 ? -128 : (byte)renorm;
+						else
+							System.err.println("hisvals.lengt " + histvals.length + " histpos " + histpos);
+						maxrenorm = maxrenorm > renorm ? maxrenorm : renorm;
+					}
+					// free memory
+					histlines.set(k, null);
+				}
+				// put the genome-wide histogram values into a ZoomHistogram
+				if(foundData){
+					ZoomHistogram zh = new ZoomHistogram(g);
+					zh.setGenomeLevelData(histvals);
+					model.addGenomeAttribute(g, zh);
+				}
+			}
+        }catch(IOException ioe){
+        	ioe.printStackTrace();
+        }
+	}
+}
diff --git a/src/org/gel/mauve/histogram/ZoomHistogram.java b/src/org/gel/mauve/histogram/ZoomHistogram.java
new file mode 100644
index 0000000..3556763
--- /dev/null
+++ b/src/org/gel/mauve/histogram/ZoomHistogram.java
@@ -0,0 +1,382 @@
+package org.gel.mauve.histogram;
+
+import java.io.Serializable;
+
+import org.gel.mauve.Genome;
+
+/**
+ * Meant to represent data that varies over a range and needs to be viewed at
+ * multiple resolutions.
+ * 
+ * @author Aaron Darling
+ *
+ */
+public class ZoomHistogram implements Serializable {
+	
+	static final long serialVersionUID = 2;
+	
+	/** < Never index fewer than this many alignment columns */
+	protected int max_resolution = 5;
+
+	/** < The number of characters covered by an index entry at each level */
+	protected long [] resolutions;
+
+	/** < The number of index entries at each level */
+	protected long [] level_sizes;
+
+	/** < The number of resolution levels */
+	protected int levels;
+
+	/** < The similarity index, similarity values are discretized to a byte value */
+	protected byte [] sim_index;
+
+
+	protected long seq_length;
+
+
+	protected int index_factor = 8;
+
+	/** < Index granularity grows in multiples of this number */
+	
+	protected int min_index_values = 500;
+
+	/** < Never create a resolution level with fewer than this many values */
+
+	protected int max_index_mb = 300;
+
+	/** < Maximum size in MB for the index */
+
+	protected byte [] min_vals;	/**< min values for levels above 0 */
+	protected byte [] max_vals; /**< max values for levels above 0 */
+
+	protected ZoomHistogram () {
+	}
+	
+	public ZoomHistogram(Genome g){
+		this.seq_length = g.getLength ();		
+		allocateIndex ();
+	}
+	
+	public ZoomHistogram (long seq_length, int level, int max_res, long [] sizes, long [] res) {
+		init (seq_length, level, max_res, sizes, res);
+	}
+	
+	protected void init (long seq_length, int levels, int max_res, long [] sizes, long [] res) {
+		this.seq_length = seq_length;
+		this.levels = levels;
+		max_resolution = max_res;
+		level_sizes = sizes;
+		resolutions = res;
+		allocateIndex ();
+	}
+	/**
+	 * Returns the distance into the values where a particular
+	 * resolution level's data begins
+	 */
+	public long getLevelOffset (int level) {
+		long level_offset = 0;
+		for (int levelI = 0; levelI < level; levelI++) {
+			level_offset += level_sizes[levelI];
+		}
+		return level_offset;
+	}
+	
+	/**
+	 * Returns the size of the specified level
+	 */
+	public long getLevelSize (int level) {
+		return level_sizes [level];
+	}
+
+	public long getResolution (int level) {
+		return resolutions[level];
+	}
+
+	public int getMaxResolution() {
+		return max_resolution;
+	}
+
+	public int getLevels () {
+		return levels;
+	}
+	
+	public byte getValueForRange (long left, long right) {
+		return getValueForRange(left,right,0);
+	}
+	// if asked for value of a range that spans beyond legal coordinates
+	// this function will return the average for the portion of the requested
+	// range that lies within bounds
+	// @argument type Whether to get the mean, min, or max.  mean = 0, min = -1, max = 1
+	public byte getValueForRange (long left, long right, int type) {
+		// never allow the range to be less than window_size--
+		// why not max_resolution * 2? with max_res * 2 we can ensure that at
+		// least
+		// one similarity value gets returned (in rare cases 2)
+		if (right - left + 1 < max_resolution * 2) {
+			long extra = (max_resolution * 2 - (right - left)) / 2;
+			left -= extra;
+			left = left < 0 ? 0 : left;
+			right = left + max_resolution * 2;
+		}
+
+		// find the highest resolution completely within a range of right - left
+		long range_size = right - left;
+		int levelI = resolutions.length - 1;
+		for (; levelI > 0; levelI--) {
+			if (resolutions[levelI] * 2 < range_size)
+				break; // this level must contain at least part of what we're
+			// looking for
+		}
+
+		// get the first resolution index at this level
+		long firstI = left / resolutions[levelI];
+		if (left % resolutions[levelI] != 0)
+			firstI++;
+		long lastI = right / resolutions[levelI];
+
+		// truncate them if they are out-of-range
+		firstI = firstI < level_sizes[levelI] ? firstI : level_sizes[levelI] - 1;
+		lastI = lastI < level_sizes[levelI] ? lastI : level_sizes[levelI] - 1;
+		firstI = firstI < 0 ? 0 : firstI;
+		lastI = lastI < 0 ? 0 : lastI;
+
+		lastI = lastI < firstI ? firstI : lastI;
+		long lev_offset = getLevelOffset (levelI);
+		int start_ind = (int) (lev_offset + firstI);
+		int end_ind = (int) (lev_offset + lastI);
+		byte sim;
+		if(type==0)
+			sim = averageValues (start_ind, end_ind);
+		else 
+			sim = minOrMaxValues (start_ind, end_ind,type);
+
+		// recursively get the beginning and ending pieces
+		// only recurse if enough space remains on either side and we
+		// haven't already recursed down to the lowest resolution level
+		long left_size = firstI * resolutions[levelI] - left;
+		byte left_sim = 0;
+		if (left_size > resolutions[0] && levelI > 0){
+			long newr = firstI * resolutions[levelI];
+			// TODO: fix whatever bug causes this infinite loop condition!
+			if(newr==right){left_sim=0;}else{
+				left_sim = getValueForRange (left, newr,type);
+			}
+		}else
+			left_sim = sim;
+		long right_size = right - (lastI + 1) * resolutions[levelI];
+		byte right_sim = 0;
+		if (right_size > resolutions[0] && levelI > 0)
+		{
+			long newl = (lastI + 1) * resolutions[levelI];
+			if(newl==left){right_sim = 0;}else{
+				right_sim = getValueForRange (newl, right,type);
+			}
+		}else
+			right_sim = sim;
+
+		// average the three together
+		if(type==0){
+			double asim_singh = left_size * left_sim + right_size * right_sim + sim
+					* (range_size - left_size - right_size);
+			asim_singh /= range_size;
+			byte final_sim = (byte) asim_singh;
+			return final_sim;
+		}else if(type==1){
+			byte m = right_sim > sim ? right_sim : sim;
+			m = left_sim > m ? left_sim : m;
+			return m;
+		}else{
+			byte m = right_sim < sim ? right_sim : sim;
+			m = left_sim < m ? left_sim : m;
+			return m;
+		}
+	}
+	
+	/**
+	 * returns the average of the value of the given range, inclusive
+	 * @param start_ind
+	 * @param end_ind
+	 * @return
+	 */
+	public byte averageValues (int start_ind, int end_ind) {
+		if (end_ind < start_ind) {
+			throw new RuntimeException ("Corrupt SimilarityIndex");
+		}
+		end_ind++;
+		long sim_sum = 0;
+		int indexI = start_ind;
+		for (; indexI < end_ind; indexI++) {
+			sim_sum += getValue (indexI);
+		}
+		return (byte) (sim_sum / (indexI - start_ind));
+	}
+	
+	/**
+	 * returns the minimum value of the given range, inclusive
+	 * @param start_ind
+	 * @param end_ind
+	 * @return
+	 */
+	public byte minOrMaxValues (int start_ind, int end_ind, int type) {
+		if (end_ind < start_ind) {
+			throw new RuntimeException ("Corrupt SimilarityIndex");
+		}
+		end_ind++;
+		byte minnow = 127;
+		if(type>0)	minnow = -128;
+		byte[] arr = null;
+		if(start_ind < level_sizes[0]){
+			arr = sim_index;
+		}else{
+			start_ind -= level_sizes[0];
+			end_ind -= level_sizes[0];
+			arr = type < 0 ? min_vals : max_vals;
+		}
+		int indexI = start_ind;
+		for (; indexI < end_ind; indexI++) {			
+			if(type < 0){	
+				byte cur = arr[indexI];
+				minnow = cur < minnow ? cur : minnow;
+			}
+			if(type > 0){
+				byte cur = arr[indexI];
+				minnow = cur > minnow ? cur : minnow;
+			}
+		}
+		return minnow;
+	}
+
+
+	/**
+	 * Returns the specified value
+	 * 
+	 * @param index
+	 * @return
+	 */
+	public byte getValue(int index) {
+		return sim_index [index];
+	}
+
+
+	/**
+	 * Allocates space for the similarity index
+	 */
+	protected void allocateIndex () {
+		// calculate the number of index levels
+		long level_one = seq_length / max_resolution;
+		long level_tmp = level_one;
+		levels = seq_length > max_resolution ? 1 : 0;
+		while (level_tmp > min_index_values) {
+			level_tmp /= index_factor;
+			levels++;
+		}
+
+		resolutions = new long [levels];
+		level_sizes = new long [levels];
+		level_tmp = level_one;
+		long size_sum = 0;
+		int levelI;
+		long cur_resolution = max_resolution;
+		for (levelI = 0; levelI < levels; levelI++) {
+			resolutions[levelI] = cur_resolution;
+			level_sizes[levelI] = level_tmp;
+			size_sum += level_tmp;
+			level_tmp /= index_factor;
+			cur_resolution *= index_factor;
+		}
+
+		// check whether the index will fit in the max size
+		if (size_sum > ((long) max_index_mb * 1024l * 1024l)) {
+			throw new RuntimeException ("Similarity index is too large.");
+		}
+
+		sim_index = new byte [(int) size_sum];
+
+		// nothing more to allocate if there are no levels
+		if (levels == 0)
+			return;
+		
+		min_vals = new byte[(int)(size_sum - level_sizes[0])];
+		max_vals = new byte[(int)(size_sum - level_sizes[0])];
+	}
+
+	/* Calculates the higher-level values in the multi-level index as
+	 * an average value over the subranges.
+	 */
+	protected void calculateHigherLevels(){
+		// calculate subsequent levels using the previous level
+		for (int levelI = 1; levelI < levels; levelI++) {
+			int componentI = (int) getLevelOffset (levelI - 1);
+			int level_offset = (int) getLevelOffset (levelI);
+			for (int indexI = 0; indexI < level_sizes[levelI]; indexI++) {
+				int sim_sum = 0;
+				int min_val = 999;	// this is bigger than byte range
+				int max_val = -999;
+				for (int subI = 0; subI < index_factor; subI++) {
+					sim_sum += sim_index[componentI];
+					min_val = sim_index[componentI] < min_val ? sim_index[componentI] : min_val;
+					max_val = sim_index[componentI] > max_val ? sim_index[componentI] : max_val;
+					componentI++;
+				}
+				// set to the average of its components
+				sim_index[level_offset + indexI] = (byte) (sim_sum / index_factor);
+				min_vals[level_offset + indexI - (int)level_sizes[0]] = (byte)min_val;
+				max_vals[level_offset + indexI - (int)level_sizes[0]] = (byte)max_val;
+			}
+		}
+	}
+
+	/**
+	 * Set an individual similarity value
+	 */
+	protected void setSimilarity (int level, int index, byte sim_value) {
+		int level_offset = 0;
+		for (int levelI = 0; levelI < level; levelI++) {
+			level_offset += level_sizes[levelI];
+		}
+		if (index > level_sizes[level])
+			throw new ArrayIndexOutOfBoundsException ();
+
+		sim_index[level_offset + index] = sim_value;
+	}
+	
+	/**
+	 * get an individual similarity
+	 */
+	public byte getSimilarity (int level, int index) {
+		int level_offset = 0;
+		for (int levelI = 0; levelI < level; levelI++) {
+			level_offset += level_sizes[levelI];
+		}
+		if (index >= level_sizes[level])
+			throw new ArrayIndexOutOfBoundsException ();
+
+		return sim_index[level_offset + index];
+	}
+	
+	/**
+	 * Sets the histogram height values for the base level
+	 * higher levels will be automatically calculated as the average over regions in the base level
+	 * 
+	 * @param base	array of bytes with length equal to getLevelSize(0)
+	 */
+	public void setBaseLevel( byte[] base ){
+		System.arraycopy(base, 0, sim_index, 0, (int)getLevelSize(0));
+		calculateHigherLevels();
+	}
+
+	/*
+	 * Use a set of values as long as the genome to calculate the base index
+	 * Calculates average values over max_resolution size chunks. Does not smooth.
+	 */
+	public void setGenomeLevelData( byte[] data ){
+		for(int d=0; d<level_sizes[0]; d++){
+			float runsum = 0;
+			for(int i=0; i<max_resolution; i++)
+				runsum += data[d*max_resolution+i];
+			runsum /= max_resolution;
+			sim_index[d] = (byte)runsum;
+		}
+		calculateHigherLevels();
+	}
+}
diff --git a/src/org/gel/mauve/module/MauveModule.java b/src/org/gel/mauve/module/MauveModule.java
new file mode 100644
index 0000000..e1f5f2c
--- /dev/null
+++ b/src/org/gel/mauve/module/MauveModule.java
@@ -0,0 +1,22 @@
+package org.gel.mauve.module;
+
+import org.gel.mauve.gui.Mauve;
+import org.gel.mauve.gui.MauveFrame;
+
+public class MauveModule extends Mauve {
+	
+	protected ModuleListener mod_list;
+	
+	public MauveModule (ModuleListener ml) {
+		mod_list = ml;
+	}
+
+	protected MauveFrame makeNewFrame() {
+		MauveModuleFrame frame = new MauveModuleFrame (this);
+		frames.add (frame);
+		return frame;
+	}
+	
+	
+
+}
diff --git a/src/org/gel/mauve/module/MauveModuleFrame.java b/src/org/gel/mauve/module/MauveModuleFrame.java
new file mode 100644
index 0000000..31e3b41
--- /dev/null
+++ b/src/org/gel/mauve/module/MauveModuleFrame.java
@@ -0,0 +1,23 @@
+package org.gel.mauve.module;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.gui.Mauve;
+import org.gel.mauve.gui.MauveFrame;
+
+public class MauveModuleFrame extends MauveFrame {
+	
+	MauveModule module;
+
+	public MauveModuleFrame(Mauve mauve) {
+		super(mauve);
+		module = (MauveModule) mauve;
+	}
+
+	public void setModel(BaseViewerModel model) {
+		super.setModel(model);
+		module.mod_list.startModule(this);
+	}
+	
+	
+
+}
diff --git a/src/org/gel/mauve/module/ModuleListener.java b/src/org/gel/mauve/module/ModuleListener.java
new file mode 100644
index 0000000..5ca4055
--- /dev/null
+++ b/src/org/gel/mauve/module/ModuleListener.java
@@ -0,0 +1,9 @@
+package org.gel.mauve.module;
+
+import org.gel.mauve.gui.MauveFrame;
+
+public interface ModuleListener {
+	
+	public void startModule (MauveFrame frame);
+
+}
diff --git a/src/org/gel/mauve/recombination/WeakArgDataModel.java b/src/org/gel/mauve/recombination/WeakArgDataModel.java
new file mode 100644
index 0000000..7e5942d
--- /dev/null
+++ b/src/org/gel/mauve/recombination/WeakArgDataModel.java
@@ -0,0 +1,18 @@
+package org.gel.mauve.recombination;
+
+import java.io.Serializable;
+
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.histogram.ZoomHistogram;
+
+public class WeakArgDataModel implements Serializable
+{
+	String treeString;
+	ZoomHistogram[] incoming;
+	ZoomHistogram[] outgoing;
+
+	WeakArgDataModel( XmfaViewerModel xmfa ){
+		incoming = new ZoomHistogram[xmfa.getSequenceCount()*2 - 1];
+		outgoing = new ZoomHistogram[xmfa.getSequenceCount()*2 - 1];
+	}
+}
diff --git a/src/org/gel/mauve/recombination/WeakArgModelBuilder.java b/src/org/gel/mauve/recombination/WeakArgModelBuilder.java
new file mode 100644
index 0000000..8c1a83e
--- /dev/null
+++ b/src/org/gel/mauve/recombination/WeakArgModelBuilder.java
@@ -0,0 +1,268 @@
+package org.gel.mauve.recombination;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Vector;
+
+import org.apache.tools.bzip2.CBZip2InputStream;
+import org.gel.mauve.Genome;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.histogram.ZoomHistogram;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/*
+ * Class to read in recombination data from a run of the Weak ARG and build a data model for it
+ */
+public class WeakArgModelBuilder {
+	public static WeakArgDataModel buildModel(File f, XmfaViewerModel xmfa) throws FileNotFoundException, IOException, SAXException
+	{
+		WeakArgDataModel model = null;
+		
+		// open the file and create an input stream
+		// assume it is bzip2 compressed
+		FileInputStream fis = new FileInputStream(f);
+		// bzip reader expects the first two bytes skipped
+		int B = fis.read(); 
+		int z = fis.read();	
+		if(B==66&&z==90){
+			model=readWargXmlBzip2(fis, xmfa);
+			// save to a cache file
+			File outfile = new File(f.getAbsolutePath() + ".mauvedata");
+			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(outfile));
+			oos.writeObject(model);
+			oos.close();
+		}else{
+			model=readStoredData(f);
+		}
+
+		// add the data to the viewer model
+		for(int i=0; i < xmfa.getSequenceCount(); i++){
+			xmfa.addGenomeAttribute(xmfa.getGenomeBySourceIndex(i), model.incoming[i]);
+		}
+		
+		return model;
+	}
+
+	protected static WeakArgDataModel readWargXmlBzip2(FileInputStream fis, XmfaViewerModel xmfa) throws FileNotFoundException, IOException, SAXException
+	{
+		WeakArgDataModel model = new WeakArgDataModel(xmfa);
+		BufferedInputStream bis = new BufferedInputStream(fis);
+		CBZip2InputStream input = new CBZip2InputStream(bis);
+		
+		// now parse the XML
+		XMLReader xmlreader = null;
+		xmlreader = XMLReaderFactory.createXMLReader();
+		WeakArgXmlHandler handler = new WeakArgXmlHandler(model, xmfa);
+		xmlreader.setContentHandler(handler);		
+		xmlreader.parse(new InputSource(input));
+		
+		handler.summarize();
+		// now take the summaries from the xml handler and create objects
+		for(int i=0; i < xmfa.getSequenceCount(); i++){
+			ZoomHistogram zh = new ZoomHistogram(xmfa.getGenomeBySourceIndex(i));
+			zh.setGenomeLevelData(toByteArray(handler.inEdgeTally[i]));
+			model.incoming[i] = zh;
+
+			ZoomHistogram zhout = new ZoomHistogram(xmfa.getGenomeBySourceIndex(i));
+			zhout.setGenomeLevelData(toByteArray(handler.outEdgeTally[i]));
+			model.outgoing[i] = zhout;
+		}
+		xmfa.setDrawAttributes(true);
+		return model;
+	}
+	
+	private static byte[] toByteArray(int[] data)
+	{
+		byte[] output=new byte[data.length];
+		for(int i=0; i<data.length; i++)
+			output[i]=(byte)data[i];
+		return output;
+	}
+	
+	protected static WeakArgDataModel readStoredData(File f) throws IOException
+	{
+		WeakArgDataModel model;
+    	ObjectInputStream cache_instream = new ObjectInputStream(new FileInputStream(f));
+    	try{
+    		model = (WeakArgDataModel)cache_instream.readObject();
+    	}catch(ClassNotFoundException cnfe)
+    	{
+    		throw new RuntimeException("Error reading stored data " + f);
+    	}
+    	return model;
+	}
+
+	/**
+	 * Class to handle XML parse events from weak arg xml
+	 * @author koadman
+	 */
+	static class WeakArgXmlHandler implements ContentHandler
+	{
+		WeakArgDataModel model;
+		XmfaViewerModel xmfa;
+		String curElement;
+		String curNameMap;
+		String curBlocks;
+		
+		int start, end, eFrom, eTo;
+		double aFrom, aTo;
+		int curblock = -1;
+
+		// temporary storage for parsing
+		int[][] inEdgeTally;
+		int[][] outEdgeTally;		
+		byte[][] regionalColors;
+		long[] seq_coords;
+		boolean[] gap;
+		
+		int iterations;
+
+		
+		
+		WeakArgXmlHandler(WeakArgDataModel model, XmfaViewerModel xmfa)
+		{
+			this.model = model;
+			this.xmfa = xmfa;
+			inEdgeTally = new int[2*xmfa.getSequenceCount()-1][];
+			outEdgeTally = new int[2*xmfa.getSequenceCount()-1][];
+			Vector<Genome> genomes = xmfa.getGenomes();
+			for(int i=0; i<xmfa.getSequenceCount(); i++){
+				inEdgeTally[i] = new int[(int)(genomes.elementAt(i).getLength())];
+				outEdgeTally[i] = new int[(int)(genomes.elementAt(i).getLength())];
+			}
+			seq_coords = new long[xmfa.getSequenceCount()];
+			gap = new boolean[xmfa.getSequenceCount()];
+		}
+
+		public void characters(char[] ch, int start, int length)
+				throws SAXException {
+			if(curElement==null)	return;
+			try{
+			if(curElement.equals("start")){				
+				this.start = Integer.parseInt(new String(ch, start, length));
+			}else 
+			if(curElement.equals("end")){
+				end = Integer.parseInt(new String(ch, start, length));
+			}else 
+			if(curElement.equals("efrom")){
+				eFrom = Integer.parseInt(new String(ch, start, length));
+			}else 
+			if(curElement.equals("eto")){
+				eTo = Integer.parseInt(new String(ch, start, length));
+			}else 
+			if(curElement.equals("afrom")){
+				aFrom = Double.parseDouble(new String(ch, start, length));
+			}else 
+			if(curElement.equals("ato")){
+				aTo = Double.parseDouble(new String(ch, start, length));
+			}else 
+			if(curElement.equals("Tree")){
+				if(model.treeString==null)
+					model.treeString = new String(ch, start, length);
+				else{
+//					if(!model.treeString.equals(new String(ch))){
+//						throw new RuntimeException("Can not understand weak ARG data with non-constant tree");
+//					}
+				}
+			}else if(curElement.equals("Blocks")){
+				curBlocks = new String(ch, start, length);
+			}else if(curElement.equals("nameMap")){
+				curNameMap = new String(ch, start, length);
+			}
+			}catch(NumberFormatException nfe){
+				System.err.println("Number format exception!");
+			}
+			curElement = null;
+		}
+		
+		public void endElement(String uri, String localName, String qName) throws SAXException 
+		{
+			if(qName.equals("recedge"))
+			{
+				// finished a recedge.  record it.
+				if(eTo<xmfa.getSequenceCount()){
+					recordRecEdge(inEdgeTally, eTo);
+				}
+
+				if(eFrom<xmfa.getSequenceCount()){
+					recordRecEdge(outEdgeTally, eFrom);
+				}
+			}
+		}
+
+		private void recordRecEdge(int[][] tally, int genome)
+		{
+			xmfa.getColumnCoordinates(curblock, start, seq_coords, gap);
+			int s = (int)seq_coords[genome];
+			xmfa.getColumnCoordinates(curblock, end, seq_coords, gap);
+			int e = (int)seq_coords[genome];
+			s = xmfa.getGenomeBySourceIndex(genome).getLength() < s ? (int)(xmfa.getGenomeBySourceIndex(genome).getLength()) : s;
+			e = xmfa.getGenomeBySourceIndex(genome).getLength() < e ? (int)(xmfa.getGenomeBySourceIndex(genome).getLength()) : e;
+			if(s>e){
+				int tmp = s;
+				s = e;
+				e = tmp;
+			}
+			for(int i=s; i<e; i++){
+				tally[genome][i]++;
+			}
+		}
+
+		public void endDocument() throws SAXException {}
+		public void endPrefixMapping(String prefix) throws SAXException {}
+		public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {}
+		public void processingInstruction(String target, String data) throws SAXException {}
+		public void setDocumentLocator(Locator locator) {}
+		public void skippedEntity(String name) throws SAXException {}
+		public void startDocument() throws SAXException {}
+		public void startPrefixMapping(String prefix, String uri) throws SAXException {}
+
+		public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException 
+		{
+			curElement = qName;
+			if(curElement.equals("Blocks")){
+				curblock++;
+			}else if(curElement.equals("Iteration")){
+				iterations++;
+			}
+		}
+
+		/*
+		 * normalizes a posterior sample to a probability distribution
+		 */
+		public void normalize(int[][] data){
+			float norm = iterations / curblock;
+			for(int i=0; i<data.length; i++)
+			{
+				if(data[i]==null)
+					continue;
+				for(int j=0; j<data[i].length; j++)
+				{
+					float f = data[i][j];
+					f /= norm;
+					f -= 0.5;
+					f *= 255;
+					data[i][j] = f < -127 ? -128 : (byte)f;
+				}
+			}
+		}
+
+		public void summarize(){
+			normalize(inEdgeTally);
+			normalize(outEdgeTally);
+		}
+
+	}
+}
diff --git a/src/org/gel/mauve/remote/MauveDisplayCommunicator.java b/src/org/gel/mauve/remote/MauveDisplayCommunicator.java
new file mode 100644
index 0000000..821f643
--- /dev/null
+++ b/src/org/gel/mauve/remote/MauveDisplayCommunicator.java
@@ -0,0 +1,17 @@
+package org.gel.mauve.remote;
+
+import org.freedesktop.dbus.DBusConnection;
+import org.gel.mauve.XmfaViewerModel;
+
+public class MauveDisplayCommunicator {
+    DBusConnection bus = null;
+    public MauveDisplayCommunicator(XmfaViewerModel model) throws org.freedesktop.dbus.exceptions.DBusException {
+    	
+    	int tmp = DBusConnection.SESSION;
+    	
+        bus = DBusConnection.getConnection(tmp);
+        bus.requestBusName("org.gel.mauve.remote.MauveInterface");
+        bus.exportObject("/MauveInterface", new MauveInterfaceImpl(model));
+    }
+
+}
diff --git a/src/org/gel/mauve/remote/MauveInterface.java b/src/org/gel/mauve/remote/MauveInterface.java
new file mode 100644
index 0000000..667e741
--- /dev/null
+++ b/src/org/gel/mauve/remote/MauveInterface.java
@@ -0,0 +1,14 @@
+package org.gel.mauve.remote;
+
+import org.freedesktop.dbus.DBusInterface;
+
+public interface MauveInterface extends DBusInterface {
+	public void setGenomeOrder(int[] order);
+	public void hackOrder();
+	public void setDisplayCoordinate(int genome, long coordinate);
+	public void setDisplayRange(int genome, long left, long right);
+	public void setDisplayBlockAndColumn(int block, long left_column, long right_column);
+	// returns block ID and column coordinate where the mouse is located
+	public int getMouseBlock();
+	public long getMouseColumn();
+}
diff --git a/src/org/gel/mauve/remote/MauveInterfaceImpl.java b/src/org/gel/mauve/remote/MauveInterfaceImpl.java
new file mode 100644
index 0000000..e5db04e
--- /dev/null
+++ b/src/org/gel/mauve/remote/MauveInterfaceImpl.java
@@ -0,0 +1,75 @@
+package org.gel.mauve.remote;
+
+import org.gel.mauve.Genome;
+import org.gel.mauve.XmfaViewerModel;
+
+public class MauveInterfaceImpl implements MauveInterface {
+	XmfaViewerModel model;
+	MauveInterfaceImpl(XmfaViewerModel model){
+		this.model = model;
+	}
+
+	public void setGenomeOrder(int[] order){
+		model.reorderSequences(order);
+	}
+	public void hackOrder(){
+		int ord[] = {13,21,10,16,28,22,23,15,20,17,0,5,27,25,26,24,14,1,2,4,18,9,19,7,3,11,12,6,8};
+		model.reorderSequences(ord);
+	}
+	
+	public void setDisplayCoordinate(int genome, long coordinate) {
+		model.alignView(model.getGenomeBySourceIndex(genome), coordinate);
+	}
+
+	public void setDisplayRange(int genome, long left, long right) {
+		Genome g = model.getGenomeBySourceIndex(genome);
+		long len = (right-left) * 5;
+		int zoom = (int) (100 * g.getViewLength () / (double) len);
+		long center = left + ((right - left) / 2);
+		model.zoomAndCenter (g, zoom, center);
+	}
+
+	public void setDisplayBlockAndColumn(int block, long left_column, long right_column)
+	{
+		System.err.println("lc " + left_column + " rc " + right_column);
+		long[] seq_coords = new long[model.getSequenceCount()];
+		boolean[] gap = new boolean[model.getSequenceCount()];
+		long[] rseq_coords = new long[model.getSequenceCount()];
+		boolean[] rgap = new boolean[model.getSequenceCount()];
+		model.getColumnCoordinates(block, left_column, seq_coords, gap);
+		model.getColumnCoordinates(block, right_column, rseq_coords, rgap);
+		for(int gI=0; gI < gap.length; gI++){
+			if(!gap[gI]){
+				long left = seq_coords[gI] < rseq_coords[gI] ? seq_coords[gI] : rseq_coords[gI];
+				long right = seq_coords[gI] > rseq_coords[gI] ? seq_coords[gI] : rseq_coords[gI];				
+				Genome g = model.getGenomeBySourceIndex(gI);
+				long len = (right-left) * 5;
+				int zoom = (int) (100 * g.getViewLength () / (double) len);
+				long center = left + ((right - left) / 2);
+				System.err.println("zoom " + zoom + " center " + center);
+				model.zoomAndCenter (g, 0, center);
+				model.zoomAndCenter (g, zoom, center);
+				model.alignView(g, center);
+				break;
+			}
+		}
+	}
+
+	public boolean isRemote() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public int getMouseBlock(){
+		long coord = model.getHighlightCoordinate();
+		Genome g = model.getHighlightGenome();
+		long[] l = model.getLCBAndColumn(g, coord);
+		return (int)l[0];
+	}
+	public long getMouseColumn(){
+		long coord = model.getHighlightCoordinate();
+		Genome g = model.getHighlightGenome();
+		long[] l = model.getLCBAndColumn(g, coord);
+		return l[1];
+	}
+}
diff --git a/src/org/gel/mauve/remote/RemoteApplet.java b/src/org/gel/mauve/remote/RemoteApplet.java
new file mode 100644
index 0000000..82d274d
--- /dev/null
+++ b/src/org/gel/mauve/remote/RemoteApplet.java
@@ -0,0 +1,182 @@
+package org.gel.mauve.remote;
+
+import java.applet.Applet;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.rmi.AccessException;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class RemoteApplet extends Applet {
+	public final static int REMOTE_PORT = 32179;
+
+	RemoteControl obj = null;
+
+	private void waitForObj (final String alignmentID, final String sequenceID,
+			final long start, final long end, final String auth_token, final String contig) {
+		Thread t = new Thread (new Runnable () {
+
+			public void run () {
+				int counter = 0;
+				while (true) {
+					try {
+						Thread.sleep (1000);
+						counter++;
+						if (counter == 600) {
+							System.err.println ("Giving up after ten minutes.");
+							break;
+						}
+
+						try {
+							initObj ();
+							break;
+						} catch (RemoteException e2) {
+							// Ignored, we're waiting for the remote server to
+							// become available.
+						} catch (NotBoundException e2) {
+							// Ignored, we're waiting for the remote server to
+							// become available.
+						} catch (Exception e2) {
+							e2.printStackTrace ();
+							return;
+						}
+					} catch (InterruptedException e1) {
+						e1.printStackTrace ();
+						return;
+					}
+				}
+
+				if (obj != null) {
+					Object retval = AccessController
+							.doPrivileged (new PrivilegedAction () {
+
+								public Object run () {
+									try {
+										obj.setFocus (alignmentID, sequenceID,
+												start, end, auth_token, contig);
+									} catch (RemoteException e) {
+										return e;
+									}
+									return null;
+								}
+							});
+
+					if (retval != null) {
+						((RemoteException) retval).printStackTrace ();
+					}
+
+					repaint ();
+				}
+
+			}
+		});
+		t.start ();
+	}
+	
+	public void goTo (final String alignmentID, final String sequenceID,
+			final long start, final long end, final String auth_token) {
+		goTo (alignmentID, sequenceID, start, end, auth_token, null);
+	}
+
+	public void goTo (final String alignmentID, final String sequenceID,
+			final long start, final long end, final String auth_token, final String contig) {
+		if (obj == null) {
+			try {
+				initObj ();
+				Object retval = AccessController
+						.doPrivileged (new PrivilegedAction () {
+
+							public Object run () {
+								try {
+									obj.setFocus (alignmentID, sequenceID,
+											start, end, auth_token, contig);
+								} catch (RemoteException e) {
+									return e;
+								}
+								return null;
+							}
+						});
+
+				if (retval != null) {
+					startApplication ();
+					waitForObj (alignmentID, sequenceID, start, end, auth_token, contig);
+				}
+			} catch (AccessException e) {
+				e.printStackTrace ();
+			} catch (RemoteException e) {
+				startApplication ();
+				waitForObj (alignmentID, sequenceID, start, end, auth_token, contig);
+			} catch (NotBoundException e) {
+				startApplication ();
+				waitForObj (alignmentID, sequenceID, start, end, auth_token, contig);
+			}
+		} else {
+			Object retval = AccessController
+					.doPrivileged (new PrivilegedAction () {
+
+						public Object run () {
+							try {
+								obj.setFocus (alignmentID, sequenceID, start,
+										end, auth_token, contig);
+							} catch (RemoteException e) {
+								return e;
+							}
+							return null;
+						}
+					});
+
+			if (retval != null) {
+				startApplication ();
+				waitForObj (alignmentID, sequenceID, start, end, auth_token, contig);
+			}
+		}
+	}
+
+	private void initObj () throws AccessException, RemoteException,
+			NotBoundException {
+		Object retval = AccessController.doPrivileged (new PrivilegedAction () {
+			public Object run () {
+				Registry r;
+				try {
+					r = LocateRegistry.getRegistry (REMOTE_PORT);
+					return r.lookup ("MauveRemote");
+				} catch (AccessException e) {
+					return e;
+				} catch (RemoteException e) {
+					return e;
+				} catch (NotBoundException e) {
+					return e;
+				}
+			}
+		});
+
+		if (retval instanceof AccessException)
+			throw (AccessException) retval;
+		if (retval instanceof RemoteException)
+			throw (RemoteException) retval;
+		if (retval instanceof NotBoundException)
+			throw (NotBoundException) retval;
+		if (retval instanceof RemoteControl)
+			obj = (RemoteControl) retval;
+	}
+
+	private void startApplication () {
+		Thread t = new Thread (new Runnable () {
+			public void run () {
+				try {
+					// Try to JWS it.
+					getAppletContext ().showDocument (
+							new URL (getCodeBase () + "/mauve.jnlp"));
+				} catch (MalformedURLException e1) {
+					e1.printStackTrace ();
+					return;
+				}
+			}
+		});
+		t.start ();
+	}
+}
diff --git a/src/org/gel/mauve/remote/RemoteControl.java b/src/org/gel/mauve/remote/RemoteControl.java
new file mode 100644
index 0000000..d6b8acb
--- /dev/null
+++ b/src/org/gel/mauve/remote/RemoteControl.java
@@ -0,0 +1,9 @@
+package org.gel.mauve.remote;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteControl extends Remote {
+	public void setFocus (String alignID, String sequenceID, long start,
+			long end, String auth_token, String contig) throws RemoteException;
+}
diff --git a/src/org/gel/mauve/remote/RemoteControlImpl.java b/src/org/gel/mauve/remote/RemoteControlImpl.java
new file mode 100644
index 0000000..b7a06a7
--- /dev/null
+++ b/src/org/gel/mauve/remote/RemoteControlImpl.java
@@ -0,0 +1,38 @@
+package org.gel.mauve.remote;
+
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+
+import org.gel.mauve.gui.Mauve;
+
+public class RemoteControlImpl extends UnicastRemoteObject implements
+		RemoteControl {
+	Mauve app;
+
+	public RemoteControlImpl (Mauve app) throws RemoteException {
+		super ();
+		this.app = app;
+	}
+
+	public void setFocus (String alignID, String sequenceID, long start,
+			long end, String auth_token, String contig) throws RemoteException {
+		app.setFocus (alignID, sequenceID, start, end, auth_token, contig);
+	}
+
+	public static void startRemote (Mauve app) {
+		try {
+			Registry r = LocateRegistry
+					.createRegistry (RemoteApplet.REMOTE_PORT);
+			RemoteControlImpl obj = new RemoteControlImpl (app);
+			r.rebind ("MauveRemote", obj);
+
+		} catch (Exception e) {
+			System.err.println ("Error creating registry and binding object.");
+			e.printStackTrace ();
+			return;
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/remote/WargDisplayCommunicator.java b/src/org/gel/mauve/remote/WargDisplayCommunicator.java
new file mode 100644
index 0000000..ba8c072
--- /dev/null
+++ b/src/org/gel/mauve/remote/WargDisplayCommunicator.java
@@ -0,0 +1,32 @@
+package org.gel.mauve.remote;
+import org.freedesktop.dbus.DBusConnection;
+import org.gel.mauve.Genome;
+import org.gel.mauve.HighlightListener;
+import org.gel.mauve.ModelEvent;
+import org.gel.mauve.XmfaViewerModel;
+
+public class WargDisplayCommunicator implements HighlightListener {
+    DBusConnection bus = null;
+    WargInterface warg;
+	public WargDisplayCommunicator(XmfaViewerModel model) {
+		try{
+        bus = DBusConnection.getConnection(DBusConnection.SESSION);
+        warg = bus.getRemoteObject("org.gel.mauve.remote.WargInterface", "/weakarg", WargInterface.class);
+        model.addHighlightListener(this);
+		}catch(org.freedesktop.dbus.exceptions.DBusException dbe){}
+//        bus.disconnect();
+	}
+
+	public void highlightChanged(ModelEvent me){
+		XmfaViewerModel model = (XmfaViewerModel)me.getSource();
+		long coord = model.getHighlightCoordinate();
+		Genome g = model.getHighlightGenome();
+		long[] l = model.getLCBAndColumn(g, coord);
+		try{
+		warg.setViewingSite((int)l[0],l[1]);
+		}catch(Exception e){
+			// the connection may have failed/broken/etc.  deregister from listening to save cpu
+	        model.removeHighlightListener(this);
+		}
+	}
+}
diff --git a/src/org/gel/mauve/remote/WargInterface.java b/src/org/gel/mauve/remote/WargInterface.java
new file mode 100644
index 0000000..0b1779a
--- /dev/null
+++ b/src/org/gel/mauve/remote/WargInterface.java
@@ -0,0 +1,8 @@
+package org.gel.mauve.remote;
+
+import org.freedesktop.dbus.DBusInterface;
+
+public interface WargInterface extends DBusInterface {
+	int getViewingSite();
+	void setViewingSite(int block, long site);
+}
diff --git a/src/org/gel/mauve/summary/AnalysisModuleFrame.java b/src/org/gel/mauve/summary/AnalysisModuleFrame.java
new file mode 100644
index 0000000..d3f0443
--- /dev/null
+++ b/src/org/gel/mauve/summary/AnalysisModuleFrame.java
@@ -0,0 +1,117 @@
+package org.gel.mauve.summary;
+
+import java.io.File;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.Chromosome;
+import org.gel.mauve.Genome;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.contigs.ContigHandler;
+import org.gel.mauve.contigs.DefaultContigHandler;
+import org.gel.mauve.gui.Mauve;
+import org.gel.mauve.gui.MauveFrame;
+import org.gel.mauve.gui.sequence.FlatFileFeatureConstants;
+import org.gel.mauve.summary.output.SegmentDataProcessor;
+
+public class AnalysisModuleFrame extends MauveFrame implements FlatFileFeatureConstants,
+		ContigHandler {
+	
+	protected DefaultContigHandler contig_handler;
+
+	public AnalysisModuleFrame (Mauve mauve) {
+		super (mauve);
+		
+	}
+	
+	public void setModel (BaseViewerModel model) {
+		try {
+			super.setModel (model);
+			contig_handler = new DefaultContigHandler (model);
+			final Hashtable args = new Hashtable ();
+			File file = model.getSrc ();
+			System.out.println ("name: " + file.getName ());
+			int end = file.getName ().lastIndexOf ('.');
+			if (end > -1)
+				file = new File (file.getParentFile (), 
+						file.getName ().substring (0, end));
+			System.out.println ("name: " + file.getName ());
+			args.put (ProcessBackboneFile.INPUT_FILE, file.toString ());
+			args.put (MODEL, model);
+			args.put (CONTIG_HANDLER, this);
+			Iterator itty = MauveInterfacer.feat_files.iterator (); 
+			while (itty.hasNext ()) {
+				Object [] data = (Object []) itty.next (); 
+				importer.importAnnotationFile ((File) data [0], 
+						model.getGenomeBySourceIndex (((Integer) data [1]).intValue ()));
+			}
+			long [] lengths = new long [model.getSequenceCount ()];
+			for (int i = 0; i < lengths.length; i++) {
+				lengths [i] = model.getGenomeBySourceIndex (i).getLength ();
+				System.out.println ("length: " + lengths [i]);
+			}
+			args.put (GENOME_LENGTHS, lengths);
+			new Thread (new Runnable () {
+				public void run () {
+					ProcessBackboneFile.startProcessor ((String) args.get (
+							ProcessBackboneFile.INPUT_FILE), args);
+				}
+			}).start ();
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+	
+	public long getContigCoord (int sequence, long loci) {
+		return contig_handler.getContigCoord(sequence, loci);
+	}
+	
+	public String getContigName (int sequence, long loci) {
+		return contig_handler.getContigName(sequence, loci);
+	}
+	
+	
+
+	public long getPseudoCoord(int sequence, long loci, String contig) {
+		return contig_handler.getPseudoCoord(sequence, loci, contig);
+	}
+
+	public void fixSegmentByContigs (int sequence, Segment segment) {
+		Genome genome = model.getGenomeBySourceIndex (sequence);
+		Chromosome one = genome.getChromosomeAt (segment.left [sequence]);
+		Chromosome end = genome.getChromosomeAt (segment.right [sequence]);
+		if (one != end && SegmentDataProcessor.multiplicityForGenome (
+				sequence, model.getSequenceCount ()) != segment.multiplicityType ()) {
+			System.out.println ("seg: " + segment);
+			return;
+		}
+		//System.out.println ("original: " + segment);
+		int part = 1;
+		while (one != end) {
+			Segment piece = new Segment (segment.left.length, true);
+			piece.left [sequence] = segment.left [sequence];
+			piece.right [sequence] = one.getEnd ();
+			segment.left [sequence] = one.getEnd () + 1;
+			piece.reverse [sequence] = segment.reverse [sequence];
+			if (segment.prevs [sequence] != null)
+				segment.prevs [sequence].nexts [sequence] = piece;
+			piece.prevs [sequence] = segment.prevs [sequence];
+			segment.prevs [sequence] = piece;
+			piece.nexts [sequence] = segment;
+			//System.out.println ("part" + part++ + ": " + piece);
+			//System.out.println ("part" + part + ": " + segment);
+			int ref = sequence + 1;
+			/*while (ref != sequence) {
+				if (segment.starts [ref] != 0) {
+					piece.starts [ref] = segment.starts [ref];
+					piece.lengths [ref] = 
+				}
+			}*/
+			one = genome.getChromosomeAt (one.getEnd () + 1);
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/MauveInterfacer.java b/src/org/gel/mauve/summary/MauveInterfacer.java
new file mode 100644
index 0000000..3d81e10
--- /dev/null
+++ b/src/org/gel/mauve/summary/MauveInterfacer.java
@@ -0,0 +1,60 @@
+package org.gel.mauve.summary;
+
+import java.io.File;
+import java.util.Hashtable;
+import java.util.LinkedList;
+
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.gui.Mauve;
+import org.gel.mauve.gui.MauveFrame;
+import org.gel.mauve.summary.output.SegmentDataProcessor;
+
+/**
+ * This class interfaces between Mauve's existing code and modules that may or
+ * may not become a part of Mauve, but require it's base classes to run
+ * 
+ * @author rissman
+ * 
+ */
+// currently, is being allowed to instantiate gui; easier to do than not,
+// and i don't know if it will be necessary or not
+
+/* This should not be instantiated the gui. This should not be here. */
+public class MauveInterfacer extends Mauve implements MauveConstants {
+
+	public static LinkedList feat_files = new LinkedList ();
+	/**
+	 * waits for parent class to finish init processing, and then extracts
+	 * necessary data
+	 * 
+	 * @param file
+	 *            The file containing alignment data
+	 */
+	public void init (String file) {
+		super.init (file);
+	}
+	
+	protected MauveFrame makeNewFrame () {
+		AnalysisModuleFrame frame = new AnalysisModuleFrame (this);
+		frames.add (frame);
+		return frame;
+	}
+	
+	public static void main (String [] args) {
+		try {
+			int ind = 1;
+			while (ind < args.length) {
+				Object [] data = new Object [2];
+				data [0] = new File (args [ind++]);
+				data [1] = new Integer (args [ind++]);
+				feat_files.add (data);
+			}
+			new MauveInterfacer ().init (args[0]);
+		} catch (ArrayIndexOutOfBoundsException e) {
+			System.out.println ("No filename given");
+			e.printStackTrace ();
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/ProcessBackboneFile.java b/src/org/gel/mauve/summary/ProcessBackboneFile.java
new file mode 100644
index 0000000..c9a73be
--- /dev/null
+++ b/src/org/gel/mauve/summary/ProcessBackboneFile.java
@@ -0,0 +1,183 @@
+package org.gel.mauve.summary;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.summary.output.SegmentDataProcessor;
+
+public class ProcessBackboneFile implements MauveConstants {
+
+	protected BufferedReader in;
+
+	protected String in_file;
+
+	protected StringTokenizer current_input;
+
+	protected Iterator sub_iterator;
+
+	protected String not_header;
+
+	public final static String INPUT_FILE = "input_file";
+
+	protected Vector backbone;
+
+	protected int count;
+
+	protected String [] current_row;
+
+	public ProcessBackboneFile (Hashtable args) {
+		init ((String) args.get (INPUT_FILE));
+	}
+	
+	public ProcessBackboneFile (String file) {
+		init (file);
+	}
+
+	protected void init (String file) {
+		try {
+			in_file = file;
+			backbone = new Vector ();
+			in = new BufferedReader (new FileReader (in_file));
+			loadData ();
+			in.close ();
+		} catch (Exception e) {
+			e.printStackTrace ();
+		}
+	}
+
+	protected void loadData () {
+		count = getSequenceCount ();
+		current_row = new String [count];
+		processFirstRow ();
+		int i = 0;
+		String row = null;
+		if (not_header != null) {
+			current_row[0] = not_header;
+			not_header = null;
+			i = 1;
+		} else {
+			try {
+				row = in.readLine ();
+				current_input = new StringTokenizer (row);
+			} catch (Exception e) {
+				System.out.println ("No data to print.");
+				e.printStackTrace ();
+			}
+		}
+		while (row != null) {
+			try {
+				for (; i < current_row.length; i++)
+					current_row[i] = current_input.nextToken ();
+				storeRow ();
+				i = 0;
+				row = in.readLine ();
+				if (row != null)
+					current_input = new StringTokenizer (row);
+			} catch (Exception e) {
+				e.printStackTrace ();
+				// System.exit(0);
+			}
+
+		}
+	}
+
+	protected void storeRow () {
+		Segment segment = new Segment (count, true);
+		for (int i = 0, j = 0; j < count; i++, j++) {
+			segment.left[j] = Long.parseLong (current_row[i]);
+			segment.right[j] = Long.parseLong (current_row[++i]);
+			if (segment.left[j] < 0) {
+				segment.left[j] *= -1;
+				segment.right[j] *= -1;
+				segment.reverse[j] = true;
+			}
+		}
+		backbone.add (segment);
+	}
+
+	protected int getSequenceCount () {
+		try {
+			current_input = new StringTokenizer (in.readLine ());
+			return current_input.countTokens ();
+		} catch (IOException e) {
+			System.out.println ("Couldn't read first line of file.");
+			e.printStackTrace ();
+			return -1;
+		}
+	}
+
+	protected void processFirstRow () {
+		Vector titles = null;
+		try {
+			StringTokenizer toke = current_input;
+			if (count % 2 == 1)
+				throw new IOException ("Odd number of column headers.");
+			String first = toke.nextToken ();
+			try {
+				Long.parseLong (first);
+				not_header = first;
+				current_input = toke;
+			} catch (NumberFormatException e) {
+				titles = new Vector (count);
+				boolean trim = first.indexOf ("_leftend") > -1;
+				for (int i = 0; i < count; i++) {
+					if (i != 0)
+						first = toke.nextToken ();
+					if (trim) {
+						if (i % 2 == 0)
+							titles.add (first
+									.substring (0, first.length () - 8));
+						else
+							titles.add (first
+									.substring (0, first.length () - 9));
+					} else
+						titles.add (first);
+				}
+			}
+		} catch (IOException e) {
+			System.out.println ("Can't read column headers");
+			e.printStackTrace ();
+			titles = null;
+		}
+		count /= 2;
+	}
+
+	public Vector getBackboneSegments () {
+		return backbone;
+	}
+	
+	public static void startProcessor (String file, Hashtable spec_args) {
+		try {
+			spec_args.put (INPUT_FILE, file + ".backbone");
+			System.out.println ("file: " + file);
+			spec_args.put (SegmentDataProcessor.FILE_STUB, file);
+			spec_args.put (SegmentDataProcessor.BACKBONE,
+					new ProcessBackboneFile (spec_args).getBackboneSegments ());
+			new SegmentDataProcessor (spec_args);
+		} catch (Exception e) {
+			System.out
+					.println ("Wrong arguments.  First should be the name of the backbone"
+							+ "file, second, the number of the sequence to sort by.");
+			e.printStackTrace ();
+		}
+	}
+
+	/**
+	 * Main.
+	 * 
+	 * @param args
+	 *            The backbone file to read and create islands from
+	 */
+	public static void main (String [] args) {
+		startProcessor ((String) args [0], new Hashtable ());
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/AbstractIslandWriter.java b/src/org/gel/mauve/summary/output/AbstractIslandWriter.java
new file mode 100644
index 0000000..90ba152
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/AbstractIslandWriter.java
@@ -0,0 +1,22 @@
+package org.gel.mauve.summary.output;
+
+public abstract class AbstractIslandWriter extends AbstractMatchDataWriter {
+
+	protected long multiplicity;
+
+	public AbstractIslandWriter (String file, SegmentDataProcessor proc) {
+		super (file, proc);
+		printIslands ();
+		doneWritingFile ();
+	}
+
+	public void printData () {
+		//if (by_genome) {
+			multiplicity = processor.multiplicityForGenome (seq_index);
+		//}
+		super.printData ();
+	}
+
+	abstract public void printIslands ();
+
+}
diff --git a/src/org/gel/mauve/summary/output/AbstractMatchDataWriter.java b/src/org/gel/mauve/summary/output/AbstractMatchDataWriter.java
new file mode 100644
index 0000000..16fe67b
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/AbstractMatchDataWriter.java
@@ -0,0 +1,185 @@
+package org.gel.mauve.summary.output;
+
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.analysis.SegmentComparator;
+import org.gel.mauve.contigs.ContigHandler;
+import org.gel.mauve.gui.sequence.FlatFileFeatureConstants;
+
+abstract public class AbstractMatchDataWriter extends AbstractTabbedDataWriter
+		implements FlatFileFeatureConstants, ContigHandler {
+
+	protected boolean shouldPrintRow (int row) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	protected int count;
+
+	protected int island_min;
+
+	protected int backbone_min;
+
+	protected double max_length_ratio;
+
+	protected int reference = -1;
+
+	protected long all_seq_multiplicity;
+
+	protected Segment [] firsts;
+
+	protected Segment current;
+
+	protected int seq_index;
+
+	protected Vector backbone;
+
+	public SegmentDataProcessor processor;
+
+	protected boolean by_genome;
+
+	public static final String AVERAGE_LENGTH = "avg_lngth";
+
+	protected ContigHandler contig_handler;
+	
+	protected long offset;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param model
+	 *            reference to mauve data
+	 * @param file_name
+	 *            name of file to output to
+	 */
+	public AbstractMatchDataWriter (String add, SegmentDataProcessor proc) {
+		super (((String) proc.get (FILE_STUB)) + "_" + add + ".tab", proc);
+	}
+
+	protected void initSubClassParticulars (Hashtable args) {
+		offset = -1;
+		processor = (SegmentDataProcessor) args;
+		island_min = DEFAULT_ISLAND_MIN;
+		backbone_min = DEFAULT_BACKBONE_MIN;
+		max_length_ratio = DEFAULT_MAX_LENGTH_RATIO;
+		current = Segment.END;
+		if (args.get (ISLAND_MIN) != null)
+			island_min = ((Integer) args.get (ISLAND_MIN)).intValue ();
+		else
+			args.put (ISLAND_MIN, new Integer (island_min));
+		if (args.get (BACKBONE_MIN) != null)
+			backbone_min = ((Integer) args.get (BACKBONE_MIN)).intValue ();
+		else
+			args.put (BACKBONE_MIN, new Integer (backbone_min));
+		if (args.get (MAX_LENGTH_RATIO) != null)
+			max_length_ratio = ((Double) args.get (MAX_LENGTH_RATIO)).doubleValue ();
+		else
+			args.put (MAX_LENGTH_RATIO, new Double (max_length_ratio));
+		if (args.get (CONTIG_HANDLER) != null)
+			contig_handler = (ContigHandler) args.get (CONTIG_HANDLER);
+		else
+			contig_handler = this;
+		backbone = (Vector) args.get (BACKBONE);
+		reference = processor.reference;
+		Collections.sort (backbone, new SegmentComparator (
+				SegmentComparator.BY_MULTIPLICITY, true));
+		firsts = (Segment []) args.get (FIRSTS);
+		count = firsts.length;
+		super.initSubClassParticulars (args);
+		all_seq_multiplicity = ((Long) processor.get (ALL_MULTIPLICITY))
+				.longValue ();
+	}
+
+	protected void printData (int which) {
+		if ((which & BY_ONE_GENOME) == BY_ONE_GENOME) {
+			by_genome = true;
+			current = firsts[seq_index];
+			printData ();
+		}
+		if ((which & BY_GENOMES) == BY_GENOMES) {
+			by_genome = true;
+			for (int i = 0; i < count; i++) {
+				seq_index = i;
+				current = firsts[i];
+				printData ();
+			}
+		}
+		if ((which & BY_BB_LIST) == BY_BB_LIST) {
+			by_genome = false;
+			row_number = 0;
+			current = (Segment) backbone.get (0);
+			printData ();
+		}
+	}
+
+	protected boolean moreRowsToPrint () {
+		if (by_genome) {
+			current = current.nexts[seq_index];
+			return !(current == Segment.END);
+		} else if (row_number < backbone.size ()) {
+			current = (Segment) backbone.get (row_number);
+			return true;
+		} else
+			return false;
+	}
+
+	protected String getData (int column, int row) {
+		long value = 0;
+		int divisor = contig_handler instanceof AbstractMatchDataWriter ? 2 : 3;
+		int seq = column / divisor;
+		if (column % divisor == 2)
+			return contig_handler.getContigName (seq, current.left [seq]);
+		else if (column % divisor == 0) {
+			value = current.left[seq];
+
+		}
+		else {
+			value = current.right[seq];
+		}
+		value = adjustForContigs (seq, value);
+		if (current.reverse [seq])
+			value -= value * 2;
+		return value + "";
+	}
+	
+	public long adjustForContigs (int sequence, long value) {
+		if (offset == -1) {
+			offset = value - contig_handler.getContigCoord (sequence, value);
+			value -= offset;
+		}
+		else {
+			value -= offset;
+			offset = -1;
+		}
+		return value;
+	}
+
+	public Vector setColumnHeaders () {
+		Vector titles = null;
+		if (processor.contains (DEFAULT_TITLES))
+			titles = (Vector) processor.get (DEFAULT_TITLES);
+		if (titles == null)
+			titles = processor.makeDefaultStartEndColumnHeaders ();
+		return titles;
+	}
+	
+	public long getContigCoord (int sequence, long loci) {
+		return loci;
+	}
+	
+	public long getPseudoCoord (int sequence, long loci, String contig) {
+		return loci;
+	}
+
+	public String getContigName (int sequence, long loci) {
+		return MauveConstants.DEFAULT_CONTIG;
+	}
+	
+	public void fixSegmentByContigs (int sequence, Segment segment) {
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/AbstractTabbedDataWriter.java b/src/org/gel/mauve/summary/output/AbstractTabbedDataWriter.java
new file mode 100644
index 0000000..be4c91d
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/AbstractTabbedDataWriter.java
@@ -0,0 +1,198 @@
+package org.gel.mauve.summary.output;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * Used to provide base functionality to print files largely made up of rows of
+ * tab- separated data. Provides loose structure for getting data based on row
+ * and column numbers. The row and column information is used only when printing
+ * data rows, as done through printDataRow (). All free-form data can be written
+ * in between rows of formatted data. Row and column information can be reset at
+ * the convenience of the implementing classes
+ * 
+ * @author Anna I Rissman
+ * 
+ */
+public abstract class AbstractTabbedDataWriter {
+
+	/**
+	 * writer used to print
+	 */
+	protected PrintStream out;
+
+	/**
+	 * represents the file name (and path) the output stream writes to
+	 */
+	protected String file_name;
+
+	/**
+	 * Contains the header for each column. The headers can be printed on demand
+	 * and are also used to determine what data from the match to display in
+	 * each column. Array indeces match column indeces.
+	 */
+	protected String [] headers;
+
+	/**
+	 * acts as a buffer for a row of information
+	 */
+	protected String [] current_row;
+
+	/**
+	 * Is meant for sub-classes as an identifier to what information should be
+	 * printed. Is incremented in printDataRow, but can be reset without harm by
+	 * sub-classes.
+	 */
+	protected int row_number;
+
+	/**
+	 * Provides basic structure for outputting data from
+	 * 
+	 * @param mod
+	 *            reference to mauve data
+	 * @param file_name
+	 *            The name of the file that should be output
+	 */
+	public AbstractTabbedDataWriter (String file, Hashtable args) {
+		try {
+			// TODO if was being official, should check if already exists, path
+			// valid,
+			// etc, and give appropriate messages
+			file_name = file;
+			out = new PrintStream (new BufferedOutputStream (
+					new FileOutputStream (file_name)));
+			initSubClassParticulars (args);
+			printHeaderInfoForFile ();
+		} catch (Exception e) {
+			System.out.println ("Can't open file");
+			e.printStackTrace ();
+		}
+	}
+
+	// TODO write a constructor that takes in a bufferedwriter and a file,
+	// in case it has already be created by something else.
+
+	/**
+	 * Prints a row of data to the output file, each field tab separated
+	 * 
+	 * @param data
+	 *            Array of fields to print. Must be the same length as the
+	 *            header array.
+	 */
+	public void printRow (String [] data) {
+		try {
+			for (int i = 0; i < data.length - 1; i++) {
+				out.print (data[i]);
+				out.print ("\t");
+			}
+			out.println (data[data.length - 1]);
+		} catch (Exception e) {
+			System.out.println ("couldn't print row: ");
+			System.out.println (data);
+		}
+	}
+
+	/**
+	 * Prints the headers
+	 */
+	public void printHeaders () {
+		if (headers != null && headers.length > 0)
+			printRow (headers);
+	}
+
+	/**
+	 * Gathers and prints a row of data
+	 * 
+	 */
+	public void printDataRow () {
+		if (shouldPrintRow (row_number)) {
+			for (int i = 0; i < headers.length; i++) {
+				current_row[i] = getData (i, row_number);
+			}
+			printRow (current_row);
+		}
+		row_number++;
+	}
+
+	public void printData () {
+		do {
+			printDataRow ();
+		} while (moreRowsToPrint ());
+	}
+
+	/**
+	 * sets how many columns there are and what their headers should be
+	 * 
+	 * @param columns
+	 *            An array of strings representing column names
+	 */
+	public void setColumnHeaders (Vector columns) {
+		headers = (String []) columns.toArray (new String [columns.size ()]);
+		current_row = new String [headers.length];
+	}
+
+	public String [] getColumnHeaders () {
+		return (String []) headers.clone ();
+	}
+
+	public void doneWritingFile () {
+		System.out.println ("done: " + file_name);
+		out.println ();
+		out.flush ();
+		out.close ();
+	}
+
+	/**
+	 * Convenience method for inheriting classes to set variables necessary from
+	 * the constructor.
+	 * 
+	 * @param args
+	 *            Contains objects necessary to successfully initialize a
+	 *            subclass. NOTE: can be null value
+	 */
+	protected void initSubClassParticulars (Hashtable args) {
+		setColumnHeaders (setColumnHeaders ());
+	}
+
+	/**
+	 * convenience method if there is data that should automatically be printed
+	 * at the beginning of a file
+	 * 
+	 */
+	public void printHeaderInfoForFile () {
+	}
+
+	/**
+	 * Returns the data that should be printed in the specified row and column.
+	 * It is up to sub-classes to track where to get the data from, as the
+	 * format is not intended to be standardized.
+	 * 
+	 * @param column
+	 *            The header representing which column this data is for
+	 * @param row
+	 *            An int that may be useful for identifying what data is
+	 *            desired. It gets incremented every time printDataRow () is
+	 *            called.
+	 */
+	abstract protected String getData (int column, int row);
+
+	/**
+	 * called from printData. Used to determine when to stop attempting to print
+	 * rows.
+	 * 
+	 * @return True if there is more tabbed data to print, false otherwise
+	 */
+	abstract protected boolean moreRowsToPrint ();
+
+	/**
+	 * Gets the names of the columns
+	 * 
+	 */
+	abstract protected Vector setColumnHeaders ();
+
+	abstract protected boolean shouldPrintRow (int row);
+
+}
diff --git a/src/org/gel/mauve/summary/output/AlignedSequenceWriter.java b/src/org/gel/mauve/summary/output/AlignedSequenceWriter.java
new file mode 100644
index 0000000..35a3113
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/AlignedSequenceWriter.java
@@ -0,0 +1,124 @@
+package org.gel.mauve.summary.output;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.Vector;
+
+import org.biojava.bio.seq.io.StreamWriter;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.XmfaViewerModel;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.analysis.SegmentComparator;
+import org.gel.mauve.backbone.BackboneListBuilder;
+import org.gel.mauve.gui.MauveFrame;
+import org.gel.mauve.module.MauveModule;
+import org.gel.mauve.module.ModuleListener;
+import org.gel.mauve.summary.ProcessBackboneFile;
+
+/**
+ * writes out fastas for each genome containing only those sections that aligned to
+ * a part of the specified genome.
+ * 
+ * @author rissman
+ *
+ */
+public class AlignedSequenceWriter implements ModuleListener {
+	
+	/**
+	 * index of the genome written sequence must align to
+	 */
+	protected int genome_ind;
+	
+	/**
+	 * reference to model that describes alignment
+	 */
+	XmfaViewerModel model;
+	
+	/**
+	 * the directory to output the fastas to
+	 */
+	String directory;
+	
+	Vector backbone;
+	/**
+	 * sets class variables and starts write process
+	 * @param args
+	 */
+	public AlignedSequenceWriter (String [] args) {
+		genome_ind = Integer.parseInt(args [1]);
+		directory = MauveHelperFunctions.getRootDirectory(model).getAbsolutePath() +
+				File.pathSeparator + model.getGenomeBySourceIndex(genome_ind).getDisplayName()
+				+ "_aligned_fastas";
+	}
+
+	/**
+	 * called when MauveFrame and model are instantiated, starts write process.
+	 */
+	public void startModule(MauveFrame frame) {
+		model = (XmfaViewerModel) frame.getModel ();
+		backbone = new ProcessBackboneFile (BackboneListBuilder.getFileByKey(model, 
+				model.getXmfa(),"BackboneFile").getAbsolutePath()).getBackboneSegments();
+		pareContigs ();
+		for (int i = 0; i < model.getSequenceCount(); i++) {
+			SegmentComparator comp = new SegmentComparator (i);
+			Collections.sort(backbone, comp);
+			writeFasta (i);
+		}
+
+	}
+	
+	/**
+	 * removes the part of backbone segments that are too close to the end of a contig in one
+	 * of the genomes
+	 *
+	 */
+	protected void pareContigs () {
+		for (int g = 0; g < model.getSequenceCount (); g++) {
+			for (int i = 0; i < backbone.size (); i++) {
+				Segment seg = (Segment) backbone.get(i);
+				if (useSegment (seg, g)) {
+					
+				}
+			}
+		}
+	}
+	
+	public boolean useSegment (Segment seg, int index) {
+		return seg.left [index] != 0 && seg.left [genome_ind] != 0;
+	}
+	
+	/**
+	 * writes a Fasta for a genome including only those segments aligned to a part of
+	 * the genome specified by class variable genome_ind.
+	 * 
+	 * @param index			the index of the genome whose sequence should be printed
+	 */
+	public void writeFasta (int index) {
+		try {
+			PrintStream out = new PrintStream(new FileOutputStream(new File(
+					directory, model.getGenomeBySourceIndex(index)
+							.getDisplayName() + "_cut.fas")));
+			for (int i = 0; i < backbone.size (); i++) {
+				Segment segment = (Segment) backbone.get (i);
+				if (useSegment (segment, index)) {
+					
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}		
+	}
+
+	/**
+	 * First arg should be name of alignment file, second the genome of interest
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		new MauveModule (new AlignedSequenceWriter (args)).init (args [0]);
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/BackboneCompareFile.java b/src/org/gel/mauve/summary/output/BackboneCompareFile.java
new file mode 100644
index 0000000..749f845
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/BackboneCompareFile.java
@@ -0,0 +1,37 @@
+package org.gel.mauve.summary.output;
+
+import org.gel.mauve.BaseViewerModel;
+
+/**
+ * outputs data from Match objects stored in Genome objects in Mauve in a format
+ * compatible with the .backbone file to see if the two match. written 2/2/07 on
+ * Mauve 2.0. We're not currently sure how matches as displayed and as in the
+ * backbone file match up, which is the purpose for this class.
+ * 
+ * @author Anna I Rissman
+ * 
+ */
+// not useful anymore; never fully necessary
+abstract public class BackboneCompareFile {// extends AbstractMatchDataWriter {
+
+	String file = "Match_Backbone_Compare_File.txt";
+
+	public BackboneCompareFile (BaseViewerModel model) {
+		// super (model, null);
+	}
+
+	protected void setColumnHeaders () {
+		// OutputHelperFunctions.writeGenomesWithIndeces (model, out);
+	}
+
+	protected int getReferenceSequence () {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	protected int getSequenceCount () {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/IslandCoordinateWriter.java b/src/org/gel/mauve/summary/output/IslandCoordinateWriter.java
new file mode 100644
index 0000000..28cc0f4
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/IslandCoordinateWriter.java
@@ -0,0 +1,55 @@
+package org.gel.mauve.summary.output;
+
+import java.util.Vector;
+
+import org.gel.mauve.MauveConstants;
+
+public class IslandCoordinateWriter extends AbstractIslandWriter implements
+		MauveConstants {
+
+	public IslandCoordinateWriter (SegmentDataProcessor processor) {
+		super ("islandcoords", processor);
+	}
+
+	// probably not quite right currently
+	public void printHeaderInfoForIslandFile () {
+		out.println ("Sequence " + reference + " is the reference sequence.");
+		out.println ("For each island, thes left and right coordinate are shown"
+						+ " for each sequence");
+		out.println ("If the island is complementary in direction to the "
+				+ "reference sequence, a negative sign is shown before the "
+				+ "coordinates for that sequence");
+		out.println ("Ignores islands segments under " + island_min
+				+ " bp and backbone segments under " + backbone_min + " bp.");
+	}
+
+	public void printIslands () {
+		printHeaders ();
+		printData (BY_ALL_AND_BB);
+	}
+	
+	protected String getData (int col, int row) {
+		if (col == headers.length - 1)
+			return current.typed_id;
+		else
+			return super.getData (col, row);
+	}
+
+	protected boolean shouldPrintRow (int row) {
+		boolean ok = false;
+		if (by_genome) {
+			if (current.multiplicityType () == multiplicity)
+				ok = true;
+		} else if (current.multiplicityType () != all_seq_multiplicity)
+			ok = true;
+		ok = ok && (current.getAvgSegmentLength () > island_min);
+		return ok;
+	}
+	
+	public Vector setColumnHeaders () {
+		Vector cols = super.setColumnHeaders ();
+		cols.add (LABEL_STRING);
+		return cols;
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/IslandFeatureWriter.java b/src/org/gel/mauve/summary/output/IslandFeatureWriter.java
new file mode 100644
index 0000000..a54560c
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/IslandFeatureWriter.java
@@ -0,0 +1,84 @@
+package org.gel.mauve.summary.output;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.gel.air.util.GroupHelpers;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.gui.sequence.FlatFileFeatureConstants;
+
+public class IslandFeatureWriter extends AbstractIslandWriter implements
+		MauveConstants, FlatFileFeatureConstants {
+
+	public static final int MULTIPLICITY_INDEX = 6;
+	public static final String ISLAND = "island";
+
+	protected IslandFeatureWriter (SegmentDataProcessor processor) {
+		super (MauveHelperFunctions.getSeqPartOfFile (processor) + "islands", processor);
+	}
+	
+	protected IslandFeatureWriter (String file, SegmentDataProcessor processor) {
+		super (file, processor);
+	}
+
+	protected void initSubClassParticulars (Hashtable args) {
+		seq_index = ((Integer) args.get (SEQUENCE_INDEX)).intValue ();
+		super.initSubClassParticulars (args);
+	}
+
+	public void printIslands () {
+		printHeaders ();
+		printData (BY_ONE_GENOME);
+	}
+
+	protected String getData (int column, int row) {
+		long value = 0;
+		switch (column) {
+			case TYPE:
+				return ISLAND;
+			case LABEL:
+				return current.typed_id;
+			case CONTIG:
+				return contig_handler.getContigName (seq_index, current.left [seq_index]);
+			case STRAND:
+				return current.reverse [seq_index] ? COMPLEMENT : FORWARD;
+			case LEFT:
+				value = current.left [seq_index];
+				break;
+			case RIGHT:
+				value = current.right [seq_index];
+				break;
+			case MULTIPLICITY_INDEX:
+				return MauveHelperFunctions.getReadableMultiplicity (current);
+			default:
+				return null;
+		}
+		return adjustForContigs (seq_index, value) + "";
+	}
+
+	public Vector setColumnHeaders () {
+		String [] cols = new String [] {TYPE_STRING, LABEL_STRING,
+				CONTIG_STRING, STRAND_STRING, LEFT_STRING, RIGHT_STRING,
+				Segment.MULTIPLICITY_STRING};
+		Vector vect = new Vector ();
+		GroupHelpers.arrayToCollection (vect, cols);
+		return vect;
+	}
+
+	public static void printIslandsAsFeatures (SegmentDataProcessor processor) {
+		int count = ((Object []) processor.get (FIRSTS)).length;
+		for (int i = 0; i < count; i++) {
+			processor.put (SEQUENCE_INDEX, new Integer (i));
+			new IslandFeatureWriter (processor);
+		}
+	}
+
+	public boolean shouldPrintRow (int row) {
+		long cur = current.multiplicityType ();
+		return (cur & multiplicity) == multiplicity && cur != all_seq_multiplicity &&
+				current.getSegmentLength (seq_index) > island_min;
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/IslandGeneFeatureWriter.java b/src/org/gel/mauve/summary/output/IslandGeneFeatureWriter.java
new file mode 100644
index 0000000..0ed10fd
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/IslandGeneFeatureWriter.java
@@ -0,0 +1,208 @@
+package org.gel.mauve.summary.output;
+
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Vector;
+
+import org.biojava.bio.seq.Feature;
+import org.biojava.bio.seq.StrandedFeature;
+import org.biojava.bio.symbol.Location;
+import org.gel.air.util.MathUtils;
+import org.gel.mauve.BaseViewerModel;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.analysis.Segment;
+
+public class IslandGeneFeatureWriter extends IslandFeatureWriter {
+	
+	public static final String PERCENT = "prct_on_is";
+	public static final int ISLAND_COL = -2;
+	public static final int PERCENT_COL = -1;
+	public static final String BACKBONE_MASK = "backbone_mask";
+	public static StringBuffer ids = new StringBuffer ();
+	public static int buffer_count;
+	
+	protected BaseViewerModel model;
+	protected ListIterator iterator;
+	protected Feature cur_feat;
+	protected double cur_percent;
+	protected double minimum_percent;
+	protected int [][] num_per_multiplicity;
+	protected boolean backbone_instead;
+	protected int [] num_features;
+	
+	public static final String ISLAND_GENE = "island_gene";
+	
+	
+	protected IslandGeneFeatureWriter (SegmentDataProcessor processor) {
+		super (MauveHelperFunctions.getSeqPartOfFile (processor) + (processor.get (
+				BACKBONE_MASK) != null ? "backbone" : "island") + "_genes", processor);
+	}
+	
+	protected void initSubClassParticulars (Hashtable args) {
+		super.initSubClassParticulars (args);
+		model = (BaseViewerModel) args.get (MODEL);
+		if (args.get (MINIMUM_PERCENT_CONTAINED) != null)
+			minimum_percent = ((Double) args.get (
+					MINIMUM_PERCENT_CONTAINED)).doubleValue ();
+		else {
+			minimum_percent = DEFAULT_MIN_PERCENT_CONTAINED;
+			args.put (MINIMUM_PERCENT_CONTAINED, new Double (minimum_percent));
+		}
+		if (args.get (BACKBONE_MASK) != null)
+			backbone_instead = true;
+		num_per_multiplicity = ((int [][]) args.get (NUM_GENES_PER_MULT));
+		Iterator itty = MauveHelperFunctions.getFeatures (model, seq_index);
+		Vector vector = new Vector ();
+		while (itty.hasNext ())
+			vector.add (itty.next ());
+		Collections.sort (vector, MauveHelperFunctions.FEATURE_COMPARATOR);
+		num_features = (int []) args.get (TOTAL_GENES);
+		num_features [seq_index] = vector.size ();
+		System.out.println ("seq: " + seq_index + " features: " + num_features [seq_index]);
+		iterator = vector.listIterator ();
+		if (iterator.hasNext ())
+			cur_feat = (Feature) iterator.next ();
+	}
+	
+	public Vector setColumnHeaders () {
+		Vector vect = super.setColumnHeaders ();
+		vect.remove (vect.size () - 1);
+		vect.add (0, PERCENT);
+		vect.add (0, ISLAND);
+		vect.add (Segment.MULTIPLICITY_STRING);
+		return vect;
+	}
+	
+	protected String getData (int col, int row) {
+		col -= 2;
+		Location loci = cur_feat.getLocation ();
+		long value = 0;
+		switch (col) {
+			case ISLAND_COL:
+				return current.typed_id;
+			case PERCENT_COL:
+				return MauveHelperFunctions.doubleToString (cur_percent, 2);
+			case TYPE:
+				return ISLAND_GENE;
+			case LABEL:
+				String id = MauveHelperFunctions.getUniqueId (cur_feat);
+				if (!backbone_instead && current.multiplicityType () < multiplicity << 1) {
+					buffer_count++;
+					ids.append (id.substring (5));
+					ids.append (',');
+				}
+				return id;
+			case CONTIG:
+				return contig_handler.getContigName (seq_index, loci.getMin ());
+			case STRAND:
+				if (!(cur_feat instanceof StrandedFeature))
+					System.out.println ("bad cast");
+				return ((StrandedFeature) cur_feat).getStrand () == 
+					StrandedFeature.NEGATIVE ? COMPLEMENT : FORWARD;
+			case LEFT:
+				value = loci.getMin ();
+				break;
+			case RIGHT:
+				value = loci.getMax ();
+				break;
+			case MULTIPLICITY_INDEX:
+				String mult = MauveHelperFunctions.getReadableMultiplicity (current);
+				performComplexIteration ();
+				return mult;
+		}
+		return adjustForContigs (seq_index, value) + "";
+	}
+	
+	public boolean badType (Feature feat) {
+		String type = feat.getType ().toLowerCase ();
+		if (type.indexOf ("rna") > -1 || type.indexOf ("gene") > -1 || 
+				type.indexOf ("cds") > -1 || type.indexOf ("asap") > -1)
+			return false;
+		else {
+			num_features [seq_index]--;
+			return true;
+		}
+	}
+	
+	public void printData () {
+		if (cur_feat != null)
+			super.printData();
+	}
+
+	public boolean shouldPrintRow (int row) {
+		Location loci = cur_feat.getLocation ();
+		boolean print = false;
+		while ((badType (cur_feat) || loci.getMax () <= current.left [seq_index]) &&
+				iterator.hasNext ()) {
+			//cur_feat = (Feature) iterator.next ();
+			performComplexIteration ();
+			if (cur_feat != null)
+				loci = cur_feat.getLocation ();
+			else
+				loci = null;
+		}
+		if (loci != null && shouldPrintSegment (row) && loci.getMin () < 
+				current.right [seq_index]) {
+			if (cur_feat instanceof StrandedFeature) {
+				cur_percent = MathUtils.percentContained (loci.getMin (), loci.getMax (), 
+						current.left [seq_index], current.right [seq_index]);
+				if (!(cur_percent >= minimum_percent)) {
+					if (loci.getMax () < current.right [seq_index] || 
+							current.nexts [seq_index] == Segment.END) {
+						performComplexIteration ();
+					}
+					else {
+						current = current.nexts [seq_index];
+					}
+				}
+				else {
+					print = true;
+					num_per_multiplicity [seq_index][(int) current.multiplicityType () - 1] += 1;
+				}
+			}
+		}
+		return print;
+	}
+	
+	protected void performComplexIteration () {
+		cur_feat = iterator.hasNext () ? (Feature) iterator.next () : null;
+		while (cur_feat != null && cur_feat.getLocation ().getMin () < 
+				current.left [seq_index] && current.prevs [seq_index] != Segment.END)
+			current = current.prevs [seq_index];
+	}
+	
+	protected boolean shouldPrintSegment (int row) {
+		if (!backbone_instead)
+			return super.shouldPrintRow (row);
+		else
+			return current.multiplicityType () == all_seq_multiplicity ? true : false;
+	}
+	
+	protected boolean moreRowsToPrint () {
+		if (cur_feat == null)
+			return false;
+		Location loci = cur_feat.getLocation ();
+		if (loci.getMin () >= current.right [seq_index] || !shouldPrintSegment (row_number))
+			return super.moreRowsToPrint ();
+		else
+			return true;
+	}
+	
+	public static void printIslandsAsFeatures (SegmentDataProcessor processor) {
+		int count = ((Object []) processor.get (FIRSTS)).length;
+		long all_mult = ((Long) processor.get (ALL_MULTIPLICITY)).longValue ();
+		processor.put (NUM_GENES_PER_MULT, new int [count][(int) all_mult]);
+		processor.put (TOTAL_GENES, new int [count]);
+		for (int i = 0; i < count; i++) {
+			processor.put (SEQUENCE_INDEX, new Integer (i));
+			new IslandGeneFeatureWriter (processor);
+			if (i == count - 1 && processor.get (BACKBONE_MASK) == null) {
+				processor.put (BACKBONE_MASK, new Object ());
+				i = -1;
+			}
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/OverviewFileWriter.java b/src/org/gel/mauve/summary/output/OverviewFileWriter.java
new file mode 100644
index 0000000..066b322
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/OverviewFileWriter.java
@@ -0,0 +1,238 @@
+package org.gel.mauve.summary.output;
+
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.analysis.Segment;
+
+public class OverviewFileWriter extends AbstractTabbedDataWriter implements MauveConstants {
+	
+	protected SegmentDataProcessor processor;
+	
+	public final static String NUMBER_GENES = "num_genes";
+	public final static String NUMBER_ISLANDS = "num_islands";
+	public final static String NUM_BASE_PAIRS = "num_bp";
+	public final static String PERCENT_TOTAL = "percent";
+	public final static String UNKNOWN = "unknown";
+	
+	protected int [][] gene_data;
+	protected int [] island_data;
+	protected long [] bp_data;
+	protected long [] long_totals;
+	protected double [] double_totals;
+	
+	protected int [] num_genes;
+	protected int [] num_segments;
+	protected long [] lengths;
+	protected Segment [] firsts;
+	
+	protected int sequence;
+	protected int total;
+	protected long cur_multiplicity;
+	protected int min_size;
+	
+	public OverviewFileWriter (SegmentDataProcessor proc) {
+		super (proc.get (SegmentDataProcessor.FILE_STUB) + "_overview.tab", proc);
+	}
+	
+	protected void initSubClassParticulars (Hashtable args) {
+		processor = (SegmentDataProcessor) args;
+		setMinSize ();
+		firsts = (Segment []) processor.get (FIRSTS);
+		if (processor.get (GENOME_LENGTHS) != null)
+			lengths = (long []) processor.get (GENOME_LENGTHS);
+		num_genes = (int []) args.get (TOTAL_GENES);
+		num_segments = new int [num_genes.length];
+		gene_data = (int [][]) processor.get (NUM_GENES_PER_MULT);
+		System.out.println ("num_genes: " + gene_data [0].length);
+		island_data = new int [gene_data [0].length];
+		bp_data = new long [gene_data [0].length];
+		row_number = gene_data [0].length + 2;
+		sequence = -1;
+		cur_multiplicity = ((Long) args.get (ALL_MULTIPLICITY)).longValue () + 1;
+		super.initSubClassParticulars (args);
+		writeHeaderInfo ();
+		printGeneInformation ();
+		doneWritingFile ();
+	}
+	
+	protected void setMinSize () {
+		int isl_min = ((Integer) processor.get (ISLAND_MIN)).intValue();
+		int bb_min = ((Integer) processor.get (BACKBONE_MIN)).intValue();
+		min_size = Math.min(isl_min, bb_min);
+	}
+	
+	protected void performCalculations () {
+		Segment seg = firsts [sequence];
+		do {
+			num_segments [sequence] += 1;
+			if (seg.getSegmentLength (sequence) > min_size) {
+				island_data [(int) seg.multiplicityType () - 1] += 1;
+				bp_data [(int) seg.multiplicityType () - 1] += seg.getSegmentLength (sequence);
+			}
+			seg = seg.nexts [sequence];
+		} while (seg != Segment.END);
+	}
+
+	public void writeHeaderInfo () {
+		try {
+			out.println ("Sequence " + processor.get (REFERENCE) + 
+					" is the reference sequence.");
+			out.println ("Island minimum: " + processor.get (ISLAND_MIN));
+			out.println ("Backbone minimum: " + processor.get (BACKBONE_MIN));
+			out.println ("Minimum length ratio considered a problem: " + 
+					processor.get (MAX_LENGTH_RATIO));
+			out.println ("Ratio represents the difference in length between the " +
+					"longest and shortest pieces over the average length.");
+			out.println ("Minimum percent of gene that must be on island: " + 
+					processor.get (MINIMUM_PERCENT_CONTAINED));
+			out.println ("File explanations: ");
+			out.println ("_islandscoords.mo contains island id and coordinate information " +
+					"for all islands in all sequences");
+			out.println ("_problembb.mo contains backbone segments whose lengths vary" +
+					"widely between sequences.");
+			out.println ("_islands contains information on all the islands in a particular sequence." +
+					"\nIt can be loaded into Mauve as features.  A file is generated per sequence");
+			out.println ("_island_genes contains similar information as _islands, but"
+					+ " by gene\n");
+		} catch (Exception e) {
+			System.out.println ("Couldn't write overview file.");
+			e.printStackTrace ();
+		}
+	}
+	
+	public void printGeneInformation () {
+		printHeaders ();
+		moreRowsToPrint ();
+		printData ();
+		out.println (IslandGeneFeatureWriter.buffer_count);
+		out.println (IslandGeneFeatureWriter.ids);
+	}
+	
+	protected String getData (int column, int row) {
+		if (row < island_data.length) {
+			double percent = -1;
+			long count = -1;
+			switch (column) {
+				case 0:
+					return MauveHelperFunctions.getReadableMultiplicity (row + 1, 
+							num_genes.length);
+				case 1:
+					if (row == island_data.length - 1 && gene_data [sequence][row] == 0)
+						gene_data [sequence][row] = num_genes [sequence] - total;
+					else
+						total += gene_data [sequence][row];
+					count = gene_data [sequence][row];
+					break;
+				case 2:
+					percent = gene_data [sequence][row] / (double) num_genes [sequence];
+					break;
+				case 3:
+					count = island_data [row];
+					break;
+				case 4:
+					percent = island_data [row] / (double) num_segments [sequence];
+					break;
+				case 5:
+					count = bp_data [row];
+					break;
+				case 6:
+					percent = bp_data [row] / (double) lengths [sequence];
+					break;
+			}
+			if (percent != -1) {
+				double_totals [column] += percent;
+				return MauveHelperFunctions.doubleToString (percent * 100, 1);
+			}
+			else if (count != -1) {
+				long_totals [column] += count;
+				return count + "";
+			}
+		}
+		else if (row == island_data.length){
+			switch (column) {
+				case 0:
+					return TOTALS;
+				case 1:
+					return num_genes [sequence] + "";
+				case 2:
+					return "100";
+				case 3:
+					return num_segments [sequence] + "";
+				case 4:
+					return "100";
+				case 5:
+					return lengths [sequence] + "";
+				case 6:
+					return "100";
+				default:
+					return null;
+			}
+		}
+		else {
+			if (column == 0)
+				return "unknown";
+			if (column % 2 == 1) {
+				long tot = (column == 1) ? num_genes [sequence] : 
+					((column == 3) ? num_segments [sequence] : lengths [sequence]);
+				return (tot - long_totals [column]) + "";
+			}
+			else
+				return MauveHelperFunctions.doubleToString (100 - (
+						double_totals [column] * 100), 1);
+		}
+		return null;
+	}
+
+	protected boolean moreRowsToPrint () {
+		if (row_number == island_data.length + 2) {
+			if (sequence == num_genes.length - 1) 
+				return false;
+			else {
+				total = 0;
+				sequence++;
+				cur_multiplicity >>= 1;
+				row_number = 0;
+				if (sequence > 0) {
+					Arrays.fill (island_data, 0);
+					Arrays.fill (bp_data, 0);
+					Arrays.fill (long_totals, 0);
+					Arrays.fill (double_totals, 0);
+				}
+				performCalculations ();
+				out.println ("Sequence " + sequence + ":");
+				return true;
+			}
+		}
+		else
+			return true;
+	}
+
+	protected Vector setColumnHeaders () {
+		Vector titles = new Vector ();
+		titles.add (Segment.MULTIPLICITY_STRING);
+		titles.add (NUMBER_GENES);
+		titles.add (PERCENT_TOTAL);
+		titles.add (NUMBER_ISLANDS);
+		titles.add (PERCENT_TOTAL);
+		titles.add (NUM_BASE_PAIRS);
+		titles.add (PERCENT_TOTAL);
+		long_totals = new long [titles.size()];
+		double_totals = new double [long_totals.length];
+		return titles;
+	}
+
+	protected boolean shouldPrintRow (int row) {
+		if (row == island_data.length + 1 || row == island_data.length || (/*gene_data [sequence][row] != 0 &&*/
+				((row + 1) & cur_multiplicity) == cur_multiplicity)) {
+			return true;
+		}
+		else {
+			return false;
+		}
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/PartialFastaWriter.java b/src/org/gel/mauve/summary/output/PartialFastaWriter.java
new file mode 100644
index 0000000..9e572d4
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/PartialFastaWriter.java
@@ -0,0 +1,68 @@
+package org.gel.mauve.summary.output;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.biojava.bio.seq.Sequence;
+import org.biojavax.bio.seq.RichSequence;
+import org.gel.mauve.gui.Mauve;
+import org.gel.mauve.gui.MauveFrame;
+import org.gel.mauve.module.MauveModule;
+import org.gel.mauve.module.ModuleListener;
+
+public class PartialFastaWriter implements ModuleListener {
+	
+	protected String [] args;
+	
+	public PartialFastaWriter(String[] args) {
+		this.args = args;
+	}
+
+	public void startModule(MauveFrame frame) {
+		int ind = 1;
+		File fout = null;
+		while (ind < args.length) {
+			try {
+				fout = new File (args [ind++]);
+				if (!fout.exists()) {
+					fout.getParentFile ().mkdirs ();
+					fout.createNewFile();
+				}
+				PrintStream out = new PrintStream (new FileOutputStream (fout));
+				Sequence sequence = frame.getModel().getGenomeBySourceIndex(
+						Integer.parseInt(args [ind++])).getAnnotationSequence();
+				String name = sequence.getName ();
+				if (name == null) {
+					name = fout.getName ();
+					int ind2 = name.lastIndexOf ('.');
+					if (ind2 != -1)
+						name = name.substring (0, ind2);
+				}
+				/*System.out.println ("seq: " + sequence.subList (Integer.parseInt(args [ind++]),
+						Integer.parseInt(args [ind++])));*/
+				RichSequence dna = RichSequence.Tools.createRichSequence (name, 
+						sequence.subList(Integer.parseInt(args [ind++]),
+						Integer.parseInt(args [ind++])));
+				RichSequence.IOTools.writeFasta(out, dna, dna.getNamespace ());
+				out.flush ();
+				out.close ();
+			}
+			catch (IOException i) {
+				System.out.println ("file: " + fout.getAbsolutePath ());
+				i.printStackTrace ();
+			}
+			catch (Exception e) {
+				e.printStackTrace();
+				//ind -= (ind - 1) % 4;
+			}
+		}
+	}
+
+	public static void main (String [] args) {
+		PartialFastaWriter writer = new PartialFastaWriter (args);
+		MauveModule.mainHook (args, new MauveModule (writer));
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/SegmentDataProcessor.java b/src/org/gel/mauve/summary/output/SegmentDataProcessor.java
new file mode 100644
index 0000000..289539f
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/SegmentDataProcessor.java
@@ -0,0 +1,285 @@
+package org.gel.mauve.summary.output;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.gel.mauve.Match;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.analysis.SegmentComparator;
+import org.gel.mauve.contigs.ContigHandler;
+
+public class SegmentDataProcessor extends Hashtable implements MauveConstants {
+
+	protected int count;
+
+	protected int backbone_min;
+
+	protected int reference = -1;
+
+	protected Segment [] firsts;
+
+	protected Segment current;
+
+	protected Vector backbone;
+	
+	protected long all_seq_multiplicity;
+
+	public SegmentDataProcessor (Hashtable map) {
+		super (map);
+		init ();
+		new IslandCoordinateWriter (this);
+		new TroubleMatchWriter (this);
+		IslandFeatureWriter.printIslandsAsFeatures (this);
+		IslandGeneFeatureWriter.printIslandsAsFeatures (this);
+		new OverviewFileWriter (this);
+	}
+
+	protected void init () {
+		String file = (String) get (FILE_STUB);
+		File temp = new File (file);
+		File dir = new File (temp.getParentFile (), ANALYSIS_OUTPUT);
+		if (!dir.exists ())
+			dir.mkdir ();
+		put (FILE_STUB, new File (dir, temp.getName ()).getAbsolutePath ());
+		backbone = (Vector) get (BACKBONE);
+		current = Segment.END;
+		count = ((Segment) backbone.get (0)).left.length;
+		for (int i = 0; i < count; i++) {
+			all_seq_multiplicity <<= 1;
+			all_seq_multiplicity |= 1;
+		}
+		put (ALL_MULTIPLICITY, new Long (all_seq_multiplicity));
+		put (REFERENCE, new Integer (getReferenceSequence ()));
+		addDataLinks ();
+		//compressBackboneSegments ();
+		addUniques ();
+		//findContigsInUniques ();
+		assignIDs ();
+	}
+	
+	protected void findContigsInUniques () {
+		ContigHandler handler = (ContigHandler) get (CONTIG_HANDLER);
+		if (handler == null)
+			return;
+		for (int i = 0; i < count; i++) {
+			Segment segment = firsts [i];
+			long mult = multiplicityForGenome (i);
+			while (segment != Segment.END) {
+				if (segment.multiplicityType () != all_seq_multiplicity) {
+					handler.fixSegmentByContigs (i, segment);
+				}
+				segment = segment.nexts [i];
+			}
+		}
+	}
+	
+	protected void assignIDs () {
+		Segment segment = null;
+		long all_mult = ((Long) get (ALL_MULTIPLICITY)).longValue ();
+		int number = 1;
+		for (int i = 0; i < count; i++) {
+			long cur_mult = multiplicityForGenome (i);
+			segment = firsts [i];
+			do {
+				if (segment.typed_id == null) {
+					String id = segment.multiplicityType () == all_mult ? "b_" :
+							segment.multiplicityType () == cur_mult ? "i_" : "b_i_";
+					id += number++;
+					segment.typed_id = id;
+				}
+				segment = segment.nexts [i];
+			} while (segment != Segment.END);
+		}
+	}
+	
+	public long multiplicityForGenome (int index) {
+		return multiplicityForGenome (index, count);
+	}
+	
+	public static long multiplicityForGenome (int index, int count) {
+		long multiplicity = 1;
+		multiplicity <<= (count - index - 1);
+		return multiplicity;
+	}
+
+	protected int getReferenceSequence () {
+		if (reference == -1) {
+			Iterator itty = backbone.iterator ();
+			int [] wrong = new int [count];
+			int remaining = count;
+			while (remaining > 1 && itty.hasNext ()) {
+				Segment seg = (Segment) itty.next ();
+				for (int i = 0; i < count; i++) {
+					if (seg.reverse[i] && wrong[i] == 0) {
+						wrong[i] = -1;
+						remaining--;
+					}
+				}
+			}
+			for (int i = 0; i < count; i++) {
+				if (wrong[i] == 0)
+					reference = i;
+			}
+		}
+		return reference;
+	}
+
+	protected void addDataLinks () {
+		firsts = new Segment [count];
+		for (int i = 0; i < count; i++) {
+			Collections.sort (backbone, new SegmentComparator (i));
+			int size = backbone.size () - 1;
+			for (int j = 0; j <= size; j++) {
+				Segment seg = (Segment) backbone.get (j);
+				if (j > 0
+						&& ((Segment) backbone.get (j - 1)).left[i] != Match.NO_MATCH) {
+					seg.prevs[i] = (Segment) backbone.get (j - 1);
+					if (firsts[i] == null) {
+						firsts[i] = seg.prevs[i];
+						firsts[i].prevs[i] = Segment.END;
+					}
+				}
+				if (j < size && seg.left[i] != Match.NO_MATCH)
+					seg.nexts[i] = (Segment) backbone.get (j + 1);
+			}
+			((Segment) backbone.get (size)).nexts[i] = Segment.END;
+		}
+		put (FIRSTS, firsts);
+	}
+
+	protected void addUniques () {
+		long [] sizes = (long []) get (GENOME_LENGTHS);
+		for (int i = 0; i < count; i++) {
+			Segment prev = firsts[i];
+			if (prev.left[i] != 1) {
+				Segment seg = new Segment (count, true);
+				seg.right[i] = prev.left[i] - 1;
+				seg.left[i] = 1;
+				firsts[i] = seg;
+				seg.nexts[i] = prev;
+				prev.prevs[i] = seg;
+			}
+			Segment cur = prev.nexts[i];
+			while (cur != firsts[i] && cur != Segment.END) {
+				if (prev.right[i] + 1 != cur.left[i]) {
+					if (prev.right[i] == cur.left[i]) {
+						/*System.out.println ("bp in two segments");
+						System.out.println ("segment: " + cur + " prev: "
+								+ prev);*/;
+					} else {
+						Segment island = new Segment (count, true);
+						island.left[i] = prev.right[i] + 1;
+						island.right[i] = cur.left[i] - 1;
+						prev.nexts[i] = island;
+						island.prevs[i] = prev;
+						island.nexts[i] = cur;
+						cur.prevs[i] = island;
+					}
+				}
+				prev = cur;
+				if (sizes != null && cur.nexts [i] == Segment.END &&
+						cur.right [i] < sizes [i]) {
+					Segment island = new Segment (count, true);
+					island.left[i] = cur.right[i] + 1;
+					island.right[i] = sizes [i];
+					cur.nexts[i] = island;
+					island.prevs[i] = cur;
+					island.nexts[i] = Segment.END;
+				}
+				cur = prev.nexts[i];
+			}
+		}
+	}
+
+	// it doesn't necessarily matter if segments length 1 are reversed or not.
+	// this
+	// algorithm assumes whatever wrote the .backbone file put it in the "best"
+	// orientation
+	//this algorithm isn't working 100%; is currently concatinating two with different multiplicities
+	protected void compressBackboneSegments () {
+		try {
+			int ind = reference;
+			int num_combined = 0;
+			long dist_added = 0;
+			do {
+				current = firsts[ind].nexts[ind];
+				while (current != Segment.END) {
+					int j = ind;
+					boolean ok = true;
+					long this_dist = 0;
+					do {
+						Segment comp = (current.reverse[ind] == current.reverse[j]) ? current.prevs[j]
+								: current.nexts[j];
+						if (comp == Segment.END || comp == firsts[j])
+							ok = false;
+						if (comp != null
+								&& comp.multiplicityType () != current
+										.multiplicityType ())
+							ok = false;
+						if (ok && comp == current.prevs[ind]) {
+							long dist = current.left[j] - comp.right[j] - 1;
+							if (dist < 0
+									|| dist > backbone_min
+									|| dist > current.right[j]
+											- current.left[j] + 1
+									|| dist > comp.right[j] - comp.left[j]
+											+ 1)
+								ok = false;
+							else
+								this_dist += dist;
+						} else if (comp != null)
+							ok = false;
+						j++;
+						if (j == count)
+							j = 0;
+					} while (ok && j != ind);
+					current = current.nexts[ind];
+					if (ok) {
+						dist_added += this_dist;
+						num_combined++;
+						current.prevs[ind].prevs[ind].append (current, false);
+						current.remove (ind);
+						if (backbone.contains (current))
+							backbone.remove (current);
+					}
+				}
+				if (++ind == count)
+					ind = 0;
+			} while (ind != reference);
+		} catch (Exception e) {
+			System.out.println ("Problems compressing backbone segments");
+			e.printStackTrace ();
+		}
+	}
+
+	protected Vector makeDefaultStartEndColumnHeaders () {
+		int divisor = (get (CONTIG_HANDLER) == null || get (CONTIG_HANDLER) 
+				instanceof AbstractMatchDataWriter) ? 2 : 3;
+		Vector titles = new Vector ();
+		int seq = -1;
+		String tail = null;
+		for (int i = 0; i < count * divisor; i++) {
+			if (i % divisor == 0) {
+				seq++;
+				tail = "left";
+			}
+			else if (i % divisor == 1)
+				tail = "right";
+			else
+				tail = "contig";
+			titles.add ("seq" + seq + "_" + tail);
+		}
+		return titles;
+	}
+
+	protected List getSegmentsSortedBySeq (int sequence) {
+		Collections.sort (backbone, new SegmentComparator (sequence));
+		return backbone;
+	}
+
+}
diff --git a/src/org/gel/mauve/summary/output/TroubleMatchWriter.java b/src/org/gel/mauve/summary/output/TroubleMatchWriter.java
new file mode 100644
index 0000000..1008fc1
--- /dev/null
+++ b/src/org/gel/mauve/summary/output/TroubleMatchWriter.java
@@ -0,0 +1,98 @@
+package org.gel.mauve.summary.output;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.gel.mauve.Match;
+import org.gel.mauve.MauveConstants;
+import org.gel.mauve.MauveHelperFunctions;
+import org.gel.mauve.analysis.Segment;
+import org.gel.mauve.analysis.SegmentComparator;
+import org.gel.mauve.gui.sequence.FlatFileFeatureConstants;
+
+public class TroubleMatchWriter extends AbstractMatchDataWriter implements
+		MauveConstants {
+
+	protected double avg_length;
+
+	protected double ratio;
+
+	protected int length_column;
+
+	protected int percent_column;
+
+	public static final String RATIO = "diff_to_lngth";
+
+	public TroubleMatchWriter (SegmentDataProcessor processor) {
+		super ("problembb", processor);
+		findOddLengthedMatches ();
+		doneWritingFile ();
+	}
+
+	protected void initSubClassParticulars (Hashtable args) {
+		super.initSubClassParticulars (args);
+		length_column = count * (contig_handler instanceof AbstractMatchDataWriter ? 2 : 3);
+		percent_column = length_column + 1;
+	}
+
+	// probably not quite right currently
+	public void printHeaderInfoForFile () {
+		out.println ("Sequence " + reference + " is the reference sequence.");
+		out
+				.println ("Each backbone segment with unclear information will be printed");
+		out.println ("If the segment is complementary in direction to the "
+				+ "reference sequence, a negative sign is shown before the "
+				+ "coordinates for that sequence");
+	}
+
+	public void findOddLengthedMatches () {
+		out.println ("identifying odd lengthed matches");
+		printHeaders ();
+		printData (BY_BB_LIST);
+	}
+
+	public boolean shouldPrintRow (int row) {
+		long [] lengths = current.getSegmentLengths ();
+		int contains = 0;
+		Arrays.sort (lengths);
+		for (int i = 0; i < count; i++) {
+			if (lengths[i] != 0)
+				contains++;
+		}
+		long difference = lengths[count - 1] - lengths[count - contains];
+		avg_length = current.getAvgSegmentLength ();
+		ratio = difference / avg_length;
+		if (avg_length > backbone_min && ratio > max_length_ratio)
+			return true;
+		else
+			return false;
+	}
+
+	public Vector setColumnHeaders () {
+		Vector titles = super.setColumnHeaders ();
+		titles.add (AVERAGE_LENGTH);
+		titles.add (RATIO);
+		return titles;
+	}
+
+	protected String getData (int col, int row) {
+		if (col < count * (contig_handler instanceof AbstractMatchDataWriter ? 2 : 3))
+			return super.getData (col, row);
+		else {
+			double data;
+			int decimals = 4;
+			if (col == length_column) {
+				data = avg_length;
+				decimals = 1;
+			} else
+				data = ratio;
+			return MauveHelperFunctions.doubleToString (data, decimals);
+		}
+
+	}
+
+}
diff --git a/src/org/gel/mauve/tree/FileKey.java b/src/org/gel/mauve/tree/FileKey.java
new file mode 100644
index 0000000..a7209a9
--- /dev/null
+++ b/src/org/gel/mauve/tree/FileKey.java
@@ -0,0 +1,71 @@
+package org.gel.mauve.tree;
+
+import java.io.Serializable;
+
+public class FileKey implements Key, Cloneable, Serializable {
+	static final long serialVersionUID = 1;
+
+	private long lend;
+
+	private long length = 0;
+
+	private long f_offset;
+
+	private long f_length = 0;
+
+	public FileKey (long lend, long f_offset) {
+		this.lend = lend;
+		this.f_offset = f_offset;
+	}
+
+	public long getLength () {
+		return length;
+	}
+
+	public void incrementLength () {
+		length++;
+	}
+
+	public long getSeqLength () {
+		return length;
+	}
+
+	public long getOffset () {
+		return f_offset;
+	}
+
+	public long getFLength () {
+		return f_length;
+	}
+
+	public void setFLength (long f_length) {
+		this.f_length = f_length;
+	}
+
+	public void cropStart (long size) {
+		length -= size;
+		lend += size;
+		if (length < 0)
+			throw new ArrayIndexOutOfBoundsException ();
+	}
+
+	public void cropEnd (long size) {
+		length -= size;
+		if (length < 0)
+			throw new ArrayIndexOutOfBoundsException ();
+	}
+
+	public Object copy () {
+		try {
+			return clone ();
+		} catch (CloneNotSupportedException e) {
+			throw new Error ("Very unexpected exception", e);
+		}
+	}
+
+	public String toString () {
+		return "FileKey[seq_offset: " + lend + " seq_len: " + length + " bfo: "
+				+ f_offset + " len: " + f_length + "]";
+	}
+
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/tree/GISTree.java b/src/org/gel/mauve/tree/GISTree.java
new file mode 100644
index 0000000..450eacd
--- /dev/null
+++ b/src/org/gel/mauve/tree/GISTree.java
@@ -0,0 +1,414 @@
+package org.gel.mauve.tree;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Records a mapping between gapped sequence coordinates and ungapped sequence
+ * coordinates. Implemented using a splay tree for O(n log n) amortized time
+ * complexity operations.
+ */
+public class GISTree implements Serializable {
+	static final long serialVersionUID = 1;
+
+	public static long end = Long.MAX_VALUE;
+
+	int rootIndex = TreeStore.NULL_REF;
+
+	TreeStore ts = null;
+
+	/** Create a new GISTree using the given TreeStore to store data */
+	public GISTree (TreeStore ts) {
+		this.ts = ts;
+	}
+
+	/** returns the total length of the gapped sequence stored in the tree */
+	public long length () {
+		return (rootIndex == TreeStore.NULL_REF) ? 0 : ts.length[rootIndex];
+	}
+
+	// interval sequence specific:
+
+	/** returns the length of ungapped sequence stored in the tree */
+	public long sequenceLength () {
+		return rootIndex == TreeStore.NULL_REF ? 0 : getSeqLength (rootIndex);
+	}
+
+	/** find the interval containing a position in the gapped sequence */
+	public int find (long seq_point) {
+		long [] position = new long [1];
+		position[0] = seq_point;
+		int index = recursiveFind (rootIndex, position);
+		splay (index);
+		return rootIndex;
+	}
+
+	/** find the interval containing a position in the ungapped sequence */
+	public int find_seqindex (long seq_point) {
+		long [] position = new long [1];
+		position[0] = seq_point;
+		int index = recursiveSeqFind (rootIndex, position);
+		splay (index);
+		return rootIndex;
+	}
+
+	public long getSequenceStart (int index) {
+		splay (index);
+		return getLeft (rootIndex) != TreeStore.NULL_REF ? getSeqLength (getLeft (rootIndex))
+				: 0;
+	}
+
+	public long getStart (int index) {
+		splay (index);
+		return getLeft (rootIndex) != TreeStore.NULL_REF ? getLength (getLeft (rootIndex))
+				: 0;
+	}
+
+	public int insert (Key val, long point) {
+		long [] position = new long [1];
+		position[0] = point;
+		int newIndex = ts.createGistNode ();
+
+		setKey (newIndex, (Key) val.copy ()); // newNode.setKey((Key)val.copy());
+		setLength (newIndex, val.getLength ()); // newNode.setLength(val.getLength());
+		setSeqLength (newIndex, val.getSeqLength ()); // newNode.setSeqLength(val.getSeqLength());
+
+		// just insert new_node as root if the tree is empty
+		if (rootIndex == TreeStore.NULL_REF) {
+			rootIndex = newIndex;
+			return rootIndex;
+		}
+
+		int insertionIndex = recursiveFind (rootIndex, position);
+
+		// insert the new node below ins_node
+		if (position[0] > 0
+				&& position[0] < getKey (insertionIndex).getLength ()) {
+			// trunc ins_node, do a right insert of new_node and the right part
+			// of ins_node
+			int rightIndex = ts.createGistNode ();
+			// Question: does inserting two nodes at once violate the splay
+			// rules?
+			// probably, but i'm not sure it really matters
+			setRight (newIndex, rightIndex);
+			setParent (rightIndex, newIndex);
+			setKey (rightIndex, (Key) getKey (insertionIndex).copy ());
+
+			// crop the key
+			getKey (rightIndex).cropStart (position[0]);
+			setLength (rightIndex, val.getLength ());
+			setSeqLength (rightIndex, val.getSeqLength ());
+			// question: do I need to update new_node.length or will the splay
+			// operations
+			// take care of it?
+
+			getKey (insertionIndex).cropEnd (
+					getKey (insertionIndex).getLength () - position[0]);
+			setSeqLength (insertionIndex, getKey (insertionIndex)
+					.getSeqLength ());
+			setLength (insertionIndex, getKey (insertionIndex).getLength ());
+			// now position[0] ought to be equal to ins_node.length,
+			// so new_node should get inserted right below it
+		}
+
+		if (position[0] == 0) {
+			// find the right-most child of the left subtree and insert there
+			int currentIndex = getLeft (insertionIndex);
+			if (currentIndex != TreeStore.NULL_REF) {
+				while (getRight (currentIndex) != TreeStore.NULL_REF) {
+					currentIndex = getRight (currentIndex);
+				}
+				// insert to the right of cur_node
+				setRight (currentIndex, newIndex);
+				setParent (newIndex, currentIndex);
+			} else {
+				// insert to the left of ins_node
+				setLeft (insertionIndex, newIndex);
+				setParent (newIndex, insertionIndex);
+			}
+		}
+
+		if (position[0] >= getKey (insertionIndex).getLength ()) {
+			// find the left-most child of the right subtree and insert there
+			int currentIndex = getRight (insertionIndex);
+			if (currentIndex != TreeStore.NULL_REF) {
+				while (getLeft (currentIndex) != TreeStore.NULL_REF) {
+					currentIndex = getLeft (currentIndex);
+				}
+				// insert to the left of cur_node
+				setLeft (currentIndex, newIndex);
+				setParent (newIndex, currentIndex);
+			} else {
+				// insert to the right of ins_node
+				setRight (insertionIndex, newIndex);
+				setParent (newIndex, insertionIndex);
+			}
+		}
+		splay (newIndex);
+		return newIndex;
+	}
+
+	/**
+	 * splay a node to the root of the tree
+	 */
+	protected void splay (int index) {
+		_splay (index);
+		rootIndex = index;
+	}
+
+	/**
+	 * Convert a sequence coordinate to a column index
+	 */
+	public long seqPosToColumn (long seq_index) {
+		int l_iter = find_seqindex (seq_index);
+		long fk_left_seq_off = getSequenceStart (l_iter);
+		long fk_left_col = getStart (l_iter);
+		fk_left_col += seq_index - fk_left_seq_off;
+		return fk_left_col;
+	}
+
+	/**
+	 * Convert a column index to a sequence index, taking the nearest seq index
+	 * to the left if the column falls in a gap region
+	 */
+	public long columnToSeqPos (long column) {
+		int l_iter = find (column);
+		long seq_off = getSequenceStart (l_iter);
+		long left_col = getStart (l_iter);
+		if (column != left_col && getKey (l_iter).getSeqLength () != 0)
+			seq_off += column - left_col;
+		return seq_off;
+	}
+
+	private void recalculateLengths (int index) {
+		ts.length[index] = ts.key[index].getLength ();
+		ts.seqLength[index] = ts.key[index].getSeqLength ();
+
+		if (ts.right[index] != TreeStore.NULL_REF) {
+			ts.length[index] += ts.length[ts.right[index]];
+			ts.seqLength[index] += ts.seqLength[ts.right[index]];
+			ts.parent[ts.right[index]] = index;
+		}
+
+		if (ts.left[index] != TreeStore.NULL_REF) {
+			ts.length[index] += ts.length[ts.left[index]];
+			ts.seqLength[index] += ts.seqLength[ts.left[index]];
+			ts.parent[ts.left[index]] = index;
+		}
+	}
+
+	/**
+	 * splay this node to the root of the tree
+	 */
+	public void _splay (int index) {
+		// splay operations and node naming convention taken from
+		// http://www.cs.nyu.edu/algvis/java/SplayTree.html
+		while (ts.parent[index] != TreeStore.NULL_REF) {
+			int yIndex = ts.parent[index];
+			int zIndex = (ts.parent[yIndex] != TreeStore.NULL_REF) ? ts.parent[yIndex]
+					: yIndex;
+			if (ts.left[ts.parent[index]] == index) {
+				if (ts.parent[ts.parent[index]] == TreeStore.NULL_REF) {
+					ts.left[yIndex] = ts.right[index];
+					ts.right[index] = yIndex;
+				} else if (ts.left[ts.parent[ts.parent[index]]] == ts.parent[index]) {
+					// zig-zig
+					ts.left[zIndex] = ts.right[yIndex];
+					ts.left[yIndex] = ts.right[index];
+					ts.right[yIndex] = zIndex;
+					ts.right[index] = yIndex;
+				} else {
+					// zag-zig
+					ts.right[zIndex] = ts.left[index];
+					ts.left[yIndex] = ts.right[index];
+					ts.right[index] = yIndex;
+					ts.left[index] = zIndex;
+				}
+			} else {
+				if (ts.right[ts.parent[index]] != index)
+					throw new Error ("Inconsistency error");
+
+				// zagging
+				if (ts.parent[ts.parent[index]] == TreeStore.NULL_REF) {
+					// zag
+					ts.right[yIndex] = ts.left[index];
+					ts.left[index] = yIndex;
+				} else if (ts.left[ts.parent[ts.parent[index]]] == ts.parent[index]) {
+					// zig-zag
+					ts.left[zIndex] = ts.right[index];
+					ts.right[yIndex] = ts.left[index];
+					ts.left[index] = yIndex;
+					ts.right[index] = zIndex;
+				} else {
+					// zag-zag
+					ts.right[zIndex] = ts.left[yIndex];
+					ts.right[yIndex] = ts.left[index];
+					ts.left[yIndex] = zIndex;
+					ts.left[index] = yIndex;
+				}
+			}
+			// update parents and lengths
+			ts.parent[index] = ts.parent[zIndex];
+			if (ts.parent[index] != TreeStore.NULL_REF) {
+				if (ts.left[ts.parent[index]] == zIndex)
+					ts.left[ts.parent[index]] = index;
+				else
+					ts.right[ts.parent[index]] = index;
+			}
+			recalculateLengths (zIndex);
+			recalculateLengths (yIndex);
+			recalculateLengths (index);
+		}
+	}
+
+	/**
+	 * returns the Key of the node immediately to the right of x, or null if x
+	 * is already the righ-most tree node
+	 */
+	public Key increment (int index) {
+		// if x has a right child, find its leftmost descendant
+		if (ts.right[index] != TreeStore.NULL_REF) {
+			int leftie = ts.right[index];
+			while (ts.left[leftie] != TreeStore.NULL_REF)
+				leftie = ts.left[leftie];
+			return ts.key[leftie];
+		}
+
+		// look for the least ancestor where x was the left descendant
+		while (ts.parent[index] != TreeStore.NULL_REF) {
+			if (ts.left[ts.parent[index]] == index)
+				return ts.key[ts.parent[index]];
+			index = ts.parent[index];
+		}
+
+		// x is already the right-most tree value
+		return null;
+	}
+
+	/**
+	 * find the node below cur_node containing a given position in the gapped
+	 * sequence, starting at the left-most position below cur_node
+	 * 
+	 * @param cur_node
+	 *            The tree node to use as the base for the search. usually the
+	 *            root
+	 * @param position
+	 *            The position to search for. A single element array. The value
+	 *            is modified to reflect the distance into the returned node
+	 *            where the requested position actually occurs. version 2 of
+	 *            recursiveFind -- don't build a potentially huge call stack!
+	 */
+	public int recursiveFind (int index, long [] position) {
+		while (index != TreeStore.NULL_REF) {
+			long left_len = ts.left[index] != TreeStore.NULL_REF ? ts.length[ts.left[index]]
+					: 0;
+			if (ts.left[index] != TreeStore.NULL_REF
+					&& position[0] < ts.length[ts.left[index]]) {
+				index = ts.left[index];
+				continue;
+			}
+
+			// it's not part of the left subtree, subtract off the left subtree
+			// length
+			position[0] -= left_len;
+			if (ts.right[index] != TreeStore.NULL_REF
+					&& position[0] >= ts.key[index].getLength ()) {
+				position[0] -= ts.key[index].getLength ();
+				index = ts.right[index];
+				continue;
+			}
+
+			// return this node if nothing else can be done
+			return index;
+		}
+		return TreeStore.NULL_REF;
+	}
+
+	/**
+	 * Find the node below cur_node containing a given position in the ungapped
+	 * sequence, starting at the left-most position below cur_node
+	 * 
+	 * @param cur_node
+	 *            The tree node to use as the base for the search. usually the
+	 *            root
+	 * @param position
+	 *            The position to search for. A single element array. The value
+	 *            is modified to reflect the distance into the returned node
+	 *            where the requested position actually occurs. version 2 of
+	 *            recursiveSeqFind -- don't build a potentially huge call stack!
+	 */
+	public int recursiveSeqFind (int index, long [] position) {
+		while (index != TreeStore.NULL_REF) {
+			long left_len = ts.left[index] != TreeStore.NULL_REF ? ts.seqLength[ts.left[index]]
+					: 0;
+			if (ts.left[index] != TreeStore.NULL_REF
+					&& position[0] < ts.seqLength[ts.left[index]]) {
+				index = ts.left[index];
+				continue;
+			}
+
+			// it's not part of the left subtree, subtract off the left subtree
+			// length
+			position[0] -= left_len;
+			if (ts.right[index] != TreeStore.NULL_REF
+					&& position[0] >= ts.key[index].getSeqLength ()) {
+				position[0] -= ts.key[index].getSeqLength ();
+				index = ts.right[index];
+				continue;
+			}
+
+			// return this node if nothing else can be done
+			return index;
+		}
+		return TreeStore.NULL_REF;
+	}
+
+	public Key getKey (int index) {
+		return ts.key[index];
+	}
+
+	public void setKey (int index, Key k) {
+		ts.key[index] = k;
+	}
+
+	public long getSeqLength (int index) {
+		return ts.seqLength[index];
+	}
+
+	public void setSeqLength (int index, long l) {
+		ts.seqLength[index] = l;
+	}
+
+	public long getLength (int index) {
+		return ts.length[index];
+	}
+
+	public void setLength (int index, long l) {
+		ts.length[index] = l;
+	}
+
+	public int getLeft (int index) {
+		return ts.left[index];
+	}
+
+	public void setLeft (int index, int l) {
+		ts.left[index] = l;
+	}
+
+	public int getRight (int index) {
+		return ts.right[index];
+	}
+
+	public void setRight (int index, int r) {
+		ts.right[index] = r;
+	}
+
+	public int getParent (int index) {
+		return ts.parent[index];
+	}
+
+	public void setParent (int index, int p) {
+		ts.parent[index] = p;
+	}
+
+}
diff --git a/src/org/gel/mauve/tree/GapKey.java b/src/org/gel/mauve/tree/GapKey.java
new file mode 100644
index 0000000..fcd19c3
--- /dev/null
+++ b/src/org/gel/mauve/tree/GapKey.java
@@ -0,0 +1,45 @@
+package org.gel.mauve.tree;
+
+import java.io.Serializable;
+
+public class GapKey implements Key, Cloneable, Serializable {
+	static final long serialVersionUID = 1;
+
+	public long length;
+
+	public GapKey (long length) {
+		this.length = length;
+	}
+
+	public long getLength () {
+		return length;
+	}
+
+	public long getSeqLength () {
+		return 0;
+	}
+
+	public void cropStart (long size) {
+		length -= size;
+		if (length < 0)
+			throw new ArrayIndexOutOfBoundsException ();
+	}
+
+	public void cropEnd (long size) {
+		length -= size;
+		if (length < 0)
+			throw new ArrayIndexOutOfBoundsException ();
+	}
+
+	public Object copy () {
+		try {
+			return clone ();
+		} catch (CloneNotSupportedException e) {
+			throw new Error ("Very unexpected exception", e);
+		}
+	}
+
+	public String toString () {
+		return "GapKey[length: " + length + "]";
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/tree/IntervalSequenceTree.java b/src/org/gel/mauve/tree/IntervalSequenceTree.java
new file mode 100644
index 0000000..e807de0
--- /dev/null
+++ b/src/org/gel/mauve/tree/IntervalSequenceTree.java
@@ -0,0 +1,320 @@
+package org.gel.mauve.tree;
+
+import java.util.Iterator;
+
+public class IntervalSequenceTree {
+	IstNode root;
+
+	/** < Root of the tree */
+	IstNode leftmost;
+
+	/** < Left most tree node, for begin() method */
+	IstNode rightmost;
+
+	/** < Right most tree node, for end() method */
+
+	/**
+	 * Returns the total length of intervals contained in this interval sequence
+	 */
+	long nodeCount () {
+		return root == null ? 0 : root.getSubtreeSize ();
+	}
+
+	public Iterator insert (Key val, long point) {
+		long [] iv_offset = new long [1];
+		iv_offset[0] = point;
+		IstNode ins_node = recursiveFind (iv_offset, root);
+		IstNode new_node = new IstNode ();
+		new_node.setKey ((Key) val.copy ());
+		new_node.setLength (val.getLength ());
+		new_node.setSubtreeSize (0);
+		if (ins_node == null) {
+			// end insert
+			rightmost = new_node;
+			if (root == null) {
+				root = new_node;
+				leftmost = new_node;
+				return new IstIterator (this, new_node);
+			}
+			// find the shallowest right insertion point
+			ins_node = decrement (null);
+			// make a new parent node
+			IstNode new_parent = new IstNode ();
+			new_parent.setLeft (ins_node);
+			new_parent.setRight (new_node);
+			new_parent.setParent (ins_node.getParent ());
+			if (new_parent.getParent () == null)
+				root = new_parent;
+			else
+				new_parent.getParent ().setRight (new_parent);
+			ins_node.setParent (new_parent);
+			new_parent.setLength (ins_node.getLength ());
+
+			// update lengths and subtree_sizes along the path to the root
+			propogateChanges (new_parent, new_node.getLength (), 2);
+			return new IstIterator (this, new_node);
+		}
+
+		// iv_offset is the distance into the node that the leaf should be split
+		// 0 is a special case (left insert)
+		if (iv_offset[0] == 0) {
+			IstNode new_parent = new IstNode ();
+			new_parent.setLeft (new_node);
+			new_parent.setRight (ins_node);
+			new_parent.setParent (ins_node.getParent ());
+			if (new_parent.getParent ().getRight () == ins_node)
+				new_parent.getParent ().setRight (new_parent);
+			else
+				new_parent.getParent ().setLeft (new_parent);
+			new_parent.setLength (ins_node.getLength ());
+
+			ins_node.setParent (new_parent);
+			new_node.setParent (new_parent);
+
+			if (point == 0)
+				leftmost = new_node;
+			// update lengths and subtree_sizes along the path to the root
+			propogateChanges (new_parent, new_node.getLength (), 2);
+		} else {
+			// need to split a leaf node
+			IstNode new_gp = new IstNode ();
+			IstNode new_parent = new IstNode ();
+			new_gp.setParent (ins_node.getParent ());
+			new_gp.setRight (new_parent);
+			new_gp.setLeft (new IstNode ());
+			new_gp.getLeft ().setKey ((Key) ins_node.getKey ().copy ());
+			new_gp.getLeft ().getKey ().cropEnd (
+					ins_node.getLength () - iv_offset[0]);
+			new_gp.getLeft ().setLength (
+					new_gp.getLeft ().getKey ().getLength ());
+			new_gp.getLeft ().setParent (new_gp);
+
+			ins_node.getKey ().cropStart (iv_offset[0]);
+			ins_node.setLength (ins_node.getKey ().getLength ());
+			ins_node.setParent (new_parent);
+			new_node.setParent (new_parent);
+			new_parent.setLeft (new_node);
+			new_parent.setRight (ins_node);
+			new_parent.setParent (new_gp);
+			new_parent
+					.setLength (new_node.getLength () + ins_node.getLength ());
+			new_parent.setSubtreeSize (2);
+
+			new_gp.setLength (ins_node.getLength ()
+					+ new_gp.getLeft ().getLength ());
+			new_gp.setSubtreeSize (1);
+			if (new_gp.getParent () == null) {
+				root = new_gp;
+				leftmost = new_gp.getLeft ();
+				rightmost = ins_node;
+			} else if (new_gp.getParent ().getRight () == ins_node)
+				new_gp.getParent ().setRight (new_gp);
+			else
+				new_gp.getParent ().setLeft (new_gp);
+
+			// update lengths and subtree_sizes along the path to the root
+			new_gp.setSubtreeSize (-1);
+			propogateChanges (new_gp, new_node.getLength (), 4);
+		}
+		return new IstIterator (this, new_node);
+	}
+
+	public long erase (long point, long length) {
+		long [] iv_offset = new long [1];
+		iv_offset[0] = point;
+
+		IstNode ins_node = recursiveFind (iv_offset, root);
+
+		// iv_offset is the distance into the node that the leaf should be split
+		// 0 is a special case (left delete)
+		long deleted_nodes = 0;
+		while (length > 0) {
+			if (ins_node == null) {
+				// end delete? that's illegal
+				return deleted_nodes;
+			}
+			if (iv_offset[0] == 0) {
+				if (length >= ins_node.getLength ()) {
+					// delete the whole thing
+					length -= ins_node.getLength ();
+					if (ins_node.getParent () == null) {
+						// deleting the root
+						root = null;
+						leftmost = null;
+						rightmost = null;
+						return deleted_nodes + 1;
+					}
+
+					IstNode other_child = null, del_node = null;
+					if (ins_node.getParent ().getLeft () == ins_node) {
+						other_child = ins_node.getParent ().getRight ();
+					} else if (ins_node.getParent ().getRight () == ins_node) {
+						other_child = ins_node.getParent ().getLeft ();
+					}
+					del_node = ins_node;
+					ins_node = increment (ins_node);
+
+					// update tree structure
+					IstNode tmp_parent = other_child.getParent ();
+					IstNode tmp_gp = tmp_parent.getParent ();
+					// java: do something about the = operator!!
+					tmp_parent.setKey (other_child.getKey ());
+					tmp_parent.setLeft (other_child.getLeft ());
+					tmp_parent.setRight (other_child.getRight ());
+					tmp_parent.setParent (other_child.getParent ());
+					tmp_parent.setSubtreeSize (other_child.getSubtreeSize ());
+					tmp_parent.setLength (other_child.getLength ());
+
+					tmp_parent.setParent (tmp_gp);
+					if (tmp_parent.getLeft () != null)
+						tmp_parent.getLeft ().setParent (tmp_parent);
+					if (tmp_parent.getRight () != null)
+						tmp_parent.getRight ().setParent (tmp_parent);
+					if (ins_node == other_child)
+						ins_node = tmp_parent;
+
+					// propogate deletion length thru root
+					tmp_parent = tmp_parent.getParent ();
+					// checkTree( root );
+					propogateChanges (tmp_parent, -del_node.getLength (), -2);
+
+					++deleted_nodes;
+				} else {
+					// crop from start
+					ins_node.getKey ().cropStart (length);
+					// checkTree( root );
+					propogateChanges (ins_node, -length, 0);
+					return deleted_nodes;
+				}
+			} else if (length >= ins_node.getLength () - iv_offset[0]) {
+				// crop from end
+				ins_node.getKey ().cropEnd (
+						ins_node.getLength () - iv_offset[0]);
+				length -= ins_node.getLength () - iv_offset[0];
+				// checkTree( root );
+				propogateChanges (ins_node,
+						-(ins_node.getLength () - iv_offset[0]), 0);
+				ins_node = increment (ins_node);
+				iv_offset[0] = 0;
+			} else {
+				// delete from middle (nastee part)
+				IstNode new_parent = new IstNode ();
+				new_parent.setLeft (ins_node);
+				new_parent.setLength (ins_node.getLength ());
+				new_parent.setRight (new IstNode ());
+				new_parent.getRight ()
+						.setKey ((Key) ins_node.getKey ().copy ());
+				new_parent.getRight ().setLength (
+						ins_node.getLength () - length - iv_offset[0]);
+				new_parent.getRight ().getKey ().cropStart (
+						length + iv_offset[0]);
+				new_parent.getLeft ().getKey ().cropEnd (
+						ins_node.getLength () - iv_offset[0]);
+				new_parent.getLeft ().setLength (iv_offset[0]);
+				new_parent.setParent (ins_node.getParent ());
+				if (new_parent.getParent () == null) {
+					root = new_parent;
+					rightmost = new_parent.getRight ();
+				} else if (new_parent.getParent ().getLeft () == ins_node)
+					new_parent.getParent ().setLeft (new_parent);
+				else if (new_parent.getParent ().getRight () == ins_node)
+					new_parent.getParent ().setRight (new_parent);
+
+				ins_node.setParent (new_parent);
+				new_parent.getRight ().setParent (new_parent);
+				propogateChanges (new_parent, -length, 2);
+				return deleted_nodes;
+			}
+		}
+		return deleted_nodes;
+	}
+
+	/**
+	 * propogates changes to node values up a tree
+	 */
+	void propogateChanges (IstNode cur_node, long length_diff, long subtree_diff) {
+		while (cur_node != null) {
+			cur_node.setLength (cur_node.getLength () + length_diff);
+			cur_node.setSubtreeSize (cur_node.getSubtreeSize () + subtree_diff);
+			cur_node = cur_node.getParent ();
+		}
+	}
+
+	protected IstNode recursiveFind (long [] point, IstNode node) {
+		if (node == null)
+			return null;
+
+		// return this node if it's a leaf
+		if (node.getKey () != null)
+			return node;
+		// look for the next node to recurse to
+		if (point[0] < node.getLength ()) {
+			if (node.getLeft () != null) {
+				if (point[0] < node.getLeft ().getLength ())
+					return recursiveFind (point, node.getLeft ());
+				point[0] -= node.getLeft ().getLength ();
+			}
+			return recursiveFind (point, node.getRight ());
+		}
+		point[0] -= node.getLength ();
+		// out of range
+		return null;
+	}
+
+	IstNode increment (IstNode x) {
+		// find the least-ancestor with another child
+		// and set x to that child
+		while (x.getParent () != null) {
+			if (x == x.getParent ().getLeft ()
+					&& x.getParent ().getRight () != null) {
+				x = x.getParent ().getRight ();
+				break;
+			} else
+				x = x.getParent ();
+		}
+		// if there were no other children to the right then we're at the end
+		if (x.getParent () == null) {
+			x = null;
+			return x;
+		}
+
+		// find the left-most leaf node below x
+		while (x.getKey () == null) {
+			if (x.getLeft () != null)
+				x = x.getLeft ();
+			else if (x.getRight () != null)
+				x = x.getRight ();
+		}
+		return x;
+	}
+
+	IstNode decrement (IstNode x) {
+		if (x != null) {
+			// find the least-ancestor with another child to the left
+			// and set x to that child
+			while (x.getParent () != null) {
+				if (x == x.getParent ().getRight ()
+						&& x.getParent ().getLeft () != null) {
+					x = x.getParent ().getLeft ();
+					break;
+				} else
+					x = x.getParent ();
+			}
+			// if there was no other children to the left then we're at the end
+			// raise hell! (cause an access violation)
+			if (x.getParent () == null)
+				x = null;
+		} else {
+			x = root;
+		}
+
+		// find the right-most leaf node below x
+		while (x.getKey () == null) {
+			if (x.getRight () != null)
+				x = x.getRight ();
+			else if (x.getLeft () != null)
+				x = x.getLeft ();
+		}
+		return x;
+	}
+}
diff --git a/src/org/gel/mauve/tree/IstIterator.java b/src/org/gel/mauve/tree/IstIterator.java
new file mode 100644
index 0000000..c68e702
--- /dev/null
+++ b/src/org/gel/mauve/tree/IstIterator.java
@@ -0,0 +1,31 @@
+package org.gel.mauve.tree;
+
+import java.util.Iterator;
+
+class IstIterator implements Iterator {
+	IstNode node;
+
+	IntervalSequenceTree ist;
+
+	IstIterator (IntervalSequenceTree ist, IstNode node) {
+		this.ist = ist;
+		this.node = node;
+	}
+
+	// Returns true if the iteration has more elements.
+	public boolean hasNext () {
+		return ist.increment (node) != null;
+	}
+
+	// Returns the next element in the iteration.
+	public Object next () {
+		node = ist.increment (node);
+		return node.getKey ();
+	}
+
+	// Removes from the underlying collection the last element returned by the
+	// iterator (optional operation).
+	public void remove () {
+
+	}
+}
\ No newline at end of file
diff --git a/src/org/gel/mauve/tree/IstNode.java b/src/org/gel/mauve/tree/IstNode.java
new file mode 100644
index 0000000..e4840ee
--- /dev/null
+++ b/src/org/gel/mauve/tree/IstNode.java
@@ -0,0 +1,67 @@
+package org.gel.mauve.tree;
+
+class IstNode {
+	private IstNode parent;
+
+	private IstNode left;
+
+	private IstNode right;
+
+	private long subtreeSize;
+
+	private long length;
+
+	private Key key;
+
+	void setParent (IstNode parent) {
+		if (parent == this)
+			throw new RuntimeException (
+					"Error:  attempt to set node's parent as itself.");
+
+		this.parent = parent;
+	}
+
+	IstNode getParent () {
+		return parent;
+	}
+
+	void setLeft (IstNode left) {
+		this.left = left;
+	}
+
+	IstNode getLeft () {
+		return left;
+	}
+
+	void setRight (IstNode right) {
+		this.right = right;
+	}
+
+	IstNode getRight () {
+		return right;
+	}
+
+	void setSubtreeSize (long subtreeSize) {
+		this.subtreeSize = subtreeSize;
+	}
+
+	long getSubtreeSize () {
+		return subtreeSize;
+	}
+
+	void setLength (long length) {
+		this.length = length;
+	}
+
+	long getLength () {
+		return length;
+	}
+
+	void setKey (Key key) {
+		this.key = key;
+	}
+
+	Key getKey () {
+		return key;
+	}
+};
diff --git a/src/org/gel/mauve/tree/Key.java b/src/org/gel/mauve/tree/Key.java
new file mode 100644
index 0000000..106c203
--- /dev/null
+++ b/src/org/gel/mauve/tree/Key.java
@@ -0,0 +1,17 @@
+package org.gel.mauve.tree;
+
+/**
+ * Key specifies the interface allowed for intervals that can be stored in an
+ * interval sequence tree.
+ */
+interface Key {
+	public long getLength ();
+
+	public long getSeqLength ();
+
+	public void cropStart (long size);
+
+	public void cropEnd (long size);
+
+	public Object copy ();
+}
diff --git a/src/org/gel/mauve/tree/TreeStore.java b/src/org/gel/mauve/tree/TreeStore.java
new file mode 100644
index 0000000..3f20654
--- /dev/null
+++ b/src/org/gel/mauve/tree/TreeStore.java
@@ -0,0 +1,98 @@
+package org.gel.mauve.tree;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+public class TreeStore implements Serializable {
+	final static int NULL_REF = -1;
+
+	private final static int INITIAL_SIZE = 10;
+
+	private final static float SCALE_FACTOR = 1.1f;
+
+	private int nodeCount = 0;
+
+	public int [] parent = new int [INITIAL_SIZE];
+
+	public int [] left = new int [INITIAL_SIZE];
+
+	public int [] right = new int [INITIAL_SIZE];
+
+	public long [] length = new long [INITIAL_SIZE];
+
+	public long [] seqLength = new long [INITIAL_SIZE];
+
+	public Key [] key = new Key [INITIAL_SIZE];
+
+	public TreeStore () {
+		Arrays.fill (parent, NULL_REF);
+		Arrays.fill (left, NULL_REF);
+		Arrays.fill (right, NULL_REF);
+	}
+
+	public int createGistNode () {
+		if (nodeCount >= parent.length) {
+			resizeArrays ();
+		}
+		int val = nodeCount;
+		nodeCount++;
+		return val;
+	}
+
+	private void resizeArrays () {
+		int newSize = (int) (parent.length * SCALE_FACTOR);
+
+		int [] new_parent = new int [newSize];
+		Arrays.fill (new_parent, TreeStore.NULL_REF);
+		System.arraycopy (parent, 0, new_parent, 0, parent.length);
+		parent = new_parent;
+
+		int [] new_left = new int [newSize];
+		Arrays.fill (new_left, TreeStore.NULL_REF);
+		System.arraycopy (left, 0, new_left, 0, left.length);
+		left = new_left;
+
+		int [] new_right = new int [newSize];
+		Arrays.fill (new_right, TreeStore.NULL_REF);
+		System.arraycopy (right, 0, new_right, 0, right.length);
+		right = new_right;
+
+		long [] new_length = new long [newSize];
+		System.arraycopy (length, 0, new_length, 0, length.length);
+		length = new_length;
+
+		long [] new_seq_length = new long [newSize];
+		System.arraycopy (seqLength, 0, new_seq_length, 0, seqLength.length);
+		seqLength = new_seq_length;
+
+		Key [] new_key = new Key [newSize];
+		System.arraycopy (key, 0, new_key, 0, key.length);
+		key = new_key;
+	}
+
+	public void pruneArrays () {
+		int [] new_parent = new int [nodeCount];
+		System.arraycopy (parent, 0, new_parent, 0, nodeCount);
+		parent = new_parent;
+
+		int [] new_left = new int [nodeCount];
+		System.arraycopy (left, 0, new_left, 0, nodeCount);
+		left = new_left;
+
+		int [] new_right = new int [nodeCount];
+		System.arraycopy (right, 0, new_right, 0, nodeCount);
+		right = new_right;
+
+		long [] new_length = new long [nodeCount];
+		System.arraycopy (length, 0, new_length, 0, nodeCount);
+		length = new_length;
+
+		long [] new_seq_length = new long [nodeCount];
+		System.arraycopy (seqLength, 0, new_seq_length, 0, nodeCount);
+		seqLength = new_seq_length;
+
+		Key [] new_key = new Key [nodeCount];
+		System.arraycopy (key, 0, new_key, 0, nodeCount);
+		key = new_key;
+	}
+}
diff --git a/test/org/gel/mauve/LcbViewerModelTest.java b/test/org/gel/mauve/LcbViewerModelTest.java
new file mode 100644
index 0000000..3013bab
--- /dev/null
+++ b/test/org/gel/mauve/LcbViewerModelTest.java
@@ -0,0 +1,59 @@
+package org.gel.mauve;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+public class LcbViewerModelTest extends TestCase {
+
+	public void testWeightAdjustmentBug() throws IOException, MauveFormatException
+	{
+		XmfaViewerModel model1 = (XmfaViewerModel) ModelBuilder.buildModel(new File("testdata/small.alignment"), null);
+		XmfaViewerModel model2 = (XmfaViewerModel) ModelBuilder.buildModel(new File("testdata/small.alignment"), null);
+		
+		// Change weight and reorder.
+		model1.updateLCBweight(50, true);
+		model1.reorderSequences(new int[] {1,0});
+		
+		// Reorder and change weight.
+		model2.reorderSequences(new int[] {1,0});
+		model2.updateLCBweight(50, true);
+		
+		Genome g0 = model1.getGenomeBySourceIndex(0);
+		Genome g1 = model1.getGenomeBySourceIndex(1);
+		
+		Genome g0b = model2.getGenomeBySourceIndex(0);
+		Genome g1b = model2.getGenomeBySourceIndex(1);
+		
+		// The order of operations should not matter.
+		assertEquals(305, model1.getVisibleLcb(0).getLeftEnd(g0));
+		assertEquals(566, model1.getVisibleLcb(0).getLeftEnd(g1));
+		assertEquals(model1.getVisibleLcb(0).getLeftEnd(g0), model2.getVisibleLcb(0).getLeftEnd(g0b));
+		assertEquals(model1.getVisibleLcb(0).getLeftEnd(g1), model2.getVisibleLcb(0).getLeftEnd(g1b));
+	}
+	
+	public void testRearrangementEliminatesColorsInMumsFile() throws IOException, MauveFormatException
+	{
+		BaseViewerModel model = ModelBuilder.buildModel(new File("testdata/small.mums"), null);
+		
+		// Reordering sequences certainly shouldn't affect color!
+		Match match = model.getMatch(0);
+		Color oldColor = match.color;
+		model.reorderSequences(new int[] {1,0});
+		assertEquals(oldColor, match.color);
+	}
+	
+	public void testUpdateLCBWeightForMauveFile() throws IOException, MauveFormatException
+	{
+		LcbViewerModel model = (LcbViewerModel) ModelBuilder.buildModel(new File("testdata/small.mauve"), null);
+		model.sanityCheck();
+		model.updateLCBweight(1389, false);
+		model.sanityCheck();
+	}
+
+	
+
+	
+}
diff --git a/test/org/gel/mauve/ModelLoader.java b/test/org/gel/mauve/ModelLoader.java
new file mode 100644
index 0000000..436c56e
--- /dev/null
+++ b/test/org/gel/mauve/ModelLoader.java
@@ -0,0 +1,44 @@
+package org.gel.mauve;
+
+import java.io.File;
+import java.io.IOException;
+
+
+public class ModelLoader
+{
+
+    public static void main(String[] args) throws IOException, MauveFormatException
+    {
+        
+        String filename;
+        if (args.length == 1)
+        {
+            filename = args[0];
+        }
+        else if (args.length == 2)
+        {
+            filename = args[1];
+        }
+        else
+        {
+            throw new RuntimeException("Required filename");
+        }
+
+        BaseViewerModel model = ModelBuilder.buildModel(new File(filename), null);
+        
+        if (args.length > 1)
+        {
+            synchronized(model)
+            {
+                try
+                {
+                    model.wait();
+                } 
+                catch (InterruptedException e)
+                {
+                }
+            }
+            
+        }
+    }
+}
diff --git a/test/org/gel/mauve/XMFAAlignmentTest.java b/test/org/gel/mauve/XMFAAlignmentTest.java
new file mode 100644
index 0000000..fb5b562
--- /dev/null
+++ b/test/org/gel/mauve/XMFAAlignmentTest.java
@@ -0,0 +1,95 @@
+package org.gel.mauve;
+
+import java.io.IOException;
+
+
+public class XMFAAlignmentTest
+{
+	/**
+	 * Checks the XMFA file to ensure that every nucleotide in the 
+	 * relevant sequences are represented exactly once
+	 * @return true if the parsed XMFA is valid
+	 */
+	boolean validateXMFA(XMFAAlignment xmfa) throws IOException {
+	    //FIXME: Return this test to usability.
+	    return false;
+//		boolean valid = true;
+//		LCB[] m_lcb_list = new LCB[ xmfa.intervals.length ];
+//		for( int ivI = 0; ivI < xmfa.intervals.length; ivI++ ){
+//			LCB lcb = new LCB();
+//			lcb.left_end = new long[ xmfa.intervals[ ivI ].starts.length ];
+//			lcb.right_end = new long[ xmfa.intervals[ ivI ].starts.length ];
+//			lcb.reverse = new boolean[ xmfa.intervals[ ivI ].starts.length ];
+//			System.arraycopy( xmfa.intervals[ ivI ].starts, 0, lcb.left_end, 0, lcb.left_end.length );
+//			System.arraycopy( xmfa.intervals[ ivI ].lengths, 0, lcb.right_end, 0, lcb.right_end.length );
+//			m_lcb_list[ ivI ] = lcb;
+//		}
+//
+//		for( int seqI = 0; seqI < xmfa.seq_count; seqI++ ){
+//			// sort the lcb list on the current sequence
+//			LcbStartComparator lcb_comp = xmfa.new LcbStartComparator( seqI );
+//			Arrays.sort( m_lcb_list, lcb_comp );
+//			long cur_coord = 0;
+//			for( int lcbI = 0; lcbI < m_lcb_list.length; lcbI++ ){
+//				if( m_lcb_list[ lcbI ].left_end[ seqI ] == 0 )
+//					continue;	// this LCB isn't defined for this seq, skip it
+//				if( cur_coord != m_lcb_list[ lcbI ].left_end[ seqI ] - 1 ){
+//					System.out.println( "Missing " + cur_coord + " from seq " + seqI );
+//					System.out.println( "m_lcb_list[ lcbI ].left_end[ seqI ] " + m_lcb_list[ lcbI ].left_end[ seqI ] );
+//					System.out.println();
+//					valid = false;
+//				}
+//				cur_coord = m_lcb_list[ lcbI ].right_end[ seqI ];
+//			}
+//		}
+//		
+//// special test case:
+//		int debug_val = 3277;
+//		long lento = xmfa.gis_tree[ debug_val ][ 1 ].length();
+//		
+//		xmfa.readRawSequence( debug_val, 1, 0, lento );
+//
+//
+//		//
+//		// read each lcb and verify that it has the correct number of characters
+//		//
+//		for( int ivI = 0; ivI < xmfa.intervals.length; ivI++ ){
+//			for( int seqI = 0; seqI < xmfa.seq_count; seqI++ ){
+//				if( ivI == debug_val )
+//					System.out.print("");
+//				long seq_len = xmfa.intervals[ ivI ].lengths[ seqI ] - xmfa.intervals[ ivI ].starts[ seqI ] + 1;
+//				if( xmfa.intervals[ ivI ].lengths[ seqI ] == 0 && xmfa.intervals[ ivI ].starts[ seqI ] == 0 )
+//					seq_len = 0;
+//				long len = xmfa.gis_tree[ ivI ][ seqI ].length();
+//				if( seq_len != xmfa.gis_tree[ ivI ][ seqI ].sequenceLength() ){
+//					System.out.println( "Error: conflicting interval and gis_tree sequence length records" );
+//					System.out.println( "At interval: " + ivI + " sequence: " + seqI + " iv seqlen: " + 
+//								seq_len + " gis_tree len: " + xmfa.gis_tree[ ivI ][ seqI ].sequenceLength() );
+//					System.out.println( "left end: " + xmfa.intervals[ ivI ].starts[ seqI ] + " right end: " +
+//					        xmfa.intervals[ ivI ].lengths[ seqI ] );
+//					valid = false;
+//				}
+//				byte[] buf = (byte[])xmfa.readRawSequence( ivI, seqI, 0, len );
+//				long char_count = 0;
+//				for( int bufI = 0; bufI < buf.length; bufI++ )
+//					if( buf[ bufI ] != '\r' && buf[ bufI ] != '\n' && buf[ bufI ] != '-' )
+//						char_count++;
+//				if( char_count != seq_len ){
+//					System.out.println( "Error in interval " + ivI + " seq: " + seqI +
+//						"\nExpected " + seq_len + " chars but read " + char_count );
+//					System.out.println( "left end: " + xmfa.intervals[ ivI ].starts[ seqI ] + " right end: " +
+//					        xmfa.intervals[ ivI ].lengths[ seqI ] );
+//					valid = false;
+//				}
+//			}
+//		}
+//		
+//		if( valid )
+//			System.out.println( "XMFA validated\n" );
+//		return valid;
+	}
+	
+	
+
+
+}
diff --git a/test/org/gel/mauve/format/DelegatingSequenceTest.java b/test/org/gel/mauve/format/DelegatingSequenceTest.java
new file mode 100644
index 0000000..65c7b04
--- /dev/null
+++ b/test/org/gel/mauve/format/DelegatingSequenceTest.java
@@ -0,0 +1,89 @@
+package org.gel.mauve.format;
+
+import java.io.FileNotFoundException;
+
+import junit.framework.TestCase;
+
+import org.biojava.bio.Annotation;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.impl.SimpleSequence;
+import org.biojava.bio.symbol.AlphabetManager;
+import org.biojava.bio.symbol.AtomicSymbol;
+import org.biojava.bio.symbol.DummySymbolList;
+import org.biojava.bio.symbol.FiniteAlphabet;
+import org.biojava.bio.symbol.SingletonAlphabet;
+
+
+public class DelegatingSequenceTest extends TestCase
+{
+    static AtomicSymbol sym = AlphabetManager.createSymbol("X");
+    static FiniteAlphabet a = new SingletonAlphabet(sym);
+    
+    MockFormat format;
+
+    public void testInitiallyNoLoading()
+    {
+        Sequence s = testingDelegate(42);
+        assertEquals(0, format.innerSequenceReadCount);
+        s.symbolAt(13);
+        assertEquals(1, format.innerSequenceReadCount);
+    }
+    
+    public void testOneSymbolWorks()
+    {
+        Sequence s = testingDelegate(1);
+        assertEquals(1, s.length());
+        assertEquals(sym, s.symbolAt(1));
+    }
+
+    public void testZeroSymbolsWorks()
+    {
+        Sequence s = testingDelegate(0);
+        assertEquals(0, s.length());
+    }
+    
+    public void testHighOutsideThrowsException()
+    {
+        Sequence s = testingDelegate(1);
+        try
+        {
+            s.symbolAt(2);
+        }
+        catch (IndexOutOfBoundsException e)
+        {
+            return;
+        }
+        fail();
+    }
+    
+    public void testLowOutsideThrowsException()
+    {
+        Sequence s = testingDelegate(0);
+        try
+        {
+            s.symbolAt(0);
+        }
+        catch (IndexOutOfBoundsException e)
+        {
+            return;
+        }
+        fail();
+    }
+    
+    private Sequence testingDelegate(int length)
+    {
+        Sequence dummySequence = new SimpleSequence(new DummySymbolList(a, length), "someurn", "somename", Annotation.EMPTY_ANNOTATION);
+        format = new MockFormat(dummySequence);
+        try
+        {
+            return new DelegatingSequence(dummySequence, format, null, 0);
+        }
+        catch (FileNotFoundException e)
+        {
+            // Never should happen.
+            throw new RuntimeException(e);
+        }
+    }
+    
+    
+}
diff --git a/test/org/gel/mauve/format/MockFormat.java b/test/org/gel/mauve/format/MockFormat.java
new file mode 100644
index 0000000..3515429
--- /dev/null
+++ b/test/org/gel/mauve/format/MockFormat.java
@@ -0,0 +1,118 @@
+package org.gel.mauve.format;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.NoSuchElementException;
+
+import org.biojava.bio.BioException;
+import org.biojava.bio.seq.Sequence;
+import org.biojava.bio.seq.SequenceIterator;
+import org.gel.mauve.FilterCacheSpec;
+import org.gel.mauve.SupportedFormat;
+
+
+public class MockFormat implements SupportedFormat
+{
+    Sequence inner;
+    int innerSequenceReadCount;
+    
+    public MockFormat(Sequence inner)
+    {
+        this.inner = inner;
+    }
+    
+    public SequenceIterator readFile(File file)
+    {
+        return new SequenceIterator()
+        {
+            int counter = 0;
+
+            public boolean hasNext()
+            {
+                return counter == 0;
+            }
+
+            public Sequence nextSequence() throws NoSuchElementException, BioException
+            {
+                if (counter == 0) 
+                {
+                    counter++;
+                    return inner; 
+                }
+                else
+                {
+                    throw new NoSuchElementException();
+                }
+            }
+            
+        };
+    }
+
+    public void validate(Sequence s, File file, int index) throws FileNotFoundException
+    {
+        return;
+    }
+
+    public Sequence readInnerSequence(File file, int index)
+    {
+        innerSequenceReadCount++;
+        
+        if (index == 0)
+        {
+            return inner;
+        }
+        else
+        {
+            throw new IndexOutOfBoundsException("index: " + index);
+        }
+    }
+
+    public SequenceIterator makeIterator(File file)
+    {
+        return new SequenceIterator()
+        {
+            int counter = 0;
+
+            public boolean hasNext()
+            {
+                return counter == 0;
+            }
+
+            public Sequence nextSequence() throws NoSuchElementException, BioException
+            {
+                if (counter == 0) 
+                {
+                    counter++;
+                    return inner; 
+                }
+                else
+                {
+                    throw new NoSuchElementException();
+                }
+            }
+            
+        };
+    }
+
+    public Sequence makeDelegate(Sequence s, File source, int index) throws FileNotFoundException
+    {
+        return new DelegatingSequence(inner, this, source, index);
+    }
+
+    public String getSequenceName(Sequence s)
+    {
+        return s.getName();
+    }
+
+    public String getChromosomeName(Sequence s)
+    {
+        return s.getName();
+    }
+
+    public FilterCacheSpec[] getFilterCacheSpecs()
+    {
+        return new FilterCacheSpec[0];
+    }
+    
+    public boolean isRich(){ return false; }
+}
diff --git a/test/org/gel/mauve/tree/GistNode.java b/test/org/gel/mauve/tree/GistNode.java
new file mode 100644
index 0000000..3a2ab34
--- /dev/null
+++ b/test/org/gel/mauve/tree/GistNode.java
@@ -0,0 +1,58 @@
+package org.gel.mauve.tree;
+
+public class GistNode {
+	
+    int index;
+    GISTree t;
+    
+	private GistNode(int index, GISTree t)
+	{
+	    this.index = index;
+	    this.t = t;
+	}
+
+    public GistNode getParent()
+    {
+        return loadNode(t.getParent(index), t);
+    }
+
+    public GistNode getLeft()
+    {
+        return loadNode(t.getLeft(index), t);
+    }
+
+    public GistNode getRight()
+    {
+        return loadNode(t.getRight(index), t);
+    }
+    
+    public long getLength()
+    {
+        return t.getLength(index);
+    }
+
+    public long getSeqLength()
+    {
+        return t.getSeqLength(index);
+    }
+
+    public Key getKey()
+    {
+        return t.getKey(index);
+    }
+
+	public static GistNode loadNode(int index, GISTree t)
+	{
+	    if (index == TreeStore.NULL_REF)
+	    {
+	        return null;
+	    }
+	    else
+	    {
+	        return new GistNode(index, t);
+	    }
+	}
+	
+	
+	
+}
diff --git a/test/org/gel/mauve/tree/GistTest.java b/test/org/gel/mauve/tree/GistTest.java
new file mode 100644
index 0000000..ec19ed6
--- /dev/null
+++ b/test/org/gel/mauve/tree/GistTest.java
@@ -0,0 +1,194 @@
+package org.gel.mauve.tree;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.RandomAccessFile;
+
+import junit.framework.TestCase;
+
+public class GistTest extends TestCase {
+	
+	
+	//TODO: Actually run this test!
+	void testGIST(GISTree[][] gis_tree, RandomAccessFile xmfa_file){
+		// test the GIST to ensure it maintains proper element
+		// ordering through a series of queries
+		java.util.Random randy = new java.util.Random();
+		int iter;
+		for( int searchI = 0; searchI < 10000; searchI++ ){
+			long search_index = 0;
+			if( randy.nextInt() % 2 == 1 ){
+				search_index = randy.nextInt() % gis_tree[0][0].length();
+				iter = gis_tree[0][0].find( search_index );
+			}else{
+				search_index = randy.nextInt() % gis_tree[0][0].sequenceLength();
+				iter = gis_tree[0][0].find_seqindex( search_index );
+			}
+		}
+		
+		// now that we've searched the tree many times, write the 
+		// sequence back out for comparison
+		int node_count = (int)nodeCount(gis_tree[0][0]);
+		System.out.println("node count: " + node_count);
+		System.out.println("seq length: " + gis_tree[0][0].sequenceLength() );
+		System.out.println("total length: " + gis_tree[0][0].length() );
+		int iters = 0;
+		try{
+		File my_outtie = new File( "gist_test.txt" );
+		FileWriter out_writer = new FileWriter( my_outtie );
+		iter = gis_tree[0][0].find(0);
+		while( true ){
+			FileKey fk = null;
+			GapKey gk = null;
+			iters++;
+			try{
+				fk = (FileKey)gis_tree[0][0].getKey(iter);
+			}catch( ClassCastException cca ){
+				// if we started in a gap the next one should be a FileKey
+				try{
+					gk = (GapKey)gis_tree[0][0].getKey(iter);
+				}catch( ClassCastException ccb ){
+					throw new RuntimeException(ccb);
+				}
+			}
+			if( fk != null ){
+				// write out the sequence
+			    // TODO: What if FLength is too long?
+				byte[] raw_buf = new byte[ (int)fk.getFLength() ];
+				xmfa_file.seek( fk.getOffset() );
+				xmfa_file.read( raw_buf, 0, (int)fk.getFLength() );
+				int seqI = 0;
+				// count the seq length minus newlines
+				for( int rawI = 0; rawI < raw_buf.length; rawI++ )
+					if( raw_buf[ rawI ] != '\r' && raw_buf[ rawI ] != '\n' )
+						seqI++;
+				// extract the sequence without newlines
+				char[] seq_buf = new char[ seqI ];
+				seqI = 0;
+				for( int rawI = 0; rawI < raw_buf.length; rawI++ )
+					if( raw_buf[ rawI ] != '\r' && raw_buf[ rawI ] != '\n' )
+						seq_buf[ seqI++ ] = (char)raw_buf[ rawI ];
+				// write the sequence without newlines
+				out_writer.write( seq_buf );
+			}
+			if( gk != null ){
+				// write out some gaps
+				char[] gap_buf = new char[ (int)gk.getLength() ];
+				for( int gapI = 0; gapI < gap_buf.length; gapI++ )
+					gap_buf[ gapI ] = '-';
+				out_writer.write( gap_buf );
+			}
+
+	        Object iterNext = gis_tree[0][0].increment(iter);
+			if(iterNext == null)
+				break;
+		}
+		out_writer.flush();
+		out_writer.close();
+		}catch( Exception e ){
+			e.printStackTrace();
+		}
+		System.out.println( "iters: " + iters );
+		node_count = (int)nodeCount(gis_tree[0][0]);
+		System.out.println("node count: " + node_count);
+		System.out.println("seq length: " + gis_tree[0][0].sequenceLength() );
+		System.out.println("total length: " + gis_tree[0][0].length() );
+	}
+
+	/** counts the number of nodes in the subtree below x (recursive) */
+	long countNodes( GistNode x ) {
+		if( x == null )
+			return 0;
+		return countNodes( x.getLeft() ) + countNodes( x.getRight() ) + 1;
+	}
+
+	/**
+	 * Returns number of nodes in this tree
+	 */
+	public long nodeCount(GISTree gist){
+		
+		return gist.rootIndex == TreeStore.NULL_REF ? 0 : countNodes(GistNode.loadNode(gist.rootIndex, gist));
+	}
+
+	/**
+	 * validate that recorded subtree lengths and node lengths are consistent
+	 */
+	long checkNodeLengths( GistNode cur_node ){
+		if( cur_node == null )
+			return 0;
+
+		long left_len = cur_node.getLeft() != null ? cur_node.getLeft().getLength() : 0;
+		long right_len = cur_node.getRight() != null ? cur_node.getRight().getLength() : 0;
+		// do left_len and right_len match the actual subtree lengths?
+		if( left_len != checkNodeLengths( cur_node.getLeft() ) )
+			System.err.println( "freakout\n" );
+		if( right_len != checkNodeLengths( cur_node.getRight() ) )
+			System.err.println( "freakout\n" );
+		// do they all sum up to the correct value?
+		if( left_len + right_len + cur_node.getKey().getLength() != cur_node.getLength() ){
+			System.err.println( "freakout\n" );
+			System.err.println(cur_node.getKey());
+		}
+		return cur_node.getLength();
+	}
+
+	/**
+	 * validate that the recorded sequence length and actual sequence length
+	 * below a node are consistent with each other
+	 */
+	long checkNodeSeqLengths( GistNode cur_node ){
+		if( cur_node == null )
+			return 0;
+
+		long left_len = cur_node.getLeft() != null ? cur_node.getLeft().getSeqLength() : 0;
+		long right_len = cur_node.getRight() != null ? cur_node.getRight().getSeqLength() : 0;
+		// do left_len and right_len match the actual subtree lengths?
+		if( left_len != checkNodeSeqLengths( cur_node.getLeft() ) )
+			System.err.println( "freakout\n" );
+		if( right_len != checkNodeSeqLengths( cur_node.getRight() ) )
+			System.err.println( "freakout\n" );
+		// do they all sum up to the correct value?
+		if( left_len + right_len + cur_node.getKey().getSeqLength() != cur_node.getSeqLength() ){
+			System.err.println( "freakout\n" );
+			System.err.println(cur_node.getKey());
+		}
+		return cur_node.getSeqLength();
+	}
+
+	/** 
+	 * validate the structure of the tree, e.g. parents and children 
+	 * are linked correctly
+	 */
+	void checkTree( GistNode cur_node ){
+		if( cur_node != null ){
+			if( cur_node.getLeft() != null && !nodesEqual(cur_node.getLeft().getParent(), cur_node))
+				System.err.println( "freakout\n" );
+			if( cur_node.getRight() != null && !nodesEqual(cur_node.getRight().getParent(), cur_node))
+				System.err.println( "freakout\n" );
+			checkTree( cur_node.getLeft() );
+			checkTree( cur_node.getRight() );
+		}
+	}
+	
+	/**
+	 * check the tree for agreement between tree data structures and the
+	 * stored intervals
+	 */
+	void checkTree(GISTree gist) {
+		
+		GistNode root = GistNode.loadNode(gist.rootIndex, gist);
+		
+		checkTree( root );
+		checkNodeLengths( root );
+		checkNodeSeqLengths( root );
+	}
+	
+    
+    private boolean nodesEqual(GistNode node1, GistNode node2)
+    {
+        if (node1 == null) return node2 == null;
+        
+        return node1.index == node2.index;
+    }
+	
+}
diff --git a/testdata/S_bayanus_small.fasta b/testdata/S_bayanus_small.fasta
new file mode 100644
index 0000000..f2f6075
--- /dev/null
+++ b/testdata/S_bayanus_small.fasta
@@ -0,0 +1,12 @@
+>contig_0
+TTATTCAACATCCTTCCACACACACACACACACTCTCACACACACACTATATACATAATATTTTATAACCGCCGCTGCTA
+ATTCTTCGCAAGGAAACCCGCCTCTTTCTCTCTCTCTATCTCTATCTCTCCCCACGGTGAGTTATACGCCCTTTATAATA
+CAATAGTTTTCTTTCTCTCTTTTACCCACTACCCTTATTTTCTTTTACCCGGACTGCGCTTTTCTCGCACTACGTAATTG
+>contig_1
+AAGTCATAACTATACAGCACCTTTGTTGGGTCTATGATCCGGAGACCTGTGAGGCCAGACAGTACTAGACAGCCTATGGT
+CCACAATCTCGTCACCACAGGAATGTCCCCTAGGATATTCAGTATCACAGCATCCATAGTCGTTTGATCCTAAGTTTTCT
+>contig_2
+TGGCCCACTACACCAGACAAAATAGTCATGATTGCAAGAGATGTGGAGCTGATGCTGAAAACACCAAAAGTCGTTGATGA
+TGCAGCATGTTTTGTGTTTACATTTCAAGATACCGCAGATTGTTAAGTTTTTAGGATAATTGATAATCGTTAGAAGAATG
+TTATGCTTATCCACCACACCTCATTAAAACGTGTATTTACTAGAAATTTAGAAGTAGGCACCTAAGCAAAAACCCTCTAC
+AAGTATCAAGTACAAAAGATATTCGAGTATGTACAACTCTTTCGGCAAACTTC
diff --git a/testdata/S_bayanus_small.raw b/testdata/S_bayanus_small.raw
new file mode 100644
index 0000000..b33150a
--- /dev/null
+++ b/testdata/S_bayanus_small.raw
@@ -0,0 +1,9 @@
+TTATTCAACATCCTTCCACACACACACACACACTCTCACACACACACTATATACATAATATTTTATAACCGCCGCTGCTA
+ATTCTTCGCAAGGAAACCCGCCTCTTTCTCTCTCTCTATCTCTATCTCTCCCCACGGTGAGTTATACGCCCTTTATAATA
+CAATAGTTTTCTTTCTCTCTTTTACCCACTACCCTTATTTTCTTTTACCCGGACTGCGCTTTTCTCGCACTACGTAATTG
+AAGTCATAACTATACAGCACCTTTGTTGGGTCTATGATCCGGAGACCTGTGAGGCCAGACAGTACTAGACAGCCTATGGT
+CCACAATCTCGTCACCACAGGAATGTCCCCTAGGATATTCAGTATCACAGCATCCATAGTCGTTTGATCCTAAGTTTTCT
+TGGCCCACTACACCAGACAAAATAGTCATGATTGCAAGAGATGTGGAGCTGATGCTGAAAACACCAAAAGTCGTTGATGA
+TGCAGCATGTTTTGTGTTTACATTTCAAGATACCGCAGATTGTTAAGTTTTTAGGATAATTGATAATCGTTAGAAGAATG
+TTATGCTTATCCACCACACCTCATTAAAACGTGTATTTACTAGAAATTTAGAAGTAGGCACCTAAGCAAAAACCCTCTAC
+AAGTATCAAGTACAAAAGATATTCGAGTATGTACAACTCTTTCGGCAAACTTC
diff --git a/testdata/S_cerevisiae_small.gbk b/testdata/S_cerevisiae_small.gbk
new file mode 100644
index 0000000..888b932
--- /dev/null
+++ b/testdata/S_cerevisiae_small.gbk
@@ -0,0 +1,207 @@
+LOCUS       NC_001133               1000 bp    DNA     linear   PLN 07-FEB-2005
+DEFINITION  Saccharomyces cerevisiae chromosome I, complete chromosome
+            sequence.
+ACCESSION   NC_001133
+VERSION     NC_001133.5  GI:50593113
+KEYWORDS    .
+SOURCE      Saccharomyces cerevisiae (baker's yeast)
+  ORGANISM  Saccharomyces cerevisiae
+            Eukaryota; Fungi; Ascomycota; Saccharomycotina; Saccharomycetes;
+            Saccharomycetales; Saccharomycetaceae; Saccharomyces.
+REFERENCE   1  (bases 1 to 1000)
+  AUTHORS   Goffeau,A., Barrell,B.G., Bussey,H., Davis,R.W., Dujon,B.,
+            Feldmann,H., Galibert,F., Hoheisel,J.D., Jacq,C., Johnston,M.,
+            Louis,E.J., Mewes,H.W., Murakami,Y., Philippsen,P., Tettelin,H. and
+            Oliver,S.G.
+  TITLE     Life with 6000 genes
+  JOURNAL   Science 274 (5287), 546-547 (1996)
+  MEDLINE   97002444
+   PUBMED   8849441
+REFERENCE   2  (bases 1 to 1000)
+  AUTHORS   Bussey,H., Kaback,D.B., Zhong,W., Vo,D.T., Clark,M.W., Fortin,N.,
+            Hall,J., Ouellette,B.F., Keng,T., Barton,A.B. et al.
+  TITLE     The nucleotide sequence of chromosome I from Saccharomyces
+            cerevisiae
+  JOURNAL   Proc. Natl. Acad. Sci. U.S.A. 92 (9), 3809-3813 (1995)
+  MEDLINE   95249563
+   PUBMED   7731988
+REFERENCE   3  (bases 1 to 1000)
+  AUTHORS   Saccharomyces Genome Database (yeast-curator at genome.stanford.edu).
+  TITLE     Direct Submission
+  JOURNAL   Submitted (17-NOV-1999) Department of Genetics, Stanford
+            University, Saccharomyces Genome Database, Stanford, CA 94305-5120,
+            USA
+REFERENCE   4  (bases 1 to 1000)
+  AUTHORS   .
+  CONSRTM   NCBI Genome Project
+  TITLE     Direct Submission
+  JOURNAL   Submitted (10-NOV-1999) National Center for Biotechnology
+            Information, NIH, Bethesda, MD 20894, USA
+COMMENT     REVIEWED REFSEQ: This record has been curated by SGD.
+            On Jul 26, 2004 this sequence version replaced gi:41629665.
+            REFSEQ: This reference sequence was provided by the Saccharomyces
+            Genome Database (SGD).
+FEATURES             Location/Qualifiers
+     source          1..1000
+                     /organism="Saccharomyces cerevisiae"
+                     /mol_type="genomic DNA"
+                     /strain="S288C"
+                     /db_xref="taxon:4932"
+                     /chromosome="I"
+     repeat_region   1..801
+                     /gene="TEL01L"
+                     /note="I left telomeric region"
+                     /evidence=not_experimental
+                     /rpt_family="Telomeric Region"
+                     /db_xref="SGD:S000028862"
+     repeat_region   complement(1..62)
+                     /gene="TEL01L-TR"
+                     /note="I left telomere TG(1-3)"
+                     /evidence=not_experimental
+                     /rpt_family="Telomeric Repeat"
+                     /db_xref="SGD:S000028864"
+     repeat_region   complement(63..336)
+                     /gene="TEL01L-XR"
+                     /note="X element Combinatorial Repeats (DCBA) containing
+                     Tbf1p binding sites. Formerly called SubTelomeric Repeats"
+                     /evidence=not_experimental
+                     /rpt_family="X element Combinatorial Repeats"
+                     /db_xref="SGD:S000028866"
+     repeat_region   complement(337..801)
+                     /gene="TEL01L-XC"
+                     /note="X element Core sequence"
+                     /evidence=not_experimental
+                     /rpt_family="X element Core sequence"
+                     /db_xref="SGD:S000028865"
+ORIGIN      
+        1 ccacaccaca cccacacacc cacacaccac accacacacc acaccacacc cacacacaca
+       61 catcctaaca ctaccctaac acagccctaa tctaaccctg gccaacctgt ctctcaactt
+      121 accctccatt accctgcctc cactcgttac cctgtcccat tcaaccatac cactccgaac
+      181 caccatccat ccctctactt actaccactc acccaccgtt accctccaat tacccatatc
+      241 caacccactg ccacttaccc taccattacc ctaccatcca ccatgaccta ctcaccatac
+      301 tgttcttcta cccaccatat tgaaacgcta acaaatgatc gtaaataaca cacacgtgct
+      361 taccctacca ctttatacca ccaccacatg ccatactcac cctcacttgt atactgattt
+      421 tacgtacgca cacggatgct acagtatata ccatctcaaa cttaccctac tctcagattc
+      481 cacttcactc catggcccat ctctcactga atcagtacca aatgcactca catcattatg
+      541 cacggcactt gcctcagcgg tctataccct gtgccattta cccataacgc ccatcattat
+      601 ccacattttg atatctatat ctcattcggc ggtcccaaat attgtataac tgcccttaat
+      661 acatacgtta taccactttt gcaccatata cttaccactc catttatata cacttatgtc
+      721 aatattacag aaaaatcccc acaaaaatca cctaaacata aaaatattct acttttcaac
+      781 aataatacat aaacatattg gcttgtggta gcaacactat catggtatca ctaacgtaaa
+      841 agttcctcaa tattgcaatt tgcttgaacg gatgctattt cagaatattt cgtacttaca
+      901 caggccatac attagaataa tatgtcacat cactgtcgta acactcttta ttcaccgagc
+      961 aataatacgg tagtggctca aactcatgcg ggtgctatga
+//
+LOCUS       NC_001134               1000 bp    DNA     linear   PLN 07-FEB-2005
+DEFINITION  Saccharomyces cerevisiae chromosome II, complete chromosome
+            sequence.
+ACCESSION   NC_001134
+VERSION     NC_001134.7  GI:50593115
+KEYWORDS    .
+SOURCE      Saccharomyces cerevisiae (baker's yeast)
+  ORGANISM  Saccharomyces cerevisiae
+            Eukaryota; Fungi; Ascomycota; Saccharomycotina; Saccharomycetes;
+            Saccharomycetales; Saccharomycetaceae; Saccharomyces.
+REFERENCE   1  (bases 1 to 813178)
+  AUTHORS   Goffeau,A., Barrell,B.G., Bussey,H., Davis,R.W., Dujon,B.,
+            Feldmann,H., Galibert,F., Hoheisel,J.D., Jacq,C., Johnston,M.,
+            Louis,E.J., Mewes,H.W., Murakami,Y., Philippsen,P., Tettelin,H. and
+            Oliver,S.G.
+  TITLE     Life with 6000 genes
+  JOURNAL   Science 274 (5287), 546-547 (1996)
+  MEDLINE   97002444
+   PUBMED   8849441
+REFERENCE   2  (bases 1 to 1000)
+  AUTHORS   Feldmann,H., Aigle,M., Aljinovic,G., Andre,B., Baclet,M.C.,
+            Barthe,C., Baur,A., Becam,A.M., Biteau,N., Boles,E. et al.
+  TITLE     Complete DNA sequence of yeast chromosome II
+  JOURNAL   EMBO J. 13 (24), 5795-5809 (1994)
+  MEDLINE   95112788
+   PUBMED   7813418
+REFERENCE   3  (bases 1 to 1000)
+  AUTHORS   Saccharomyces Genome Database (yeast-curator at genome.stanford.edu).
+  TITLE     Direct Submission
+  JOURNAL   Submitted (17-NOV-1999) Department of Genetics, Stanford
+            University, Saccharomyces Genome Database, Stanford, CA 94305-5120,
+            USA
+REFERENCE   4  (bases 1 to 1000)
+  AUTHORS   .
+  CONSRTM   NCBI Genome Project
+  TITLE     Direct Submission
+  JOURNAL   Submitted (10-NOV-1999) National Center for Biotechnology
+            Information, NIH, Bethesda, MD 20894, USA
+COMMENT     REVIEWED REFSEQ: This record has been curated by SGD.
+            On Jul 26, 2004 this sequence version replaced gi:50234881.
+            REFSEQ: This reference sequence was provided by the Saccharomyces
+            Genome Database (SGD).
+FEATURES             Location/Qualifiers
+     source          1..1000
+                     /organism="Saccharomyces cerevisiae"
+                     /mol_type="genomic DNA"
+                     /strain="S288C"
+                     /db_xref="taxon:4932"
+                     /chromosome="II"
+     repeat_region   1..1000
+                     /gene="TEL02L"
+                     /note="II left telomeric region"
+                     /evidence=not_experimental
+                     /rpt_family="Telomeric Region"
+                     /db_xref="SGD:S000028867"
+     repeat_region   complement(1..1000)
+                     /gene="TEL02L-YP"
+                     /note="Y' element (short)"
+                     /evidence=not_experimental
+                     /rpt_family="Y' element"
+                     /db_xref="SGD:S000028870"
+     gene            complement(280..1000)
+                     /locus_tag="YBL113C"
+                     /db_xref="GeneID:852159"
+     CDS             complement(280..1000)
+                     /locus_tag="YBL113C"
+                     /note="Ybl113cp;
+                     go_component: cellular_component unknown [goid GO:0008372]
+                     [evidence ND];
+                     go_function: helicase activity [goid GO:0004386] [evidence
+                     ISS] [pmid 9837911];
+                     go_process: biological_process unknown [goid GO:0000004]
+                     [evidence ND]"
+                     /codon_start=1
+                     /evidence=not_experimental
+                     /product="Hypothetical ORF"
+                     /protein_id="NP_009437.1"
+                     /db_xref="GI:6319355"
+                     /db_xref="SGD:S000002153"
+                     /db_xref="GeneID:852159"
+                     /translation="MDLNQRKEKKGQHVGCCGSRTDLSADTVELIERMDRLAENQATA
+                     SMSIVALPSSFQESNSSDRCRKYCSSDEDSDTCIHGSANASTNATTNSSTNATTTASI
+                     NVRTSATTTASINVRTSATTTESTNSNTNATTTESTNSSTNATTTASINVRTSATTTE
+                     STNSSTNATTTASINVRTSATTTESTNSSTNATTTASINVRTSATTTESTNSNTNAST
+                     NATTNSSTNATTTASTNVRTSATTNATTNSSTNATTTASTNVRTSATTTASTNVRTSA
+                     TTTASINVRTSATTTESINSSTNATTTESTNSNTSATTTESTDSNTNATTTASINVRT
+                     SATTTESTNSNTSATTTESTDSNTSATTTASTNSSTNATTTASTNSSTNATTTESTNA
+                     SAKEDANKDGNAEDNRFHPVTDINKESYKRKGSQMVFLERKKLKAQFPNTSENMNVLQ
+                     FLGFRSDEIKHLFLYGIDIYFCPEGVFTQYGLCKGCQKMFGLCVCWAGQKVSYRRIAW
+                     EALAVERMLRNDEEYKEYLEDIEPYHGDPVGYLKYFSVKRREIYSQIQRNYAWYLAIT
+                     RRRETISVLDSTRGKQGSQVFRMSGRQIKELYYKVWSNLRESKTEVLQYFLNWDEKKC
+                     REEWEAKDDTVFVEALEKVGVFQRLRSMTSAGLQGPQYVKLQFSRHHRQLRSRYELSL
+                     GMHLRDQLALGVTPSKVPHWTAFLSMLIGLFYNKTFRQKLEYLLEQISEMWLLPHWLD
+                     LANVEVLAADNTRVPLYMLMVAVHKELDSDDVPDGRFDIILLCRDSSREVGE"
+ORIGIN      
+        1 aaatagccct catgtacgtc tcctccaagc cctgttgtct cttacccgga tgttcaacca
+       61 aaagctactt actaccttta ttttatgttt actttttata ggttgtcttt ttatcccact
+      121 tcttcgcact tgtctctcgc tactgccgtg caacaaacac taaatcaaaa caatgaaata
+      181 ctactacatc aaaacgcatt ttccctagaa aaaaaatttt cttacaatat actatactac
+      241 acaatacata atcactgact ttcgtaacaa caatttcctt cactctccaa cttctctgct
+      301 cgaatctcta catagtaata ttatatcaaa tctaccgtct ggaacatcat cgctatccag
+      361 ctctttgtga accgctacca tcagcatgta cagtggtacc ctcgtgttat ctgcagcgag
+      421 aacttcaacg tttgccaaat caagccaatg tggtaacaac cacatctccg aaatctgctc
+      481 caaaagatat tccagtttct gccgaaatgt tttattgtag aacagcccta tcagcatcga
+      541 caggaatgcc gtccaatgcg gcactttaga tggggtaact cccagcgcaa gctgatctcg
+      601 caagtgcatt cctagactta attcatatct gctcctcaac tgtcgatgat gcctgctaaa
+      661 ctgcagcttg acgtactgcg gaccctgcag tccagcgctc gtcatggaac gcaaacgctg
+      721 aaaaactcca actttctcga gcgcttccac aaagaccgta tcgtcttttg cctcccattc
+      781 ttcccggcac tttttttcgt cccagttcaa aaagtactgc agcacctctg tcttcgattc
+      841 acgcaagttg ctccatactt tataatacaa ctctttgatc tgccttccag acatgcggaa
+      901 aacttggctc ccttgcttgc ctcttgtcga atccaataca ctaattgttt ctcttcttct
+      961 agtaatggcc aggtaccaag cataatttct ctgtatctga
+//
diff --git a/testdata/small.alignment b/testdata/small.alignment
new file mode 100644
index 0000000..e92fdb8
--- /dev/null
+++ b/testdata/small.alignment
@@ -0,0 +1,58 @@
+#FormatVersion Mauve1
+#Sequence1File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.fasta
+#Sequence1Format	FastA
+#Sequence2File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+#Sequence2Format	GenBank
+#Annotation2File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+#Annotation2Format	GenBank
+> 1:566-608 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.fasta
+CTTATCCACCACACCTCATTAAAACG------------------------------------------------------
+--------------------------TGTA--------------------------------------------------
+--------------------------------------------------------------------------------
+-------------------------------------------------------TTTACTAGAAATT
+> 2:305-609 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+cttctacccacca---tattgaaacgCTAACAAATGATCGTAAATAACACACACGTGCTTACCCTACCACTTTATACCAC
+CACCACATGCCATACTCACCCTCACTTGTATACTGATTTTACGTACGCACACGGATGCTACAGTATATACCATCTCAAAC
+TTACCCTACTCTCAGATTCCACTTCACTCCATGGCCCATCTCTCACTGAATCAGTACCAAATGCACTCACATCATTATGC
+ACGGCACTTGCCTCAGCGGTCTATACCCTGTGCCATTTACCCATAACGCCCATCAttatccacatttt
+=
+> 1:1-565 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.fasta
+TTATTCAACATCCTTCCACACACACACACACACTCTCACACACACACTATATACATAATATTTTATAACCGCCGCTGCTA
+ATTCTTCGCAAGGAAACCCGCCTCTTTCTCTCTCTCTATCTCTATCTCTCCCCACGGTGAGTTATACGCCCTTTATAATA
+CAATAGTTTTCTTTCTCTCTTTTACCCACTACCCTTATTTTCTTTTACCCGGACTGCGCTTTTCTCGCACTACGTAATTG
+AAGTCATAACTATACAGCACCTTTGTTGGGTCTATGATCCGGAGACCTGTGAGGCCAGACAGTACTAGACAGCCTATGGT
+CCACAATCTCGTCACCACAGGAATGTCCCCTAGGATATTCAGTATCACAGCATCCATAGTCGTTTGATCCTAAGTTTTCT
+TGGCCCACTACACCAGACAAAATAGTCATGATTGCAAGAGATGTGGAGCTGATGCTGAAAACACCAAAAGTCGTTGATGA
+TGCAGCATGTTTTGTGTTTACATTTCAAGATACCGCAGATTGTTAAGTTTTTAGGATAATTGATAATCGTTAGAAGAATG
+TTATG
+=
+> 2:1-304 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+ccacaccacacccacacacccacacaccacaccacacaccacaccacacccacacacacacatcctaacactaccctaac
+acagccctaatctaaccctggccaacctgtctctcaacttaccctccattaccctgcctccactcgttaccctgtcccat
+tcaaccataccactccgaaccaccatccatccctctacttactaccactcacccaccgttaccctccaattacccatatc
+caacccactgccacttaccctaccattaccctaccatccaccatgacctactcaccatactgtt
+=
+> 1:609-693 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.fasta
+TAGAAGTAGGCACCTAAGCAAAAACCCTCTACAAGTATCAAGTACAAAAGATATTCGAGTATGTACAACTCTTTCGGCAA
+ACTTC
+=
+> 2:610-2000 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+gatatctatatctcattcggcggtcccaaatattgtataactgcccttaatacatacgttataccacttttgcaccatat
+acttaccactccatttatatacacttatgtcaatattacagaaaaatccccacaaaaatcacctaaacataaaaatattc
+tacttttcaacaataatacataaacatattggcttgtggtagcaacactatcatggtatcactaacgtaaaagttcctca
+atattgcaatttgcttgaacggatgctatttcagaatatttcgtacttacacaggccatacattagaataatatgtcaca
+tcactgtcgtaacactctttattcaccgagcaataatacggtagtggctcaaactcatgcgggtgctatgaaaatagccc
+tcatgtacgtctcctccaagccctgttgtctcttacccggatgttcaaccaaaagctacttactacctttattttatgtt
+tactttttataggttgtctttttatcccacttcttcgcacttgtctctcgctactgccgtgcaacaaacactaaatcaaa
+acaatgaaatactactacatcaaaacgcattttccctagaaaaaaaattttcttacaatatactatactacacaatacat
+aatcactgactttcgtaacaacaatttccttcactctccaacttctctgctcgaatctctacatagtaatattatatcaa
+atctaccgtctggaacatcatcgctatccagctctttgtgaaccgctaccatcagcatgtacagtggtaccctcgtgtta
+tctgcagcgagaacttcaacgtttgccaaatcaagccaatgtggtaacaaccacatctccgaaatctgctccaaaagata
+ttccagtttctgccgaaatgttttattgtagaacagccctatcagcatcgacaggaatgccgtccaatgcggcactttag
+atggggtaactcccagcgcaagctgatctcgcaagtgcattcctagacttaattcatatctgctcctcaactgtcgatga
+tgcctgctaaactgcagcttgacgtactgcggaccctgcagtccagcgctcgtcatggaacgcaaacgctgaaaaactcc
+aactttctcgagcgcttccacaaagaccgtatcgtcttttgcctcccattcttcccggcactttttttcgtcccagttca
+aaaagtactgcagcacctctgtcttcgattcacgcaagttgctccatactttataatacaactctttgatctgccttcca
+gacatgcggaaaacttggctcccttgcttgcctcttgtcgaatccaatacactaattgtttctcttcttctagtaatggc
+caggtaccaagcataatttctctgtatctga
+=
diff --git a/testdata/small.mauve b/testdata/small.mauve
new file mode 100644
index 0000000..c8e0688
--- /dev/null
+++ b/testdata/small.mauve
@@ -0,0 +1,28 @@
+FormatVersion	4
+SequenceCount	2
+Sequence0File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.fasta
+Sequence0Length	693
+Sequence1File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+Sequence1Length	2000
+IntervalCount	5
+Interval 0
+13	566	305
+10	582	318
+GappedAlignment
+269	592	328
+--------------------------------------------------------------------------------TGTA-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+CTAACAAATGATCGTAAATAACACACACGTGCTTACCCTACCACTTTATACCACCACCACATGCCATACTCACCCTCACTTGTATACTGATTTTACGTACGCACACGGATGCTACAGTATATACCATCTCAAACTTACCCTACTCTCAGATTCCACTTCACTCCATGGCCCATCTCTCACTGAATCAGTACCAAATGCACTCACATCATTATGCACGGCACTTGCCTCAGCGGTCTATACCCTGTGCCATTTACCCATAACGCCCATCA
+13	596	597
+
+Interval 1
+565	1	0
+
+Interval 2
+304	0	1
+
+Interval 3
+85	609	0
+
+Interval 4
+1391	0	610
+
diff --git a/testdata/small.mums b/testdata/small.mums
new file mode 100644
index 0000000..0938c58
--- /dev/null
+++ b/testdata/small.mums
@@ -0,0 +1,128 @@
+FormatVersion	3
+SequenceCount	2
+Sequence0File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.fasta
+Sequence0Length	693
+Sequence1File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+Sequence1Length	2000
+MatchCount	121
+13	596	597	1413520	0	0
+13	97	102	1420416	0	0
+13	16	50	1436560	0	0
+13	680	714	1412488	0	0
+13	74	136	1409376	0	0
+13	183	289	1422480	0	0
+13	491	608	1423168	0	0
+13	310	486	1433808	0	0
+13	5	194	1446192	0	0
+13	681	879	1445160	0	0
+13	336	541	1434840	0	0
+13	134	399	1417664	0	0
+13	315	585	1427984	0	0
+13	213	542	1416272	0	0
+13	475	804	1452040	0	0
+13	558	911	1425232	0	0
+13	418	812	1433120	0	0
+13	101	496	1419728	0	0
+13	170	614	1407624	0	0
+13	598	1073	1449632	0	0
+13	179	676	1443440	0	0
+13	114	615	1441720	0	0
+13	578	1183	1416616	0	0
+13	48	660	1449976	0	0
+13	572	1187	1426608	0	0
+13	45	682	1432776	0	0
+13	335	1008	1437592	0	0
+13	325	1000	1412832	0	0
+13	12	738	1438968	0	0
+13	247	1015	1450664	0	0
+13	82	860	1451696	0	0
+14	655	1483	1408328	0	0
+13	7	857	1411784	0	0
+18	184	1067	1410752	0	0
+13	248	1172	1406920	0	0
+13	663	1596	1451352	0	0
+13	112	1068	1442408	0	0
+13	363	1324	1446536	0	0
+13	453	1516	1447224	0	0
+13	161	1268	1408672	0	0
+13	40	1153	1414208	0	0
+13	348	1480	1437936	0	0
+13	324	1457	1434496	0	0
+13	289	1430	1448256	0	0
+13	321	1468	1438624	0	0
+13	649	1804	1421792	0	0
+13	47	1205	1428328	0	0
+13	652	1841	1415240	0	0
+13	448	1711	1445504	0	0
+13	221	1511	1448600	0	0
+13	489	1805	1447568	0	0
+13	251	1657	1411096	0	0
+13	87	1538	1440344	0	0
+13	234	1701	1448944	0	0
+13	91	1568	1420760	0	0
+13	225	1800	1451008	0	0
+13	120	1736	1441376	0	0
+13	140	1821	1420072	0	0
+13	78	1765	1443784	0	0
+13	186	1933	1425920	0	0
+13	30	1857	1418352	0	0
+13	664	-1885	1440688	0	0
+13	465	-1955	1433464	0	0
+13	640	-1619	1435872	0	0
+13	657	-1488	1421448	0	0
+13	603	-1456	1415584	0	0
+13	38	-1982	1413864	0	0
+13	368	-1639	1418008	0	0
+13	530	-1461	1436216	0	0
+13	638	-1308	1441032	0	0
+13	646	-1291	1444128	0	0
+13	285	-1593	1417304	0	0
+13	384	-1447	1412144	0	0
+13	275	-1543	1446880	0	0
+13	374	-1441	1434152	0	0
+13	371	-1422	1415928	0	0
+13	550	-1215	1411440	0	0
+13	532	-1227	1424200	0	0
+13	511	-1224	1419040	0	0
+13	212	-1520	1444816	0	0
+13	439	-1288	1422136	0	0
+13	624	-1100	1407280	0	0
+13	602	-1065	1421104	0	0
+13	128	-1531	1442752	0	0
+13	65	-1535	1424888	0	0
+13	145	-1397	1450320	0	0
+13	420	-1085	1409016	0	0
+13	577	-912	1435184	0	0
+13	200	-1204	1439312	0	0
+13	156	-1212	1409720	0	0
+13	661	-705	1426264	0	0
+13	158	-1177	1410408	0	0
+13	293	-1034	1445848	0	0
+13	620	-656	1414896	0	0
+13	146	-1094	1416960	0	0
+13	549	-683	1418696	0	0
+13	263	-950	1423512	0	0
+13	154	-1032	1444472	0	0
+13	303	-882	1422824	0	0
+13	180	-970	1449288	0	0
+13	499	-605	1426952	0	0
+13	581	-498	1435528	0	0
+13	467	-595	1413176	0	0
+13	431	-602	1424544	0	0
+13	441	-511	1425576	0	0
+13	157	-792	1410064	0	0
+18	231	-693	1442064	0	0
+13	286	-548	1439656	0	0
+13	357	-437	1423856	0	0
+13	503	-217	1440000	0	0
+13	556	-163	1407984	0	0
+13	270	-269	1437248	0	0
+13	634	156	1427640	0	0
+13	579	315	1438280	0	0
+13	566	305	1443096	0	0
+13	401	153	1447912	0	0
+13	574	363	1436904	0	0
+13	608	418	1452384	0	0
+13	259	207	1414552	0	0
+13	355	338	1427296	0	0
+13	323	314	1419384	0	0
diff --git a/testdata/small_raw.alignment b/testdata/small_raw.alignment
new file mode 100644
index 0000000..c3a3405
--- /dev/null
+++ b/testdata/small_raw.alignment
@@ -0,0 +1,58 @@
+#FormatVersion Mauve1
+#Sequence1File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.raw
+#Sequence1Format	Raw
+#Sequence2File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+#Sequence2Format	GenBank
+#Annotation2File	C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+#Annotation2Format	GenBank
+> 1:566-608 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.raw
+CTTATCCACCACACCTCATTAAAACG------------------------------------------------------
+--------------------------TGTA--------------------------------------------------
+--------------------------------------------------------------------------------
+-------------------------------------------------------TTTACTAGAAATT
+> 2:305-609 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+cttctacccacca---tattgaaacgCTAACAAATGATCGTAAATAACACACACGTGCTTACCCTACCACTTTATACCAC
+CACCACATGCCATACTCACCCTCACTTGTATACTGATTTTACGTACGCACACGGATGCTACAGTATATACCATCTCAAAC
+TTACCCTACTCTCAGATTCCACTTCACTCCATGGCCCATCTCTCACTGAATCAGTACCAAATGCACTCACATCATTATGC
+ACGGCACTTGCCTCAGCGGTCTATACCCTGTGCCATTTACCCATAACGCCCATCAttatccacatttt
+=
+> 1:1-565 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.raw
+TTATTCAACATCCTTCCACACACACACACACACTCTCACACACACACTATATACATAATATTTTATAACCGCCGCTGCTA
+ATTCTTCGCAAGGAAACCCGCCTCTTTCTCTCTCTCTATCTCTATCTCTCCCCACGGTGAGTTATACGCCCTTTATAATA
+CAATAGTTTTCTTTCTCTCTTTTACCCACTACCCTTATTTTCTTTTACCCGGACTGCGCTTTTCTCGCACTACGTAATTG
+AAGTCATAACTATACAGCACCTTTGTTGGGTCTATGATCCGGAGACCTGTGAGGCCAGACAGTACTAGACAGCCTATGGT
+CCACAATCTCGTCACCACAGGAATGTCCCCTAGGATATTCAGTATCACAGCATCCATAGTCGTTTGATCCTAAGTTTTCT
+TGGCCCACTACACCAGACAAAATAGTCATGATTGCAAGAGATGTGGAGCTGATGCTGAAAACACCAAAAGTCGTTGATGA
+TGCAGCATGTTTTGTGTTTACATTTCAAGATACCGCAGATTGTTAAGTTTTTAGGATAATTGATAATCGTTAGAAGAATG
+TTATG
+=
+> 2:1-304 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+ccacaccacacccacacacccacacaccacaccacacaccacaccacacccacacacacacatcctaacactaccctaac
+acagccctaatctaaccctggccaacctgtctctcaacttaccctccattaccctgcctccactcgttaccctgtcccat
+tcaaccataccactccgaaccaccatccatccctctacttactaccactcacccaccgttaccctccaattacccatatc
+caacccactgccacttaccctaccattaccctaccatccaccatgacctactcaccatactgtt
+=
+> 1:609-693 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_bayanus_small.raw
+TAGAAGTAGGCACCTAAGCAAAAACCCTCTACAAGTATCAAGTACAAAAGATATTCGAGTATGTACAACTCTTTCGGCAA
+ACTTC
+=
+> 2:610-2000 + C:\Documents and Settings\pinfield\workspace\mauve\testdata\S_cerevisiae_small.gbk
+gatatctatatctcattcggcggtcccaaatattgtataactgcccttaatacatacgttataccacttttgcaccatat
+acttaccactccatttatatacacttatgtcaatattacagaaaaatccccacaaaaatcacctaaacataaaaatattc
+tacttttcaacaataatacataaacatattggcttgtggtagcaacactatcatggtatcactaacgtaaaagttcctca
+atattgcaatttgcttgaacggatgctatttcagaatatttcgtacttacacaggccatacattagaataatatgtcaca
+tcactgtcgtaacactctttattcaccgagcaataatacggtagtggctcaaactcatgcgggtgctatgaaaatagccc
+tcatgtacgtctcctccaagccctgttgtctcttacccggatgttcaaccaaaagctacttactacctttattttatgtt
+tactttttataggttgtctttttatcccacttcttcgcacttgtctctcgctactgccgtgcaacaaacactaaatcaaa
+acaatgaaatactactacatcaaaacgcattttccctagaaaaaaaattttcttacaatatactatactacacaatacat
+aatcactgactttcgtaacaacaatttccttcactctccaacttctctgctcgaatctctacatagtaatattatatcaa
+atctaccgtctggaacatcatcgctatccagctctttgtgaaccgctaccatcagcatgtacagtggtaccctcgtgtta
+tctgcagcgagaacttcaacgtttgccaaatcaagccaatgtggtaacaaccacatctccgaaatctgctccaaaagata
+ttccagtttctgccgaaatgttttattgtagaacagccctatcagcatcgacaggaatgccgtccaatgcggcactttag
+atggggtaactcccagcgcaagctgatctcgcaagtgcattcctagacttaattcatatctgctcctcaactgtcgatga
+tgcctgctaaactgcagcttgacgtactgcggaccctgcagtccagcgctcgtcatggaacgcaaacgctgaaaaactcc
+aactttctcgagcgcttccacaaagaccgtatcgtcttttgcctcccattcttcccggcactttttttcgtcccagttca
+aaaagtactgcagcacctctgtcttcgattcacgcaagttgctccatactttataatacaactctttgatctgccttcca
+gacatgcggaaaacttggctcccttgcttgcctcttgtcgaatccaatacactaattgtttctcttcttctagtaatggc
+caggtaccaagcataatttctctgtatctga
+=

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/mauve.git



More information about the debian-med-commit mailing list