f48fc325d72424177878c4849096c77e06c2d3b0 max Mon Aug 17 07:34:42 2026 -0700 BLAT results: own track group, Delete-all button, clearer names, refs #38086 Behind hg.conf blatResultsGroup (default off, a release gate): - BLAT result custom tracks go into their own "BLAT Results" track group instead of the generic Custom Tracks group. - That group's header gets a "Delete all" button that removes every BLAT result track at once, so users are not stuck deleting them one by one. - Headerless queries are named by query size + the top hit's gene, e.g. "360bp SOD1", instead of the useless "blat YourSeq"; the date goes in the longLabel. hgBlat.c getCustomName naming + blatDateStamp/topHitLocusLabel helpers hgc.c buildBigPsl tags the results track group=blat customFactory.c checkGroup accepts the synthetic "blat" group hgTracks.c synthesize the BLAT Results group; Delete-all button; keep the per-track delete icon for the new group hgTracks.js deleteAllBlatTracks() hgCustom.c hgct_do_delete_blat action (removes all blatResult=on tracks) hgConfCatalog.py register blatResultsGroup as a release gate diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index 45051e58b9a..6f695dd6b82 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -7241,30 +7241,47 @@ // // If there isn't a map group, make one and set the priority so it will be right after custom tracks // and hub groups. if (!foundMap) { AllocVar(group); group->name = cloneString("map"); group->label = cloneString("Mapping and Sequencing"); group->defaultPriority = priority; group->priority = priority; group->defaultIsClosed = FALSE; slAddHead(&list, group); hashAdd(hash, "map", group); } +// The "BLAT Results" group holds BLAT-search result custom tracks (group=blat, tagged +// blatResult=on), keeping them out of the generic Custom Tracks group so they are easy to find and +// clear as a set. It is synthesized here rather than stored in the grp table; the group header is +// skipped when it has no tracks (see the group loop that draws the controls). Priority 1.5 places +// it just after Custom Tracks (priority 1) so BLAT users find their results near the top. Gated by +// hg.conf blatResultsGroup, the same flag hgBlat/hgc read before tagging tracks with group=blat. +if (cfgOptionBooleanDefault("blatResultsGroup", FALSE)) + { + AllocVar(group); + group->name = cloneString("blat"); + group->label = cloneString("BLAT Results"); + group->defaultPriority = group->priority = 1.5; + group->defaultIsClosed = FALSE; + slAddHead(&list, group); + hashAdd(hash, "blat", group); + } + // The "Visible Tracks" group is now the default top group struct group *visible = NULL; AllocVar(visible); visible->name = "visible"; visible->label = "Visible Tracks"; visible->defaultPriority = priority; visible->priority = -1; visible->defaultIsClosed = FALSE; slAddHead(&list, visible); hashAdd(hash, "visible", visible); /* Loop through tracks and fill in their groups. * If necessary make up an unknown group. */ for (track = *pTrackList; track != NULL; track = track->next) @@ -8840,31 +8857,31 @@ printTrashIcon("Delete this custom track", "trackDeleteIcon", dataAttrs); } static void printQuickLiftDelIcon(struct track *track, char *sourceDb) /* Trash-can icon next to a track in a quickLift group; clicking it removes * the track from the quickLift hub. */ { char dataAttrs[1024]; safef(dataAttrs, sizeof dataAttrs, "data-track='%s' data-sourcedb='%s'", track->track, sourceDb); printTrashIcon("Remove this track from the QuickLift group", "quickLiftDelIcon", dataAttrs); } static void printTrackLink(struct track *track) /* print a link hgTrackUi with shortLabel and various icons and mouseOvers */ { -if (sameOk(track->groupName, "user")) +if (sameOk(track->groupName, "user") || sameOk(track->groupName, "blat")) printTrackDelIcon(track); char *quickLiftSourceDb = (track->tdb != NULL) ? trackDbSetting(track->tdb, "quickLiftDb") : NULL; /* skip the synthetic "Alignment Differences" track -- it's added at runtime * by hubConnect, not from the hub file, so removing it from the file is a * no-op and it would reappear on the next page load. */ if (quickLiftSourceDb != NULL && (track->tdb->type == NULL || !startsWith("bigQuickLiftChain", track->tdb->type))) printQuickLiftDelIcon(track, quickLiftSourceDb); if (track->hasUi) { char *url = trackUrl(track->track, chromName); char *longLabel = replaceChars(track->longLabel, "\"", """); @@ -10212,30 +10229,42 @@ if (hub->email) hPrintf("The authors can be reached at %s", hub->email); hPrintf("' href='%s' " "style='color:#FFF; font-size: 13px;' target=_blank>Info", hub->descriptionUrl); } hPrintf("  "); } } hPrintf(" ", group->name); + // The BLAT Results group gets a "Delete all" button that removes every BLAT result track + // at once, so users are not stuck deleting accumulated results one by one. + if (sameString(group->name, "blat")) + { + safef(idText, sizeof idText, "%s_delAll", group->name); + hPrintf(" ", idText); + jsOnEventByIdF("click", idText, + "if (window.confirm('Delete all %d BLAT result tracks?')) deleteAllBlatTracks();", + slCount(group->trackList)); + } + if (hub || group->errMessage) { safef(idText, sizeof idText, "%s_%d_disconn", hubName, disconCount); disconCount++; hPrintf("\n", idText); jsOnEventByIdF("click", idText, "if (window.confirm(\"Disconnect this hub?\\n\\nReconnecting later will require navigating to My Data -> Track Hubs to find it again or re-entering the hub URL.\")) {" "document.disconnectHubForm.elements['hubId'].value='%s';" "document.disconnectHubForm.submit();return true;" "}", hubName + strlen(hubTrackPrefix)); #ifdef GRAPH_BUTTON_ON_QUICKLIFT