11049a7e4e1de1b3218ec8184cdf5e9df8658681 braney Mon Jul 20 10:05:02 2026 -0700 quickLift: highlight the clicked difference on the hgc details page hgTracks/quickLift.c now encodes the specific difference in each mapbox item name as chainId.type.chromStart.chromEnd (the "identical" region boxes keep the bare chain id). hgc.c parses that item name back into a chain id plus the clicked region, bolds the matching row in the Deletions/Insertions/Doubles/ Mismatches tables, and prints a note telling the user which row they clicked. Also adds bigQuickLiftChain to the set of types that suppress the details-page header item. diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index a99485e8b78..bdc8a98ff62 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -4801,30 +4801,50 @@ printf("total%f\n", as->total); printf("standard deviation%f\n", as->stdDev); printf("\n"); } void quickLiftChainClick(struct sqlConnection *conn, struct trackDb *tdb, char *item, int start, char *otherDb) /* Handle a click on the quickChain track */ { struct twoBitFile *otherTbf = getOtherTwoBitUrl(tdb); char *otherOrg = NULL; struct chain *chain = NULL, *subChain = NULL, *toFree = NULL; int chainWinSize; boolean otherIsActive = FALSE; +// The item name is either a bare chain id (from the "identical" regions) or +// "chainId.type.chromStart.chromEnd" identifying the specific difference that +// was clicked. Pull the chain id off the front and remember the clicked +// region (if any) so we can bold it in the tables below. +char *chainItem = cloneString(item); +long clickStart = -1, clickEnd = -1; +char *dot = strchr(chainItem, '.'); +if (dot != NULL) + { + *dot = 0; + char *rest = dot + 1; + char *words[4]; + if (chopByChar(rest, '.', words, ArraySize(words)) == 3) + { + clickStart = atol(words[1]); + clickEnd = atol(words[2]); + } + } +item = chainItem; + if (hDbIsActive(otherDb)) otherIsActive = TRUE; if (! sameWord(otherDb, "seq")) { otherOrg = hOrganism(otherDb); } if (otherOrg == NULL) { /* use first word of chain label (count on org name as first word) */ otherOrg = firstWordInLine(cloneString(tdb->shortLabel)); } chain = chainLoadItemInRange(tdb, item); if (startsWith("big", tdb->type)) @@ -4893,115 +4913,124 @@ case QUICKTYPE_INSERT: insertions++; break; case QUICKTYPE_DEL: deletions++; break; case QUICKTYPE_DOUBLE: doubles++; break; case QUICKTYPE_MISMATCH: mismatches++; break; } } +// If the click came from a specific difference (not one of the "identical" +// regions), the matching row is shown in bold in the tables below. +if (clickStart >= 0) + printf("
The item you clicked on is shown in bold in the tables below.
\n"); + if (deletions) { printf("
Deletions in Window:
"); printf("\n"); printf("", trackHubSkipHubName(database), trackHubSkipHubName(otherDb)); for(hr = regions; hr; hr = hr->next) { if (hr->type != QUICKTYPE_DEL) continue; char *ourPos, *otherPos; snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd); ourPos = cloneString(addCommasToPos(database, position)); snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd); otherPos = cloneString(addCommasToPos(database, position)); - printf(""); } printf("
%s Position%s PositionBasesAlignment
%s%s%.*s", ourPos, otherPos, hr->otherBaseCount, hr->otherBases); + char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : ""; + printf("%s%s%.*s", hilite, ourPos, otherPos, hr->otherBaseCount, hr->otherBases); hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track); printf("alignment
"); } if (insertions) { printf("
Insertions in Window:
"); printf("\n"); printf("", trackHubSkipHubName(database), trackHubSkipHubName(otherDb)); for(hr = regions; hr; hr = hr->next) { if (hr->type != QUICKTYPE_INSERT) continue; char *ourPos, *otherPos; snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd); ourPos = cloneString(addCommasToPos(database, position)); snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd); otherPos = cloneString(addCommasToPos(database, position)); - printf(""); } printf("
%s Position%s PositionBasesAlignment
%s%s%.*s", ourPos, otherPos, hr->baseCount, hr->bases); + char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : ""; + printf("%s%s%.*s", hilite, ourPos, otherPos, hr->baseCount, hr->bases); hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track); printf("alignment
"); } if (doubles) { printf("
Double Gaps in Window:
"); printf("\n"); printf("", trackHubSkipHubName(database), trackHubSkipHubName(otherDb), trackHubSkipHubName(database), trackHubSkipHubName(otherDb)); for(hr = regions; hr; hr = hr->next) { if (hr->type != QUICKTYPE_DOUBLE) continue; char *ourPos, *otherPos; snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd); ourPos = cloneString(addCommasToPos(database, position)); snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd); otherPos = cloneString(addCommasToPos(database, position)); - printf(""); } printf("
%s Position%s Position# Bases in %s#Bases in %sAlignment
%s%s%d%d", ourPos, otherPos, hr->baseCount, hr->otherBaseCount); + char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : ""; + printf("%s%s%d%d", hilite, ourPos, otherPos, hr->baseCount, hr->otherBaseCount); hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track); printf("alignment
"); } if (mismatches) { printf("
Mismatches in Window:
"); printf("\n"); printf("", trackHubSkipHubName(database), trackHubSkipHubName(otherDb)); for(hr = regions; hr; hr = hr->next) { if (hr->type != QUICKTYPE_MISMATCH) continue; char *ourPos, *otherPos; snprintf(position, 128, "%s:%ld-%ld", hr->chrom, hr->chromStart, hr->chromEnd); ourPos = cloneString(addCommasToPos(database, position)); snprintf(position, 128, "%s:%ld-%ld", hr->oChrom, hr->oChromStart, hr->oChromEnd); otherPos = cloneString(addCommasToPos(database, position)); - printf(""); } printf("
%s Position%s PositionChangeAlignment
%s%s%.*s -> %.*s", ourPos, otherPos, hr->otherBaseCount, hr->otherBases, hr->baseCount, hr->bases); + char *hilite = (clickStart >= 0 && hr->chromStart == clickStart && hr->chromEnd == clickEnd) ? " style='font-weight:bold'" : ""; + printf("%s%s%.*s -> %.*s", hilite, ourPos, otherPos, hr->otherBaseCount, hr->otherBases, hr->baseCount, hr->bases); hgcAnchorSomewhereExt("htcChainAli", item, tdb->track, chain->tName, hr->chromStart - 10, hr->chromEnd + 10, tdb->track); printf("alignment
"); } } void genericClickHandlerPlus( struct trackDb *tdb, char *item, char *itemForUrl, char *plus) /* Put up generic track info, with additional text appended after item. */ { char *dupe, *type, *words[16], *headerItem; int wordCount; int start = cartInt(cart, "o"); int end = cartInt(cart, "t"); @@ -5023,30 +5052,31 @@ } else if (!trackHubDatabase(database)) conn = hAllocConnTrack(database, tdb); if (itemForUrl == NULL) itemForUrl = item; dupe = cloneString(tdb->type); wordCount = chopLine(dupe, words); headerItem = cloneString(item); type = words[0]; /* Suppress printing item name in page header, as it is not informative for these types of * tracks... */ if (container == NULL && wordCount > 0) { if (sameString(type, "maf") || sameString(type, "wigMaf") || sameString(type, "bigMaf") || sameString(type, "netAlign") + || sameString(type, "bigQuickLiftChain") || sameString(type, "encodePeak")) headerItem = NULL; else if (( sameString(type, "narrowPeak") || sameString(type, "broadPeak") || sameString(type, "gappedPeak") ) && headerItem && sameString(headerItem, ".") ) headerItem = NULL; } /* Print header. */ genericHeader(tdb, headerItem); if (differentString(type, "bigInteract") && differentString(type, "interact")) { // skip generic URL code as these may have multiple items returned for a click