03a3d6b3671c719c86ca59b8ef6aab7d07b1a68d
angie
  Mon Oct 8 11:04:37 2012 -0700
Improved mouseover titles for VCF hap-cluster display: trim indel left base,abbreviate long alleles (e.g. 40kb deletion was causing giant mouseover titles
to be ignored/truncated by web browser).

diff --git src/hg/hgTracks/vcfTrack.c src/hg/hgTracks/vcfTrack.c
index 11156ce..b2c2f48 100644
--- src/hg/hgTracks/vcfTrack.c
+++ src/hg/hgTracks/vcfTrack.c
@@ -466,106 +466,123 @@
 INLINE Color pgSnpColor(char *allele)
 /* Color allele by first base according to pgSnp palette. */
 {
 if (allele[0] == 'A')
     return revCmplDisp ? MG_MAGENTA : MG_RED;
 else if (allele[0] == 'C')
     return revCmplDisp ? darkGreenColor : MG_BLUE;
 else if (allele[0] == 'G')
     return revCmplDisp ? MG_BLUE : darkGreenColor;
 else if (allele[0] == 'T')
     return revCmplDisp ? MG_RED : MG_MAGENTA;
 else
     return shadesOfGray[5];
 }
 
+INLINE void abbrevAndHandleRC(char *abbrevAl, size_t abbrevAlSize, const char *fullAl)
+/* Limit the size of displayed allele to abbrevAlSize-1 and handle reverse-complemented display. */
+{
+boolean fullLen = strlen(fullAl);
+boolean truncating = (fullLen > abbrevAlSize-1);
+if (truncating)
+    {
+    int truncLen = abbrevAlSize - 4;
+    if (revCmplDisp)
+	{
+	safencpy(abbrevAl, abbrevAlSize, (fullAl + fullLen - truncLen), truncLen);
+	reverseComplement(abbrevAl, truncLen);
+	}
+    else
+	safencpy(abbrevAl, abbrevAlSize, fullAl, truncLen);
+    safecat(abbrevAl, abbrevAlSize, "...");
+    }
+else
+    {
+    safecpy(abbrevAl, abbrevAlSize, fullAl);
+    if (revCmplDisp)
+	reverseComplement(abbrevAl, fullLen);
+    }
+}
+
 INLINE void gtSummaryString(struct vcfRecord *rec, struct dyString *dy)
 // Make pgSnp-like mouseover text, but with genotype counts instead of allele counts.
 {
 if (isNotEmpty(rec->name) && !sameString(rec->name, "."))
     dyStringPrintf(dy, "%s: ", rec->name);
 if (rec->alleleCount < 2)
     {
     dyStringAppendC(dy, '?');
     return;
     }
 const struct vcfFile *vcff = rec->file;
 int gtRefRefCount = 0, gtRefAltCount = 0, gtAltAltCount = 0, gtUnkCount = 0, gtOtherCount = 0;
 int i;
 for (i=0;  i < vcff->genotypeCount;  i++)
     {
     struct vcfGenotype *gt = &(rec->genotypes[i]);
     if (gt->hapIxA == 0 && gt->hapIxB == 0)
 	gtRefRefCount++;
     else if (gt->hapIxA == 1 && gt->hapIxB == 1)
 	gtAltAltCount++;
     else if ((gt->hapIxA == 0 && gt->hapIxB == 1) || (gt->hapIxA == 1 && gt->hapIxB == 0))
 	gtRefAltCount++;
     else if (gt->hapIxA < 0 || gt->hapIxB < 0)
 	gtUnkCount++;
     else
 	gtOtherCount++;
     }
-// These are pooled strings! Restore when done.
-if (revCmplDisp)
-    {
-    for (i=0;  i < rec->alleleCount;  i++)
-	reverseComplement(rec->alleles[i], strlen(rec->alleles[i]));
-    }
+char refAl[16];
+abbrevAndHandleRC(refAl, sizeof(refAl), rec->alleles[0]);
+char altAl1[16];
+abbrevAndHandleRC(altAl1, sizeof(altAl1), rec->alleles[1]);
 if (sameString(chromName, "chrY"))
     dyStringPrintf(dy, "%s:%d %s:%d",
-		   rec->alleles[0], gtRefRefCount, rec->alleles[1], gtRefAltCount);
+		   refAl, gtRefRefCount, altAl1, gtRefAltCount);
 else
