bd5ebb2dc0b10ad589ed54c3319da1fac28d2b26
max
  Wed May 7 06:38:51 2025 -0700
adding html to genePred exon mouseover, refs #33749

diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c
index 7019c9e5acb..55dfafa301c 100644
--- src/hg/hgTracks/simpleTracks.c
+++ src/hg/hgTracks/simpleTracks.c
@@ -2659,53 +2659,55 @@
 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 */
 {
 
+static const char *phaseHelp = "<a style='float:right' target=_blank href='../goldenPath/help/codonPhase.html'>Phase?</a><br>";
+
 if (startPhase==-1) // UTRs don't have a frame at all
     {
-    safef(buf, EXONTEXTLEN, ", untranslated region");
+    safef(buf, EXONTEXTLEN, "<b>No Codon:</b> Untranslated region<br>");
     }
 else
     {
     char *exonNote = "";
     boolean isNotLastExon = (exonIntronNumber<numExons);
     if (isNotLastExon)
         {
         if (startPhase==endPhase)
             exonNote = ": in-frame exon";
         else
             exonNote = ": out-of-frame exon";
-        safef(buf, EXONTEXTLEN, ", codon phase: start %d, end %d%s", startPhase, endPhase, exonNote);
+        safef(buf, EXONTEXTLEN, "<b>Codon phase:</b> start %d, end %d%s<br>%s", startPhase, endPhase, exonNote, phaseHelp);
         } 
     else
         {
         if (startPhase==0)
             exonNote = ": in-frame exon";
         else
             exonNote = ": out-of-frame exon";
-        safef(buf, EXONTEXTLEN, ", start codon phase %d%s", startPhase, exonNote);
+        safef(buf, EXONTEXTLEN, "<b>Codon phase:</b> start %d%s<br>%s", startPhase, exonNote, phaseHelp);
         }
     }
 }
 
 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;
@@ -2998,39 +3000,38 @@
             }
 	    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 = "";
             // 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 */
+            // 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];
                 // 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
                 if (isExon && lf->codons && zoomedToCdsColorLevel)
                     {
                     struct simpleFeature *codon;
@@ -3054,52 +3055,52 @@
                             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);
+                                        dyStringPrintf(codonDy, "<b>Transcript: </b> %s<br>", 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",
+                                        dyStringPrintf(codonDy, "<b>cDNA: </b> c.%d-%d<br>", codonHgvsIx + 1, codonHgvsIx + 3);
+                                    dyStringPrintf(codonDy, "<b>Strand: </b> %c<br><b>Exon: </b>%s %d of %d<br>%s",
                                                 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 // either an intron, or else an exon zoomed out too far for codons (or no codons)
                     {
                     char *sep = "";
                     if (!isEmpty(existingText))
-                        sep = ", ";
+                        sep = "<br>";
 
-                    safef(mouseOverText, sizeof(mouseOverText), "%s%sstrand %c, %s %d of %d%s",
+                    safef(mouseOverText, sizeof(mouseOverText), "<b>Transcript:</b> %s%s<b>Strand:</b> %c<br><b>Exon:</b> %s %d of %d<br>%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?
                                     // JC: Why do we care about pileups?  First mapbox drawn wins.
                     }
                 }