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 = " → in-frame exon"; + else + exonNote = " → out-of-frame exon"; + safef(buf, EXONTEXTLEN, ", codon phase: start %d, end %d%s", startPhase, endPhase, exonNote); + } + else + { + if (startPhase==0) + exonNote = " → in-frame exon"; + else + exonNote = " → 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 = " → in-frame exon"; - safef(buf, sizeof(buf), ", codon phase: start %d, end %d%s", startPhase, endPhase, exonNote); - } - else - { - if (startPhase==0) - exonNote = " → 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