6d8128674f5497f4cab629c88fb2ecc0bc297b33
hiram
  Mon Jul 21 11:59:00 2014 -0700
fixup slAddHead argument reference refs #9741
diff --git src/hg/hgTracks/joinedRmskTrack.c src/hg/hgTracks/joinedRmskTrack.c
index 6271adf..80f6c0b 100644
--- src/hg/hgTracks/joinedRmskTrack.c
+++ src/hg/hgTracks/joinedRmskTrack.c
@@ -1,1195 +1,1194 @@
 /* joinedRmskTrack - A comprehensive RepeatMasker visualization track
  *                   handler. This is an extension of the original
  *                   rmskTrack.c written by UCSC.
  *
  *  Written by Robert Hubley 10/2012
  *
  *  Modifications:
  *
  *  7/2014
  *     With the help of Jim Kent we modified the
  *     glyph design to more closely resemble the
  *     existing browser styles.
  */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "common.h"
 #include "hash.h"
 #include "linefile.h"
 #include "jksql.h"
 #include "hdb.h"
 #include "hgTracks.h"
 #include "rmskJoined.h"
 
 
 /* DETAIL_VIEW_MAX_SCALE: The size ( max bp ) at which the
  * detailed view of repeats is turned on, otherwise it reverts
  * back to the original UCSC glyph.
  */
 #define DETAIL_VIEW_MAX_SCALE 30000
 
 /* MAX_UNALIGNED_PIXEL_LEN: The maximum size of unaligned sequence
  * to draw before switching to the unscaled view: ie. "----/###/---"
  * This is measured in pixels *not* base pairs due to the
  * mixture of scaled ( lines ) and unscaled ( text ) glyph
  * components.
  */
 #define MAX_UNALIGNED_PIXEL_LEN 150
 
 // TODO: Document
 #define LABEL_PADDING 5
 
 
 // TODO: Document
 static float pixelsPerBase = 1.0;
 
 /* Hash of the repeatItems ( tg->items ) for this track.
  *   This is used to hold display names for the lines of
  *   the track, the line heights, and class colors.
  */
 struct hash *classHash = NULL;
 static struct repeatItem *otherRepeatItem = NULL;
 
 /* Hash of subtracks
  *   The joinedRmskTrack is designed to be used as a composite track.
  *   When jRepeatLoad is called this hash is populated with the
  *   results of one or more table queries
  */
 struct hash *subTracksHash = NULL;
 
 struct subTrack
 {
   // The number of display levels used in levels[]
 int levelCount;
   // The rmskJoined records from table query
 struct rmskJoined *levels[30];
 };
 
 
 // Repeat Classes: Display names
 static char *rptClassNames[] = {
 "SINE", "LINE", "LTR", "DNA", "Simple", "Low Complexity", "Satellite",
 "RNA", "Other", "Unknown",
 };
 
 // Repeat Classes: Data names
 static char *rptClasses[] = {
 "SINE", "LINE", "LTR", "DNA", "Simple_repeat", "Low_complexity",
 "Satellite", "RNA", "Other", "Unknown",
 };
 
 
 /* Repeat class to color mappings. I took these from a
  * online color dictionary website:
  *
  *     http://people.csail.mit.edu/jaffer/Color/Dictionaries
  *
  *  I used the html4 catalog and tried to the 10 most distinct
  *  colors.
  *
  *  NOTE: If these are changed, do not forget to update the
  *        help table in joinedRmskTrack.html
  */
 static Color jRepeatClassColors[] = {
 0xff0000ff,			// SINE - red
 0xff00ff00,			// LINE - lime
 0xff000080,			// LTR - maroon
 0xffff00ff,			// DNA - fuchsia
 0xff00ffff,			// Simple - yellow
 0xff008080,			// LowComplex - olive
 0xffff0000,			// Satellite - blue
 0xff008000,			// RNA - green
 0xff808000,			// Other - teal
 0xffffff00,			// Unknown - aqua
 };
 
 // Basic range type
 struct Extents
 {
 int start;
 int end;
 };
 
 static struct Extents * getExtents (struct rmskJoined *rm)
 /* Calculate the glyph extents in genome coordinate
  * space ( ie. bp )
  *
  * It is not straightforward to determine the extent
  * of this track's glyph.  This is due to the mixture
  * of graphic and text elements within the glyph.
  *
  * TODO: Consider changing to graphics (ie. pixel) space.
  */
 {
 static struct Extents ex;
 char lenLabel[20];
 
 if (rm == NULL)
 return NULL;
 
 ex.start = rm->alignStart
     -
     (int) ((mgFontStringWidth (tl.font, rm->name) +
 	    LABEL_PADDING) / pixelsPerBase);
 
 
 if ((rm->blockSizes[0] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN)
     {
     safef (lenLabel, sizeof (lenLabel), "%d", rm->blockSizes[0]);
     ex.start -= (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) +
 	(int) ((mgFontStringWidth (tl.font, lenLabel) +
 		LABEL_PADDING) / pixelsPerBase);
     }
 else
     {
     ex.start -= rm->blockSizes[0];
     }
 
 ex.end = rm->alignEnd;
 
 if ((rm->blockSizes[rm->blockCount - 1] * pixelsPerBase) >
     MAX_UNALIGNED_PIXEL_LEN)
     {
     safef (lenLabel, sizeof (lenLabel), "%d",
 	   rm->blockSizes[rm->blockCount - 1]);
     ex.end +=
 	(int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) +
 	(int) ((mgFontStringWidth (tl.font, lenLabel) +
 		LABEL_PADDING) / pixelsPerBase);
     }
 else
     {
     ex.end += rm->blockSizes[rm->blockCount - 1];
     }
 
 return &ex;
 }
 
 static int cmpRepeatVisStart (const void *va, const void *vb)
 /* Sort repeats by display start position.  Note: We
  * account for the fact we may not start the visual
  * display at chromStart.  See MAX_UNALIGNED_PIXEL_LEN.
  */
 {
 struct rmskJoined *a = *((struct rmskJoined **) va);
 struct rmskJoined *b = *((struct rmskJoined **) vb);
 
 struct Extents *ext = NULL;
 ext = getExtents (a);
 int aStart = ext->start;
 ext = getExtents (b);
 int bStart = ext->start;
 
 return (aStart - bStart);
 }
 
 static struct repeatItem * makeJRepeatItems ()
 /* Initialize the track */
 {
 classHash = newHash (6);
 struct repeatItem *ri, *riList = NULL;
 int i;
 int numClasses = ArraySize (rptClasses);
 for (i = 0; i < numClasses; ++i)
     {
     AllocVar (ri);
     ri->class = rptClasses[i];
     ri->className = rptClassNames[i];
     // New color attribute
     ri->color = jRepeatClassColors[i];
     slAddHead (&riList, ri);
     // Hash now prebuilt to hold color attributes
     hashAdd (classHash, ri->class, ri);
     if (sameString (rptClassNames[i], "Other"))
     otherRepeatItem = ri;
     }
 slReverse (&riList);
 return riList;
 }
 
 static void jRepeatLoad (struct track *tg)
 /* We do the query(ies) here so we can report how deep the track(s)
  * will be when jRepeatTotalHeight() is called later on.
  */
 {
   /*
    * Initialize the subtracks hash - This will eventually contain
    * all the repeat data for each displayed subtrack.
    */
 if (!subTracksHash)
 subTracksHash = newHash (20);
 
 tg->items = makeJRepeatItems ();
 
 int baseWidth = winEnd - winStart;
 pixelsPerBase = (float) insideWidth / (float) baseWidth;
 if (tg->visibility == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE)
     {
     struct subTrack *st = NULL;
     AllocVar (st);
     if (st)
 	{
 	st->levels[0] = NULL;
 	st->levelCount = 0;
 	struct rmskJoined *rm = NULL;
 	char **row;
 	int rowOffset;
 	struct sqlConnection *conn = hAllocConn (database);
 	struct sqlResult *sr = hRangeQuery (conn, tg->table, chromName,
 					    winStart, winEnd, NULL,
 					    &rowOffset);
-
 	struct rmskJoined *detailList = NULL;
 	while ((row = sqlNextRow (sr)) != NULL)
 	    {
 	    rm = rmskJoinedLoad (row + rowOffset);
-	    slAddHead (detailList, rm);
+	    slAddHead (&detailList, rm);
 	    }
 	slSort (&detailList, cmpRepeatVisStart);
 
 	sqlFreeResult (&sr);
 	hFreeConn (&conn);
 
 	int crChromStart, crChromEnd;
 	while (detailList)
 	    {
 	    st->levels[st->levelCount++] = detailList;
 
 	    struct rmskJoined *cr = detailList;
 	    detailList = detailList->next;
 	    cr->next = NULL;
 	    int rmChromStart, rmChromEnd;
 	    struct rmskJoined *prev = NULL;
 	    rm = detailList;
 
 	    struct Extents *ext = NULL;
 	    ext = getExtents (cr);
 	    crChromStart = ext->start;
 	    crChromEnd = ext->end;
 
 	    while (rm)
 		{
 		ext = getExtents (rm);
 		rmChromStart = ext->start;
 		rmChromEnd = ext->end;
 
 		if (rmChromStart > crChromEnd)
 		    {
 		    cr->next = rm;
 		    cr = rm;
 		    crChromStart = rmChromStart;
 		    crChromEnd = rmChromEnd;
 		    if (prev)
 			{
 			prev->next = rm->next;
 			}
 		    else
 			{
 			detailList = rm->next;
 			}
 
 		    rm = rm->next;
 		    cr->next = NULL;
 		    }
 		else
 		    {
 		    // Save for a lower level
 		    prev = rm;
 		    rm = rm->next;
 		    }
 		}		// while ( rm )
 	    }			// while ( detailList )
 	// Create Hash Entry
 	hashReplace (subTracksHash, tg->table, st);
 	}			// if ( st )
     }				// if ( tg->visibility == tvFull
 }
 
 static void jRepeatFree (struct track *tg)
 /* Free up jRepeatMasker items. */
 {
 slFreeList (&tg->items);
 }
 
 static char * jRepeatName (struct track *tg, void *item)
 /* Return name of jRepeat item track. */
 {
 static char empty = '\0';
 struct repeatItem *ri = item;
   /*
    * In detail view mode the items represent different packing
    * levels.  No need to display a label at each level.  Instead
    * Just return a label for the first level.
    */
 if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     if (strcmp (ri->className, "SINE") == 0)
 	{
 	return ("Repeats");
 	}
     else
 	{
 	return &empty;
 	}
     }
 return ri->className;
 }
 
 int jRepeatTotalHeight (struct track *tg, enum trackVisibility vis)
 {
   // Are we in full view mode and at the scale needed to display
   // the detail view?
 if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     // Lookup the depth of this subTrack and report it
     struct subTrack *st = hashFindVal (subTracksHash, tg->table);
     if (st)
     return ((st->levelCount + 1) * 24);
     else
     // Just display one line
     return (24);
     }
 else
 return tgFixedTotalHeightNoOverflow (tg, vis);
 }
 
 int jRepeatItemHeight (struct track *tg, void *item)
 {
   // Are we in full view mode and at the scale needed to display
   // the detail view?
 if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
 return 24;
 else
 return tgFixedItemHeight (tg, item);
 }
 
 static void drawDashedHorizLine (struct hvGfx *hvg, int x1, int x2,
 		     int y, int dashLen, int gapLen, Color lineColor)
 // ie.    - - - - - - - - - - - - - - - -
 {
 int cx1 = x1;
 int cx2;
 while (1)
     {
     cx2 = cx1 + dashLen;
     if (cx2 > x2)
     cx2 = x2;
     hvGfxLine (hvg, cx1, y, cx2, y, lineColor);
     cx1 += (dashLen + gapLen);
     if (cx1 > x2)
     break;
     }
 }
 
 static void drawDashedHorizLineWHash (struct hvGfx *hvg, int x1, int x2,
 			  int y, int dashLen, int gapLen, Color lineColor,
 			  int unalignedLen)
 // ie.    - - - - - - -\234\- - - - - - - - -
 {
 int cx1 = x1;
 int cx2;
 
 char lenLabel[20];
 safef (lenLabel, sizeof (lenLabel), "%d", unalignedLen);
 MgFont *font = tl.font;
 int fontHeight = tl.fontHeight;
 int stringWidth = mgFontStringWidth (font, lenLabel) + LABEL_PADDING;
 
 int glyphWidth = x2 - x1;
 
 if (glyphWidth < stringWidth + 6 + (2 * dashLen))
 stringWidth = 0;
 
 
 
 int midX = ((glyphWidth) / 2) + x1;
 int startHash = midX - (stringWidth * 0.5);
 int midPointDrawn = 0;
 
   /*
      Degrade Gracefully:
      * Too little space to draw dashes or even
      * hash marks, give up.
    */
 if (glyphWidth < 6 + dashLen)
     {
     hvGfxLine (hvg, x1, y, x2, y, lineColor);
     return;
     }
 if (glyphWidth < 6 + (2 * dashLen))
     {
     midX -= 3;
     hvGfxLine (hvg, x1, y, midX, y, lineColor);
     hvGfxLine (hvg, midX, y - 3, midX + 3, y + 3, lineColor);
     hvGfxLine (hvg, midX + 3, y - 3, midX + 6, y + 3, lineColor);
     hvGfxLine (hvg, midX + 6, y, x2, y, lineColor);
     return;
     }
 
 while (1)
     {
     cx2 = cx1 + dashLen;
     if (cx2 > x2)
     cx2 = x2;
 
     if (!midPointDrawn && cx2 > startHash)
 	{
 	// Draw double slashes "\\" instead of dash
 	hvGfxLine (hvg, cx1, y - 3, cx1 + 3, y + 3, lineColor);
 	if (stringWidth)
 	    {
 	    hvGfxTextCentered (hvg, cx1 + 3, y - 3, stringWidth,
 			       fontHeight, MG_BLACK, font, lenLabel);
 	    cx1 += stringWidth;
 	    }
 	hvGfxLine (hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor);
 	cx1 += 6;
 	midPointDrawn = 1;
 	}
     else
 	{
 	// Draw a dash
 	hvGfxLine (hvg, cx1, y, cx2, y, lineColor);
 	cx1 += dashLen;
 	}
 
     if (!midPointDrawn && cx1 + gapLen > midX)
 	{
 	// Draw double slashes "\\" instead of gap
 	hvGfxLine (hvg, cx1, y - 3, cx1 + 3, y + 3, lineColor);
 	if (stringWidth)
 	    {
 	    hvGfxTextCentered (hvg, cx1 + 3, y - 3, stringWidth,
 			       fontHeight, MG_BLACK, font, lenLabel);
 	    cx1 += stringWidth;
 	    }
 	hvGfxLine (hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor);
 	cx1 += 6;
 	midPointDrawn = 1;
 	}
     else
 	{
 	cx1 += gapLen;
 	}
 
     if (cx1 > x2)
     break;
     }
 }
 
 static void drawNormalBox (struct hvGfx *hvg, int x1, int y1,
 	       int width, int height, Color fillColor, Color outlineColor)
 {
 struct gfxPoly *poly = gfxPolyNew ();
 int y2 = y1 + height;
 int x2 = x1 + width;
   /*
    *     1,5--------------2
    *       |              |
    *       |              |
    *       |              |
    *       4--------------3
    */
 gfxPolyAddPoint (poly, x1, y1);
 gfxPolyAddPoint (poly, x2, y1);
 gfxPolyAddPoint (poly, x2, y2);
 gfxPolyAddPoint (poly, x1, y2);
 gfxPolyAddPoint (poly, x1, y1);
 hvGfxDrawPoly (hvg, poly, fillColor, TRUE);
 hvGfxDrawPoly (hvg, poly, outlineColor, FALSE);
 gfxPolyFree (&poly);
 }
 
 static void drawBoxWChevrons (struct hvGfx *hvg, int x1, int y1,
 		  int width, int height, Color fillColor, Color outlineColor,
 		  char strand)
 {
 drawNormalBox (hvg, x1, y1, width, height, fillColor, outlineColor);
 static Color white = 0xffffffff;
 int dir = (strand == '+' ? 1 : -1);
 int midY = y1 + ((height) >> 1);
 clippedBarbs (hvg, x1 + 1, midY, width, ((height >> 1) - 2), 5,
 	      dir, white, TRUE);
 }
 
 static void drawRMGlyph (struct hvGfx *hvg, int y, int heightPer,
 	     int width, int baseWidth, int xOff, struct rmskJoined *rm)
 /*
  *  Draw a detailed RepeatMasker annotation glyph given
  *  a single rmskJoined structure.
  *
  *  A couple of caveats about the use of the rmskJoined
  *  structure to hold a RepeatMasker annotation.
  *
  *  chromStart/chromEnd:  These represent genomic coordinates
  *                        that mark the extents of graphics
  *                        annotation on the genome.  I.e
  *                        aligned blocks + unaligned consensus
  *                        blocks.  The code below may not
  *                        draw to these extents in some cases.
  *
  *  score:  This represents the average divergence of the
  *          individual aligned blocks.  It is a percentage
  *          * 100.  Ie. 23.11 % is encoded as the integer 2311.
  *
  *  blockRelStarts:  Two types of blocks are interwoven in this
  *                format.  Aligned and unaligned blocks.  Aligned
  *                blocks will have a value >= 0 representing the
  *                relative position from chromStart for this
  *                start of this block.  A value of -1 for chromStart
  *                is indicative of unaligned block and starts from
  *                the end of the last aligned block on the chromosome
  *                or from chromStart if it's the first block in the list.
  *
  *  name:  Stores the id#class/family
  *
  *  rgb:  Unused at this time
  *
  *  description: Unused but the intention would be to store
  *               the RepeatMasker *.out lines which make up
  *               all the aligned sections of this joined
  *               annotation.
  *
  *  blockSizes:   As you would expect.
  *
  *  Here is an example:
  *                ie.
  *                A forward strand RM annotation from chr1:186-196
  *                which is aligned to a 100 bp repeat from 75-84
  *                in the consensus would be represented as:
  *
  *                chromStart: 111
  *                chromEnd: 212
  *                blockRelStarts: -1, 75, -1
  *                blockSizes: 75, 10, 16
  *
  */
 {
 int idx;
 int lx1, lx2;
 int cx1, cx2;
 int w;
 struct repeatItem *ri;
   /*
    * heightPer is the God given height of a single
    * track item...respect your overlord.
    */
 int alignedBlockHeight = heightPer * 0.5;
 int alignedBlockOffset = heightPer - alignedBlockHeight;
 int unalignedBlockHeight = heightPer * 0.75;
 int unalignedBlockOffset = heightPer - unalignedBlockHeight;
 
 Color black = hvGfxFindColorIx (hvg, 0, 0, 0);
 Color fillColor = shadesOfGray[5];
 Color outlineColor = jRepeatClassColors[0];
 
   // Break apart the name and get the class of the
   // repeat.
 char class[256];
   // Simplify repClass for lookup: strip trailing '?',
   // simplify *RNA to RNA:
 char *poundPtr = index (rm->name, '#');
 if (poundPtr)
     {
     safecpy (class, sizeof (class), poundPtr + 1);
     char *slashPtr = index (class, '/');
     if (slashPtr)
     *slashPtr = '\0';
     }
 else
     {
     safecpy (class, sizeof (class), "Unknown");
     }
 char *p = &(class[strlen (class) - 1]);
 if (*p == '?')
 *p = '\0';
 if (endsWith (class, "RNA"))
 safecpy (class, sizeof (class), "RNA");
 
   // Lookup the class to get the color scheme
 ri = hashFindVal (classHash, class);
 if (ri == NULL)
 ri = otherRepeatItem;
 
   // Pick the fill color based on the divergence
 int percId = 10000 - rm->score;
 int grayLevel = grayInRange (percId, 6000, 10000);
 fillColor = shadesOfGray[grayLevel];
 outlineColor = ri->color;
 
   // Draw from left to right
 for (idx = 0; idx < rm->blockCount; idx++)
     {
     int fragGStart = rm->blockRelStarts[idx];
 
     /*
      *  Assumptions about blockCount
      *   - first aligned block = 1, the block 0 is
      *     the unaligned consnesus either 5' or 3' of
      *     this block.
      *   - The last aligned block is blockCount - 1;
      */
     if (fragGStart > -1)
 	{
 	// Aligned Block
 	int fragSize = rm->blockSizes[idx];
 	fragGStart += rm->chromStart;
 	int fragGEnd = fragGStart + fragSize;
 	lx1 = roundingScale (fragGStart - winStart, width, baseWidth) + xOff;
 	lx1 = max (lx1, 0);
 	lx2 = roundingScale (fragGEnd - winStart, width, baseWidth) + xOff;
 	w = lx2 - lx1;
 	if (w <= 0)
 	w = 1;
 
 	if (idx == 1 && rm->blockCount == 3)
 	    {
 	    // One and only one aligned block
 	    drawBoxWChevrons (hvg, lx1, y + alignedBlockOffset, w,
 			      alignedBlockHeight, fillColor,
 			      outlineColor, rm->strand[0]);
 	    }
 	else if (idx == 1)
 	    {
 	    // First block
 	    if (rm->strand[0] == '-')
 		{
 		// First block on negative strand is the point block
 		drawBoxWChevrons (hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 		}
 	    else
 		{
 		// First block on the positive strand is the tail block
 		drawBoxWChevrons (hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 
 		}
 	    }
 	else if (idx == (rm->blockCount - 2))
 	    {
 	    // Last block
 	    if (rm->strand[0] == '-')
 		{
 		// Last block on the negative strand is the tail block
 		drawBoxWChevrons (hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 
 		}
 	    else
 		{
 		// Last block on the positive strand is the poitn block
 		drawBoxWChevrons (hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 
 		}
 	    }
 	else
 	    {
 	    // Intermediate aligned blocks are drawn as rectangles
 	    drawBoxWChevrons (hvg, lx1, y + alignedBlockOffset, w,
 			      alignedBlockHeight, fillColor,
 			      outlineColor, rm->strand[0]);
 	    }
 
 	}
     else
 	{
 	// Unaligned Block
 	int relStart = 0;
 	if (idx == 0)
 	    {
 	    /*
 	     * Unaligned sequence at the start of an annotation
 	     * Draw as:
 	     *     |-------------     or |------//------
 	     *                  |                      |
 	     *                  >>>>>>>                >>>>>>>>
 	     */
 	    lx2 = roundingScale (rm->chromStart +
 				 rm->blockRelStarts[idx + 1] -
 				 winStart, width, baseWidth) + xOff;
 	    if ((rm->blockSizes[idx] * pixelsPerBase) >
 		MAX_UNALIGNED_PIXEL_LEN)
 		{
 		lx1 = roundingScale (rm->chromStart +
 				     (rm->blockRelStarts[idx + 1] -
 				      (int)
 				      (MAX_UNALIGNED_PIXEL_LEN /
 				       pixelsPerBase)) -
 				     winStart, width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLineWHash (hvg, lx1, lx2,
 					  y +
 					  unalignedBlockOffset,
 					  5, 5, black, rm->blockSizes[idx]);
 		}
 	    else
 		{
 		lx1 = roundingScale (rm->chromStart - winStart,
 				     width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLine (hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		}
 	    // Line down
 	    hvGfxLine (hvg, lx2, y + alignedBlockOffset, lx2,
 		       y + unalignedBlockOffset, black);
 	    hvGfxLine (hvg, lx1, y + unalignedBlockOffset - 3, lx1,
 		       y + unalignedBlockOffset + 3, black);
 
 	    // Draw labels
 	    MgFont *font = tl.font;
 	    int fontHeight = tl.fontHeight;
 	    int stringWidth =
 		mgFontStringWidth (font, rm->name) + LABEL_PADDING;
 	    hvGfxTextCentered (hvg, lx1 - stringWidth,
 			       y + unalignedBlockOffset + fontHeight,
 			       stringWidth, fontHeight, MG_BLACK, font,
 			       rm->name);
 
 
 	    }
 	else if (idx == (rm->blockCount - 1))
 	    {
 	    /*
 	     * Unaligned sequence at the end of an annotation
 	     * Draw as:
 	     *       -------------|   or        ------//------|
 	     *       |                          |
 	     *  >>>>>                    >>>>>>>
 	     */
 	    lx1 = roundingScale (rm->chromStart +
 				 rm->blockRelStarts[idx - 1] +
 				 rm->blockSizes[idx - 1] - winStart,
 				 width, baseWidth) + xOff;
 	    if ((rm->blockSizes[idx] * pixelsPerBase) >
 		MAX_UNALIGNED_PIXEL_LEN)
 		{
 		lx2 = roundingScale (rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] +
 				     (int)
 				     (MAX_UNALIGNED_PIXEL_LEN /
 				      pixelsPerBase) - winStart,
 				     width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLineWHash (hvg, lx1, lx2,
 					  y +
 					  unalignedBlockOffset,
 					  5, 5, black, rm->blockSizes[idx]);
 		}
 	    else
 		{
 		lx2 = roundingScale (rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] +
 				     rm->blockSizes[idx] -
 				     winStart, width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLine (hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		}
 	    // Line down
 	    hvGfxLine (hvg, lx1, y + alignedBlockOffset, lx1,
 		       y + unalignedBlockOffset, black);
 	    hvGfxLine (hvg, lx2, y + unalignedBlockOffset - 3, lx2,
 		       y + unalignedBlockOffset + 3, black);
 	    }
 	else
 	    {
 	    /*
 	     * Middle Unaligned
 	     * Draw unaligned sequence between aligned fragments
 	     * as:       .........
 	     *          /         \
 	     *     >>>>>           >>>>>>
 	     *
 	     * or:
 	     *         .............
 	     *         \           /
 	     *     >>>>>           >>>>>>
 	     *
 	     * Also use ....//.... to indicate not to scale if
 	     * necessary.
 	     *
 	     */
 	    int alignedGapSize = rm->blockRelStarts[idx + 1] -
 		(rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1]);
 	    int unaSize = rm->blockSizes[idx];
 
 	    int alignedOverlapSize = unaSize - alignedGapSize;
 
 	    if (unaSize < 0)
 		{
 		relStart =
 		    rm->blockRelStarts[idx - 1] +
 		    rm->blockSizes[idx - 1] - (alignedOverlapSize / 2);
 
 		if (abs (unaSize) > rm->blockSizes[idx - 1])
 		unaSize = -rm->blockSizes[idx - 1];
 
 		lx1 = roundingScale (rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] +
 				     unaSize - winStart, width,
 				     baseWidth) + xOff;
 		lx1 = max (lx1, 0);
 		lx2 = roundingScale (rm->chromStart +
 				     rm->blockRelStarts[idx +
 							1] -
 				     winStart, width, baseWidth) + xOff;
 
 		int midPoint = ((lx2 - lx1) / 2) + lx1;
 
 		// Block Intersection Line
 		hvGfxLine (hvg, lx1, y + alignedBlockOffset, lx1,
 			   y + alignedBlockOffset + alignedBlockHeight,
 			   black);
 
 		// Line Up
 		hvGfxLine (hvg, lx1, y + alignedBlockOffset,
 			   midPoint, y + unalignedBlockOffset, black);
 
 		// Line Down
 		hvGfxLine (hvg, lx2, y + alignedBlockOffset,
 			   midPoint, y + unalignedBlockOffset, black);
 
 		}
 	    else if (alignedOverlapSize > 0 &&
 		     ((alignedOverlapSize * 0.5) >
 		      (0.3 * rm->blockSizes[idx - 1])
 		      || (alignedOverlapSize * 0.5) >
 		      (0.3 * rm->blockSizes[idx + 1])))
 		{
 		// Need to shorten unaligned length
 		int smallOverlapLen = 0;
 		smallOverlapLen = (0.3 * rm->blockSizes[idx - 1]);
 		if (smallOverlapLen > (0.3 * rm->blockSizes[idx + 1]))
 		smallOverlapLen = (0.3 * rm->blockSizes[idx + 1]);
 		unaSize = (smallOverlapLen * 2) + alignedGapSize;
 		relStart =
 		    rm->blockRelStarts[idx - 1] +
 		    rm->blockSizes[idx - 1] - smallOverlapLen;
 		lx1 = roundingScale (relStart + rm->chromStart -
 				     winStart, width, baseWidth) + xOff;
 		lx1 = max (lx1, 0);
 		lx2 = roundingScale (relStart + rm->chromStart +
 				     unaSize - winStart, width,
 				     baseWidth) + xOff;
 		// Line Up
 		cx1 = roundingScale (rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] -
 				     winStart, width, baseWidth) + xOff;
 		hvGfxLine (hvg, cx1, y + alignedBlockOffset, lx1,
 			   y + unalignedBlockOffset, black);
 
 		// Line Across
 		drawDashedHorizLineWHash (hvg, lx1, lx2,
 					  y +
 					  unalignedBlockOffset,
 					  5, 5, black, rm->blockSizes[idx]);
 		// Line Down
 		cx2 = roundingScale (rm->chromStart +
 				     rm->blockRelStarts[idx +
 							1] -
 				     winStart, width, baseWidth) + xOff;
 		hvGfxLine (hvg, cx2, y + alignedBlockOffset, lx2,
 			   y + unalignedBlockOffset, black);
 		}
 	    else
 		{
 		// Adequate to display full length
 		relStart =
 		    rm->blockRelStarts[idx - 1] +
 		    rm->blockSizes[idx - 1] - (alignedOverlapSize / 2);
 		lx1 = roundingScale (relStart + rm->chromStart -
 				     winStart, width, baseWidth) + xOff;
 		lx1 = max (lx1, 0);
 		lx2 = roundingScale (relStart + rm->chromStart +
 				     unaSize - winStart, width,
 				     baseWidth) + xOff;
 		// Line Up
 		int cx1 =
 		    roundingScale (rm->chromStart +
 				   rm->blockRelStarts[idx - 1] +
 				   rm->blockSizes[idx - 1] - winStart,
 				   width, baseWidth) + xOff;
 		hvGfxLine (hvg, cx1, y + alignedBlockOffset, lx1,
 			   y + unalignedBlockOffset, black);
 		drawDashedHorizLine (hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		// Line Down
 		cx2 = roundingScale (rm->chromStart +
 				     rm->blockRelStarts[idx +
 							1] -
 				     winStart, width, baseWidth) + xOff;
 		hvGfxLine (hvg, cx2, y + alignedBlockOffset, lx2,
 			   y + unalignedBlockOffset, black);
 		}
 	    }
 	}
     }
 
 }
 
 static void origRepeatDraw (struct track *tg, int seqStart, int seqEnd,
 		struct hvGfx *hvg, int xOff, int yOff, int width,
 		MgFont * font, Color color, enum trackVisibility vis)
 /* This is the original repeat drawing routine, modified
  * to handle the new rmskJoined table structure.
  */
 {
 int baseWidth = seqEnd - seqStart;
 struct repeatItem *ri;
 int y = yOff;
 int heightPer = tg->heightPer;
 int lineHeight = tg->lineHeight;
 int x1, x2, w;
 boolean isFull = (vis == tvFull);
 Color col;
 struct sqlConnection *conn = hAllocConn (database);
 struct sqlResult *sr = NULL;
 char **row;
 int rowOffset;
 
 
 if (isFull)
     {
     /*
      * Do gray scale representation spread out among tracks.
      */
     struct hash *hash = newHash (6);
     struct rmskJoined *ro;
     int percId;
     int grayLevel;
 
     for (ri = tg->items; ri != NULL; ri = ri->next)
 	{
 	ri->yOffset = y;
 	y += lineHeight;
 	hashAdd (hash, ri->class, ri);
 	}
     sr = hRangeQuery (conn, tg->table, chromName, winStart, winEnd, NULL,
 		      &rowOffset);
     while ((row = sqlNextRow (sr)) != NULL)
 	{
 	ro = rmskJoinedLoad (row + rowOffset);
 	char class[256];
 	// Simplify repClass for lookup: strip trailing '?',
 	// simplify *RNA to RNA:
 	char *poundPtr = index (ro->name, '#');
 	if (poundPtr)
 	    {
 	    safecpy (class, sizeof (class), poundPtr + 1);
 	    char *slashPtr = index (class, '/');
 	    if (slashPtr)
 	    *slashPtr = '\0';
 	    }
 	else
 	    {
 	    safecpy (class, sizeof (class), "Unknown");
 	    }
 	char *p = &(class[strlen (class) - 1]);
 	if (*p == '?')
 	*p = '\0';
 	if (endsWith (class, "RNA"))
 	safecpy (class, sizeof (class), "RNA");
 	ri = hashFindVal (hash, class);
 	if (ri == NULL)
 	ri = otherRepeatItem;
 	percId = 10000 - ro->score;
 	grayLevel = grayInRange (percId, 6000, 10000);
 	col = shadesOfGray[grayLevel];
 
 	int idx = 0;
 	for (idx = 0; idx < ro->blockCount; idx++)
 	    {
 	    if (ro->blockRelStarts[idx] > 0)
 		{
 		int blockStart = ro->chromStart + ro->blockRelStarts[idx];
 		int blockEnd =
 		    ro->chromStart + ro->blockRelStarts[idx] +
 		    ro->blockSizes[idx];
 
 		x1 = roundingScale (blockStart - winStart, width,
 				    baseWidth) + xOff;
 		x1 = max (x1, 0);
 		x2 = roundingScale (blockEnd - winStart, width,
 				    baseWidth) + xOff;
 		w = x2 - x1;
 		if (w <= 0)
 		w = 1;
 		hvGfxBox (hvg, x1, ri->yOffset, w, heightPer, col);
 		}
 	    }
 	rmskJoinedFree (&ro);
 	}
     freeHash (&hash);
     }
 else
     {
     char table[64];
     boolean hasBin;
     struct dyString *query = newDyString (1024);
     /*
      * Do black and white on single track.  Fetch less than
      * we need from database.
      */
     if (hFindSplitTable (database, chromName, tg->table, table, &hasBin))
 	{
 	dyStringPrintf (query,
 			"select chromStart,blockCount,blockSizes,"
 			"blockRelStarts from %s where ", table);
 	if (hasBin)
 	hAddBinToQuery (winStart, winEnd, query);
 	dyStringPrintf (query, "chromStart<%u and chromEnd>%u ",
 			winEnd, winStart);
 	/*
 	 * if we're using a single rmsk table, add chrom to the where clause
 	 */
 	if (startsWith ("rmskJoined", table))
 	dyStringPrintf (query, " and chrom = '%s' ", chromName);
 	sr = sqlGetResult (conn, query->string);
 	while ((row = sqlNextRow (sr)) != NULL)
 	    {
 	    int idx = 0;
 	    int blockCount = sqlSigned (row[1]);
 	    int sizeOne;
 	    int *blockSizes;
 	    int *blockRelStarts;
 	    int chromStart = sqlUnsigned (row[0]);
 	    sqlSignedDynamicArray (row[2], &blockSizes, &sizeOne);
 	    assert (sizeOne == blockCount);
 	    sqlSignedDynamicArray (row[3], &blockRelStarts, &sizeOne);
 	    assert (sizeOne == blockCount);
 
 	    for (idx = 1; idx < blockCount - 1; idx++)
 		{
 		if (blockRelStarts[idx] >= 0)
 		    {
 		    int blockStart = chromStart + blockRelStarts[idx];
 		    int blockEnd =
 			chromStart + blockRelStarts[idx] + blockSizes[idx];
 
 		    x1 = roundingScale (blockStart - winStart,
 					width, baseWidth) + xOff;
 		    x1 = max (x1, 0);
 		    x2 = roundingScale (blockEnd - winStart, width,
 					baseWidth) + xOff;
 		    w = x2 - x1;
 		    if (w <= 0)
 		    w = 1;
 		    hvGfxBox (hvg, x1, yOff, w, heightPer, MG_BLACK);
 		    }
 		}
 	    }
 	}
     dyStringFree (&query);
     }
 sqlFreeResult (&sr);
 hFreeConn (&conn);
 }
 
 /* Main callback for displaying this track in the viewport
  * of the browser.
  */
 static void jRepeatDraw (struct track *tg, int seqStart, int seqEnd,
 	     struct hvGfx *hvg, int xOff, int yOff, int width,
 	     MgFont * font, Color color, enum trackVisibility vis)
 {
 int baseWidth = seqEnd - seqStart;
 
 // TODO: Document
 pixelsPerBase = (float) width / (float) baseWidth;
   /*
    * Its unclear to me why heightPer is not updated to the
    * value set in jRepeatItemHeight() at the time this callback
    * is invoked.  Getting the correct value myself.
    * was:
    * int heightPer = tg->heightPer;
    */
 int heightPer = jRepeatItemHeight (tg, NULL);
 boolean isFull = (vis == tvFull);
 struct rmskJoined *rm;
 
   // If we are in full view mode and the scale is sufficient,
   // display the new visualization.
 if (isFull && baseWidth <= DETAIL_VIEW_MAX_SCALE)
     {
     int level = yOff;
 
     struct subTrack *st = hashFindVal (subTracksHash, tg->table);
     if (!st)
 	{
 	return;
 	}
     int lidx = st->levelCount;
     int currLevel = 0;
     for (currLevel = 0; currLevel < lidx; currLevel++)
 	{
 	rm = st->levels[currLevel];
 	while (rm)
 	    {
 	    drawRMGlyph (hvg, level, heightPer, width, baseWidth, xOff, rm);
 
 	    char statusLine[128];
 	    int ss1 = roundingScale (rm->alignStart - winStart,
 				     width, baseWidth) + xOff;
 
 	    safef (statusLine, sizeof (statusLine), "%s", rm->name);
 
 	    int x1 = roundingScale (rm->alignStart - winStart, width,
 				    baseWidth) + xOff;
 	    x1 = max (x1, 0);
 	    int x2 = roundingScale (rm->alignEnd - winStart, width,
 				    baseWidth) + xOff;
 	    int w = x2 - x1;
 	    if (w <= 0)
 	    w = 1;
 
 	    mapBoxHc (hvg, rm->alignStart, rm->alignEnd, ss1, level,
 		      w, heightPer, tg->track, rm->id, statusLine);
 	    rm = rm->next;
 	    }
 	level += heightPer;
 	rmskJoinedFreeList (&(st->levels[currLevel]));
 	}
     }
 else
     {
     // Draw the stereotypical view
     origRepeatDraw (tg, seqStart, seqEnd,
 		    hvg, xOff, yOff, width, font, color, vis);
     }
 }
 
 void jRepeatMethods (struct track *tg)
 {
 tg->loadItems = jRepeatLoad;
 tg->freeItems = jRepeatFree;
 tg->drawItems = jRepeatDraw;
 tg->colorShades = shadesOfGray;
 tg->itemName = jRepeatName;
 tg->mapItemName = jRepeatName;
 tg->totalHeight = jRepeatTotalHeight;
 tg->itemHeight = jRepeatItemHeight;
 tg->itemStart = tgItemNoStart;
 tg->itemEnd = tgItemNoEnd;
 tg->mapsSelf = TRUE;
 }