92c5b9ee733e28d68fbb180b26d175b46776a27e max Sat Sep 26 21:59:16 2026 -0700 hgTracks: fix hub genome dropdown's db-field collision, add real labels and position lookup The groupDropdown "Genomes:" select shared name='db' with TrackForm's own hidden db field, so submitting any button in the form (e.g. Hide all) sent two values for db and the hub's own genome silently won it. Give the select its own id instead, and switch genomes via an onchange handler that sets the real db field and resubmits. Also label each option via hOrganism()/hFreezeFromDb() (the same lookup hgTracks already uses for the page title), so native UCSC assemblies get a readable label too, not just GenArk-style hub genomes; and reset position to the target genome's own default position via hDbDb() before resubmitting, since a coordinate from the current assembly usually doesn't exist in the next one. refs #38434 diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index 2dd6b287f7e..8bf9456ffae 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -10295,41 +10295,90 @@ tracks that are annotations on %s that have been lifted to this \ assembly, along with a track (Alignment Differences) that shows \ mismatches and indels between %s and this assembly."\ , hub->defaultDb, hub->defaultDb); else safef(infoText, sizeof infoText, "A track hub is a list of tracks produced and hosted by external data providers. The UCSC browser group is not responsible for them. This hub is loaded from %s", hub->url); printInfoIconColor(infoText, "white"); } hPrintf("</td><td style='text-align:right;'>\n"); if (hubName) { if (cfgOptionBooleanDefault("groupDropdown", FALSE) && hub && hub->genomeList && hub->genomeList->next) { - puts("<span style='font-size:13px'>Genomes: </span><select style='width:7em' name='db'>"); + // deliberately not named "db": this select lives inside TrackForm + // alongside the page's own hidden db field, and sharing that name + // would submit two values for it on any button in the form + safef(idText, sizeof idText, "%s_dbSel", group->name); + hPrintf("<span style='font-size:13px'>Genomes: </span>" + "<select id='%s' style='max-width:9em; text-overflow:ellipsis; " + "overflow:hidden; white-space:nowrap;' " + "title='Switch to another genome in this hub'>\n", idText); for (struct trackHubGenome *thg = hub->genomeList; thg != NULL; thg = thg->next) { - if (!sameWord(thg->name, database)) - { - // hub genome names come from the hub, so encode them + // hOrganism/hFreezeFromDb/hDbDb all already know how to resolve either + // a native db (via dbDb) or a hub_-prefixed genome (via the hub's own + // genomes.txt fields) -- see the identical recipe used for the page + // title above, around trackHubSkipHubName(hOrganism(...)). For a NATIVE + // genome each of those three is its own hgcentral query (pooled + // connection, but still one round trip apiece), so a hub listing many + // native genomes pays 3N queries here. Not batching that: hubs with + // large genome counts are GenArk-style (twoBitPath genomes, resolved + // from the already-parsed hub with no SQL at all); hubs of many native + // genomes are old and small in practice. + char *shortName = trackHubSkipHubName(thg->name); // display only, not allocated + // trackHub.c's genome reader prefixes a hub genome's own organism + // setting with the hub name too (addAssembly), so strip it same as name + char *organismAlloc = hOrganism(thg->name); + char *organism = trackHubSkipHubName(organismAlloc); // display only + char *freeze = hFreezeFromDb(thg->name); + struct dbDb *thgDbDb = hDbDb(thg->name); // never errAborts, unlike hDefaultPos() + char *defaultPos = (thgDbDb != NULL) ? cloneString(thgDbDb->defaultPos) : NULL; + dbDbFree(&thgDbDb); + char extraBuf[400]; + extraBuf[0] = 0; + if (isNotEmpty(organism) && isNotEmpty(freeze)) + safef(extraBuf, sizeof extraBuf, "%s, %s", organism, freeze); + else if (isNotEmpty(organism)) + safef(extraBuf, sizeof extraBuf, "%s", organism); + else if (isNotEmpty(freeze)) + safef(extraBuf, sizeof extraBuf, "%s", freeze); + char labelBuf[512]; + if (isNotEmpty(extraBuf)) + safef(labelBuf, sizeof labelBuf, "%s - %s", shortName, extraBuf); + else + safef(labelBuf, sizeof labelBuf, "%s", shortName); char *escName = htmlEncode(thg->name); - printf("<option value='%s'>%s</option>\n", escName, escName); - } + char *escLabel = htmlEncode(labelBuf); + char *escPos = htmlEncode(emptyForNull(defaultPos)); + printf("<option value='%s' title='%s' data-pos='%s'%s>%s</option>\n", + escName, escLabel, escPos, + sameWord(thg->name, database) ? " selected" : "", escLabel); + freez(&organismAlloc); + freez(&freeze); + freez(&defaultPos); } puts("</select>"); + jsOnEventByIdF("change", idText, + "var o=this.selectedOptions[0]; document.TrackForm.db.value=this.value; " + "var posEl=document.getElementById('position'); " + "if (posEl) posEl.value=o.dataset.pos||''; " + "var posInput=document.getElementById('positionInput'); " + "if (posInput) posInput.value=''; " + "document.TrackForm.submit(); return true;"); } // visibility: hidden means that the element takes up space so the center alignment is not disturbed. if ((hub != NULL) && !startsWith("QuickLift", group->label)) { if (hub->descriptionUrl == NULL) { hPrintf("<a title='The track hub authors have not provided a descriptionUrl with background " "information about this track hub. "); if (hub->email) // hub-supplied, so encode it hPrintf("The authors can be reached at %s. ", htmlEncode(hub->email)); hPrintf("This link leads to our documentation page about the descriptionUrl statement in hub.txt. "); hPrintf("' href='../goldenPath/help/hgTrackHubHelp.html#hub.txt' " "style='color:#FFF; font-size: 13px;' target=_blank>No Info</a>");