80dce1b0dc80c7c4fe8f81523b3d4754a7ebc9bf
max
  Sun Apr 21 17:05:48 2024 -0700
changing exon mouseover as per Ana and user ML, ref #33506

diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c
index ca1883e..d42286f 100644
--- src/hg/hgTracks/simpleTracks.c
+++ src/hg/hgTracks/simpleTracks.c
@@ -2608,30 +2608,67 @@
     *pNewWinStart = 0;
 *pNewWinEnd = *pNewWinStart + newWinSize;
 }
 
 void linkedFeaturesMoveWinEnd(long exonEnd, long bufferToEdge, long newWinSize, long *pNewWinStart, long *pNewWinEnd)
 /* A function used by linkedFeaturesNextPrevItem to make that function */
 /* easy to read. Move the window so that the end of the exon in question */
 /* is near the end of the browser window. */
 {
 *pNewWinEnd = exonEnd + bufferToEdge;
 if (*pNewWinEnd > virtSeqBaseCount)
     *pNewWinEnd = virtSeqBaseCount;
 *pNewWinStart = *pNewWinEnd - newWinSize;
 }
 
+#define EXONTEXTLEN 256
+
+static void makeExonFrameText(int exonIntronNumber, int numExons, int startPhase, int endPhase, char *buf) 
+/* Write mouseover text that describes the exon's phase into buf[EXONTEXTLEN].
+
+   Note that start/end-phases are in the direction of transcription:
+   if transcript is on + strand, the start phase is the exonFrame value, and the end phase is the next exonFrame (3' on DNA) value
+   if transcript is on - strand, the start phase is the previous (=3' on DNA) exonFrame and the end phase is the exonFrame */
+{
+
+if (startPhase==-1) // UTRs don't have a frame at all
+    {
+    safef(buf, EXONTEXTLEN, ", untranslated region");
+    }
+else
+    {
+    char *exonNote = "";
+    boolean isNotLastExon = (exonIntronNumber<numExons);
+    if (isNotLastExon)
+        {
+        if (startPhase==endPhase)
+            exonNote = " &#8594; in-frame exon";
+        else
+            exonNote = " &#8594; out-of-frame exon";
+        safef(buf, EXONTEXTLEN, ", codon phase: start %d, end %d%s", startPhase, endPhase, exonNote);
+        } 
+    else
+        {
+        if (startPhase==0)
+            exonNote = " &#8594; in-frame exon";
+        else
+            exonNote = " &#8594; out-of-frame exon";
+        safef(buf, EXONTEXTLEN, ", start codon phase %d%s", startPhase, exonNote);
+        }
+    }
+}
+
 boolean linkedFeaturesNextPrevItem(struct track *tg, struct hvGfx *hvg, void *item, int x, int y, int w, int h, boolean next)
 /* Draw a mapBox over the arrow-button on an *item already in the window*. */
 /* Clicking this will do one of several things: */
 {
 boolean result = FALSE;
 struct linkedFeatures *lf = item;
 struct simpleFeature *exons = lf->components;
 struct simpleFeature *exon = exons;
 char *nextExonText;
 char *prevExonText;
 long newWinSize = virtWinEnd - virtWinStart;
 long bufferToEdge = 0.05 * newWinSize;
 long newWinStart, newWinEnd;
 int numExons = 0;
 int exonIx = 0;
@@ -2841,31 +2878,30 @@
 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;
 
@@ -2918,71 +2954,49 @@
 	    if (!revStrand) {
 		exonIntronNumber = exonIx;
                 strandChar = '+';
             }
 	    else {
 		exonIntronNumber = numExonIntrons-exonIx+1;
                 strandChar = '-';
             }
 
             // we still need to show the existing mouseover text
             char* existingText = lf->mouseOver;
             if (isEmpty(existingText))
                 existingText = lf->name;
 
             // construct a string that tells the user about the codon frame situation of this exon
-            char *frameText = "";
-            if ((gp != NULL) && gp->exonFrames && isExon)
-                {
-                // start/end-phases are in the direction of transcription:
+            // char *frameText = "";
+            // for coding exons, determine the start and end phase of the exon and an English text describing both:
             // if transcript is on + strand, the start phase is the exonFrame value, and the end phase is the next exonFrame (3' on DNA) value
-                // if transcript is on - strand, the start phase is the previous (=3' on DNA) exonFrame and the end phase is the exonFrame
-                int startPhase = gp->exonFrames[exonIx-1];
+            // if transcript is on - strand, the start phase is the previous (=3' on DNA) exonFrame and the end phase is the exonFrame */
+            int startPhase = -1;
             int endPhase = -1;
+            char phaseText[EXONTEXTLEN];
+            phaseText[0] = 0;
+            if ((gp != NULL) && gp->exonFrames && isExon)
+                {
+                startPhase = gp->exonFrames[exonIx-1];
+                //printf("start phase is set<br>");
                 if (!revStrand) 
                     endPhase = gp->exonFrames[exonIx];
                 else 
                     if (exonIx>1)
                         endPhase = gp->exonFrames[exonIx-2];
-
-                if (gp->exonFrames[exonIx-1]==-1) // UTRs don't have a frame at all
-                    {
-                    frameText = ", untranslated region";
-                    }
-                else
-                    {
-                    //printf("%s %d %d %s_ex_%d_frame_%d<br>", chromName, s, e, gp->name, exonIx, startPhase);
-                    char buf[256];
-                    char *exonNote = "";
-                    if (exonIntronNumber<numExons) // do not do this for the last exon (exonIx is 1-based)
-                        {
-                        //printf("exonIx %d, numExons %d<br>", exonIx, numExons);
-                        ////int nextExonFrame = gp->exonFrames[nextExIx];
-                        //printf("nextExIx %d, nextExonFrame %d, endPhase %d<br>", nextExIx, nextExonFrame, endPhase);
-
-                        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;
-                    }
+                // construct a string that tells the user about the codon frame situation of this exon
+                makeExonFrameText(exonIntronNumber, numExons, startPhase, endPhase, phaseText);
                 }
 
 	    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 (isExon && lf->codons && zoomedToCdsColorLevel)
                     {
                     for (codon = lf->codons; codon != NULL; codon = codon->next)
                         {
                         codonS = codon->start; codonE = codon->end;
                         if (codonS <= winEnd && codonE >= winStart)
                             {
@@ -3002,58 +3016,77 @@
 
                                 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;
                                     if (codonHgvsIx >= 0)
                                         dyStringPrintf(codonDy, "c.%d-%d, ", codonHgvsIx + 1, codonHgvsIx + 3);
                                     dyStringPrintf(codonDy, "strand %c, %s %d of %d%s",
-                                                strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText);
+                                                strandChar, exonIntronText, exonIntronNumber, numExonIntrons, phaseText);
                                     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);
+                                existingText, strandChar, exonIntronText, exonIntronNumber, numExonIntrons, phaseText);
                     else
                         safef(mouseOverText, sizeof(mouseOverText), "strand %c, %s %d of %d%s",
-                                strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText);
+                                strandChar, exonIntronText, exonIntronNumber, numExonIntrons, phaseText);
                     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;
                     }
 
+	    if (w > 0) // draw exon or intron if width is greater than 0
+		{
+                char *sep = "";
+                if (!isEmpty(existingText))
+                    sep = ", ";
+
+                safef(mouseOverText, sizeof(mouseOverText), "%s%sstrand %c, %s %d of %d%s", 
+                        existingText, sep, strandChar, exonIntronText, exonIntronNumber, numExonIntrons, phaseText);
+
+                // 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 old 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++;
@@ -4178,30 +4211,32 @@
     }
 if (!hideArrows)
     {
     if ((intronGap == 0) && (vis == tvFull || vis == tvPack))
 	{
 	if (lf->highlightColor && (lf->highlightMode == highlightOutline))
 	    clippedBarbs(hvg, x1, midY, w, tl.barbHeight, tl.barbSpacing,
                          lf->orientation, lf->highlightColor, FALSE);
         else
             clippedBarbs(hvg, x1, midY, w, tl.barbHeight, tl.barbSpacing,
                          lf->orientation, bColor, FALSE);
         }
     }
 
 components = (lf->codons && zoomedToCdsColorLevel) ? lf->codons : lf->components;
+
+
 for (sf = components; sf != NULL; sf = sf->next)
     {
     s = sf->start; e = sf->end;
 
     /* Draw UTR portion(s) of exon, if any: */
     if (s < tallStart)
 	{
 	e2 = e;
 	if (e2 > tallStart) e2 = tallStart;
 	if (lf->highlightColor && (lf->highlightMode == highlightOutline))
 	    {
 	    drawScaledBox(hvg, s, e2, scale, xOff, y+shortOff , shortHeight , lf->highlightColor);
 	    drawScaledBox(hvg, s, e2, scale, xOff + 1, y+shortOff + 1, shortHeight - 2, color);
 	    }
 	else