-    dyStringPrintf(dy, "%s/%s:%d %s/%s:%d %s/%s:%d", rec->alleles[0], rec->alleles[0], gtRefRefCount,
-		   rec->alleles[0], rec->alleles[1], gtRefAltCount,
-		   rec->alleles[1], rec->alleles[1], gtAltAltCount);
+    dyStringPrintf(dy, "%s/%s:%d %s/%s:%d %s/%s:%d", refAl, refAl, gtRefRefCount,
+		   refAl, altAl1, gtRefAltCount, altAl1, altAl1, gtAltAltCount);
 if (gtUnkCount > 0)
     dyStringPrintf(dy, " unknown:%d", gtUnkCount);
 if (gtOtherCount > 0)
     dyStringPrintf(dy, " other:%d", gtOtherCount);
-// Restore original values of pooled strings.
-if (revCmplDisp)
-    {
-    for (i=0;  i < rec->alleleCount;  i++)
-	reverseComplement(rec->alleles[i], strlen(rec->alleles[i]));
-    }
 }
 
 void mapBoxForCenterVariant(struct vcfRecord *rec, struct hvGfx *hvg, struct track *tg,
 			    int xOff, int yOff, int width)
 /* Special mouseover for center variant */
 {
 struct dyString *dy = dyStringNew(0);
+unsigned int chromStartMap = vcfRecordTrimIndelLeftBase(rec);
 gtSummaryString(rec, dy);
 dyStringAppend(dy, "   Haplotypes sorted on ");
 char *centerChrom = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "centerVariantChrom");
 if (centerChrom == NULL || !sameString(chromName, centerChrom))
     dyStringAppend(dy, "middle variant by default. ");
 else
     dyStringAppend(dy, "this variant. ");
 dyStringAppend(dy, "To anchor sorting to a different variant, click on that variant and "
 	       "then click on the 'Use this variant' button below the variant name.");
 const double scale = scaleForPixels(width);
 int x1 = round((double)(rec->chromStart-winStart)*scale) + xOff;
 int x2 = round((double)(rec->chromEnd-winStart)*scale) + xOff;
 int w = x2-x1;
 if (w <= 1)
     {
     x1--;
     w = 3;
     }
-mapBoxHgcOrHgGene(hvg, rec->chromStart, rec->chromEnd, x1, yOff, w, tg->height, tg->track,
+mapBoxHgcOrHgGene(hvg, chromStartMap, rec->chromEnd, x1, yOff, w, tg->height, tg->track,
 		  rec->name, dy->string, NULL, TRUE, NULL);
 }
 
 // These are initialized when we start drawing, then constant.
 static Color purple = 0;
 static Color undefYellow = 0;
 
 enum hapColorMode
     {
     altOnlyMode,
     refAltMode,
     baseMode
     };
 
 static Color colorByAltOnly(int refs, int alts, int unks)
