7bed3b764ff1081822f1a43845464107d71528e9
chmalee
  Mon Feb 26 09:25:48 2024 -0800
Add codon position information to genePred mouseovers, refs #32815

diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c
index b67298a..722d19f 100644
--- src/hg/hgTracks/simpleTracks.c
+++ src/hg/hgTracks/simpleTracks.c
@@ -2833,70 +2833,71 @@
 else
     {
     exonText   = trackDbSettingClosestToHomeOrDefault(tg->tdb, "exonText"  , "Exon"  );
     intronText = trackDbSettingClosestToHomeOrDefault(tg->tdb, "intronText", "Intron");
     }
 while (exon != NULL)
 /* Make a stupid list of exons separate from what's given. */
 /* It seems like lf->components isn't necessarily sorted. */
     {
     refAdd(&exonList, exon);
     exon = exon->next;
     }
 /* Now sort it. */
 slSort(&exonList, exonSlRefCmp);
 
+
 numExons = slCount(exonList);
 struct genePred *gp = lf->original;
 boolean revStrand = (lf->orientation == -1);
 int eLast = -1;
 int s = -1;
 int e = -1;
 char mouseOverText[256];
 boolean isExon = TRUE;
 int picStart = insideX;
 int picEnd = picStart + insideWidth;
 if (lButton)
     picStart += buttonW;
 if (rButton)
     picEnd -= buttonW;
 
 for (ref = exonList; TRUE; )
     {
     exon = ref->val;
     if (isExon)
 	{
 	s = exon->start;
 	e = exon->end;
 	}
     else
 	{
 	s = eLast;
 	e = exon->start;
 	}
     // skip exons and introns that are completely outside the window
-    if (!(s > winEnd) || (e < winStart))
+    if (s <= winEnd && e >= winStart)
 	{
 	int sClp = (s < winStart) ? winStart : s;
 	int eClp = (e > winEnd)   ? winEnd   : e;
 
 	int sx = round((sClp - winStart)*scale) + insideX;
 	int ex = round((eClp - winStart)*scale) + insideX;
 
         // skip regions entirely outside available picture
         // (accounts for space taken by exon arrows buttons)
-	if (!(sx > picEnd) || (ex < picStart))
+	if (sx <= picEnd && ex >= picStart)
 	    {
 	    // clip it to avail pic
 	    sx = (sx < picStart) ? picStart : sx;
     	    ex = (ex > picEnd)   ? picEnd   : ex;
 
 	    int w = ex - sx;
 
 	    int exonIntronNumber;
 	    char *exonIntronText;
 	    int numExonIntrons = numExons;
 	    if (isExon)
 		{
 		exonIntronText = exonText;
 		}
 	    else
@@ -2952,70 +2953,121 @@
 
                         if (startPhase==endPhase)
                             exonNote = " &#8594; in-frame exon";
                         safef(buf, sizeof(buf), ", codon phase: start %d, end %d%s", startPhase, endPhase, exonNote);
                         } 
                     else
                         {
                         if (startPhase==0)
                             exonNote = " &#8594; in-frame exon";
                         safef(buf, sizeof(buf), ", start codon phase %d%s", startPhase, exonNote);
                         }
                     frameText = buf;
                     }
                 }
 
+	    if (w > 0) // draw exon or intron if width is greater than 0
+		{
+                // draw mapBoxes for the codons if we are zoomed in far enough
+                struct simpleFeature *codon;
+                struct dyString *codonDy = dyStringNew(0);
+                int codonS, codonE;
+                if (lf->codons && lf->codons->codonIndex > 0 && zoomedToCdsColorLevel)
+                    {
+                    for (codon = lf->codons; codon != NULL; codon = codon->next)
+                        {
+                        codonS = codon->start; codonE = codon->end;
+                        if (codonS <= winEnd && codonE >= winStart)
+                            {
+                            int codonSClp = (codonS < winStart) ? winStart : codonS;
+                            int codonEClp = (codonE > winEnd)   ? winEnd   : codonE;
+
+                            int codonsx = round((codonSClp - winStart)*scale) + insideX;
+                            int codonex = round((codonEClp - winStart)*scale) + insideX;
+
+                            // skip regions entirely outside available picture
+                            // (accounts for space taken by exon arrows buttons)
+                            if (codonsx <= picEnd && codonex >= picStart)
+                                {
+                                // clip it to avail pic
+                                codonsx = (codonsx < picStart) ? picStart : codonsx;
+                                codonex = (codonex > picEnd)   ? picEnd   : codonex;
+
+                                int w = codonex - codonsx;
+                                if (w > 0)
+                                    {
+                                    // temporarily remove the mouseOver from the lf, since linkedFeatureMapItem will always 
+                                    // prefer a lf->mouseOver over the itemName
+                                    char *oldMouseOver = lf->mouseOver;
+                                    lf->mouseOver = NULL;
+                                    dyStringClear(codonDy);
+                                    if (!isEmpty(existingText))
+                                        dyStringPrintf(codonDy, "%s, ", existingText);
+                                    int codonHgvsIx = (codon->codonIndex - 1) * 3;
+                                    dyStringPrintf(codonDy, "c.%d-%d, ", codonHgvsIx + 1, codonHgvsIx + 3);
+                                    dyStringPrintf(codonDy, "strand %c, %s %d of %d%s",
+                                                strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText);
+                                    tg->mapItem(tg, hvg, item, codonDy->string, tg->mapItemName(tg, item),
+                                            sItem, eItem, codonsx, y, w, heightPer);
+                                    // and restore the mouseOver
+                                    lf->mouseOver = oldMouseOver;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                else
+                    {
+                    // temporarily remove the mouseOver from the lf, since linkedFeatureMapItem will always 
+                    // prefer a lf->mouseOver over the itemName
                     if (!isEmpty(existingText))
                         safef(mouseOverText, sizeof(mouseOverText), "%s, strand %c, %s %d of %d%s",
                                 existingText, strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText);
                     else
                         safef(mouseOverText, sizeof(mouseOverText), "strand %c, %s %d of %d%s",
                                 strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText);
-
-	    if (w > 0) // draw exon or intron if width is greater than 0
-		{
-                // temporarily remove the mouseOver from the lf, since linkedFeatureMapItem will always 
-                // prefer a lf->mouseOver over the itemName
                     char *oldMouseOver = lf->mouseOver;
                     lf->mouseOver = NULL;
                     tg->mapItem(tg, hvg, item, mouseOverText, tg->mapItemName(tg, item),
                         sItem, eItem, sx, y, w, heightPer);
                     // and restore the mouseOver
                     lf->mouseOver = oldMouseOver;
+                    }
 
 		picStart = ex;  // prevent pileups. is this right? add 1? does it work?
 		}
 	    }
 	}
 
     if (isExon)
 	{
     	eLast = e;
 	ref = ref->next;
 	if (!ref)
 	    break;
 	}
     else
 	{
 	exonIx++;
 	}
     isExon = !isExon;
 
     if (s > winEnd) // since the rest will also be outside the window
 	break;
 
     }
+
 slFreeList(&exonList);
 }
 
 static struct window *makeMergedWindowList(struct window *windows)
 /* Make a copy of the windows list, merging nearby regions on the same chrom
 and which are within some limit (1MB?) of each other. */
 {
 int mergeLimit = 1024*1024;
 struct window *w = windows;
 struct window *resultList = NULL;
 struct window *mergedW = NULL;
 if (!windows)
     errAbort("Unexpected error: windows list NULL in makeMergedWindowList()");
 boolean doNew = TRUE;
 while(TRUE)