d540e9dca3211faf36fab80fa3b640d4bd6ee288
max
  Tue Jan 23 06:18:20 2024 -0800
adding phase mouseover, refs #32487

diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c
index 0aca981..5d0d0c3 100644
--- src/hg/hgTracks/simpleTracks.c
+++ src/hg/hgTracks/simpleTracks.c
@@ -2830,43 +2830,46 @@
     }
 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))
@@ -2898,40 +2901,85 @@
 		{
 		exonIntronText = intronText;
 		--numExonIntrons;  // introns are one fewer than exons
 		}
 
             char strandChar;
 	    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->exonFrames && isExon)
+                {
+                // 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
+                int startPhase = gp->exonFrames[exonIx-1];
+                int endPhase = -1;
+                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), ", start-end codon phase %d-%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 (!isEmpty(existingText))
-                safef(mouseOverText, sizeof(mouseOverText), "%s, strand %c, %s %d of %d", 
-                        existingText, strandChar, exonIntronText, exonIntronNumber, numExonIntrons);
+                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", 
-                        strandChar, exonIntronText, exonIntronNumber, numExonIntrons);
+                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?
 		}
 	    }