@@ -929,50 +946,54 @@
 unsigned short gtHapIx = ((const struct hapCluster *)itemOrCluster)->gtHapIx;
 struct yFromNodeHelper *helper = extraData;
 if (gtHapIx >= helper->gtHapCount)
     errAbort("vcfTrack.c: gtHapIx %d out of range [0,%d).", gtHapIx, helper->gtHapCount);
 int y;
 if (yType == yrtStart)
     y = helper->gtHapIxToPxStart[gtHapIx];
 else if (yType == yrtEnd)
     y = helper->gtHapIxToPxEnd[gtHapIx];
 else
     y = (helper->gtHapIxToPxStart[gtHapIx] + helper->gtHapIxToPxEnd[gtHapIx]) / 2;
 return y;
 }
 
 void initTitleHelper(struct titleHelper *th, char *track, int startIx, int centerIx, int endIx,
-		     int nRecords, const struct vcfFile *vcff)
+		     int nRecords, struct vcfFile *vcff)
 /* Set up info including arrays of ref & alt alleles for cluster mouseover. */
 {
 th->track = track;
 th->startIx = startIx;
 th->centerIx = centerIx;
 th->endIx = endIx;
 th->nRecords = nRecords;
 int len = endIx - startIx;
 AllocArray(th->refs, len);
 AllocArray(th->alts, len);
 struct vcfRecord *rec;
 int i;
 for (rec = vcff->records, i = 0;  rec != NULL && i < endIx;  rec = rec->next, i++)
     {
     if (i < startIx)
 	continue;
-    th->refs[i-startIx] = rec->alleles[0];
-    th->alts[i-startIx] = cloneString(rec->alleles[1]);
-    tolowers(th->alts[i-startIx]);
+    char refAl[16];
+    abbrevAndHandleRC(refAl, sizeof(refAl), rec->alleles[0]);
+    th->refs[i-startIx] = vcfFilePooledStr(vcff, refAl);
+    char altAl1[16];
+    abbrevAndHandleRC(altAl1, sizeof(altAl1), rec->alleles[1]);
+    tolowers(altAl1);
+    th->alts[i-startIx] = vcfFilePooledStr(vcff, altAl1);
     }
 }
 
 static void drawTreeInLabelArea(struct hacTree *ht, struct hvGfx *hvg, int yOff, int clipHeight,
 				struct yFromNodeHelper *yHelper, struct titleHelper *titleHelper,
 				boolean drawRectangle)
 /* Draw the haplotype clustering in the left label area (as much as fits there). */
 {
 // Figure out which hvg to use, save current clipping, and clip to left label coords:
 struct hvGfx *hvgLL = (hvgSide != NULL) ? hvgSide : hvg;
 int clipXBak, clipYBak, clipWidthBak, clipHeightBak;
 hvGfxGetClip(hvgLL, &clipXBak, &clipYBak, &clipWidthBak, &clipHeightBak);
 hvGfxUnclip(hvgLL);
 hvGfxSetClip(hvgLL, leftLabelX, yOff, leftLabelWidth, clipHeight);
 // Draw the tree:
@@ -999,31 +1020,31 @@
 if (sameString(colorBy, VCF_HAP_COLORBY_ALTONLY))
     colorMode = altOnlyMode;
 else if (sameString(colorBy, VCF_HAP_COLORBY_REFALT))
     colorMode = refAltMode;
 else if (sameString(colorBy, VCF_HAP_COLORBY_BASE))
     colorMode = baseMode;
 return colorMode;
 }
 
 static void vcfHapClusterDraw(struct track *tg, int seqStart, int seqEnd,
 			      struct hvGfx *hvg, int xOff, int yOff, int width,
 			      MgFont *font, Color color, enum trackVisibility vis)
 /* Split samples' chromosomes (haplotypes), cluster them by center-weighted
  * alpha similarity, and draw in the order determined by clustering. */
 {
-const struct vcfFile *vcff = tg->extraUiData;
+struct vcfFile *vcff = tg->extraUiData;
 if (vcff->records == NULL)
     return;
 purple = hvGfxFindColorIx(hvg, 0x99, 0x00, 0xcc);
 undefYellow = hvGfxFindRgb(hvg, &undefinedYellowColor);
 enum hapColorMode colorMode = getColorMode(tg->tdb);
 pushWarnHandler(ignoreEm);
 struct vcfRecord *rec;
 for (rec = vcff->records;  rec != NULL;  rec = rec->next)
     vcfParseGenotypes(rec);
 popWarnHandler();
 unsigned short gtHapCount = 0;
 int nRecords = slCount(vcff->records);
 int centerIx = getCenterVariantIx(tg, seqStart, seqEnd, vcff->records);
 // Limit the number of variants that we compare, to keep from timing out:
 // (really what we should limit is the number of distinct haplo's passed to hacTree!)