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/lib/customFactory.c src/hg/lib/customFactory.c
index a98d4584811..718f748183a 100644
--- src/hg/lib/customFactory.c
+++ src/hg/lib/customFactory.c
@@ -3776,30 +3776,35 @@
 *retG = atoi(row[1]);
 *retB = atoi(row[2]);
 }
 
 static void stripJavascript(char **pString)
 /* Replace *pString (which is dynamically allocated) with javascript free version of itself. */
 {
 char *tmp = *pString;
 *pString = jsStripJavascript(tmp);
 freeMem(tmp);
 }
 
 static boolean checkGroup(char *db, char *group)
 /* Check if group is valid in db (if mysql, in grp table; if hub, in groups file or default) */
 {
+// "blat" is the synthetic "BLAT Results" group that hgTracks adds in code (it is not a row in the
+// grp table), so accept it here; otherwise a BLAT result track's group=blat would be rejected and
+// forced back into the generic "user" (Custom Tracks) group.
+if (sameString(group, "blat"))
+    return TRUE;
 static struct hash *dbToGroups = NULL;
 if (dbToGroups == NULL)
     dbToGroups = hashNew(0);
 struct hash *groups = hashFindVal(dbToGroups, db);
 if (groups == NULL)
     {
     groups = hashNew(0);
     hashAdd(dbToGroups, db, groups);
     struct grp *groupList = hLoadGrps(db), *grp;
     for (grp = groupList;  grp != NULL;  grp = grp->next)
         hashAddInt(groups, grp->name, TRUE);
     }
 return hashIntValDefault(groups, group, FALSE);
 }