66ea6cb4eaf2036e464be55b295765ed5105a0fb max Wed Apr 22 09:42:25 2026 -0700 hgTrackUi/hui: render filter UI on supertrack configuration pages Supertracks (group of tracks with superTrack on, no data of their own) previously had no way to expose a shared filter: their trackDb stanza can declare filter.* / filterByRange.* / filterValues.*, but those settings were never drawn on the supertrack's hgTrackUi page. So users had to open each subtrack's own configuration page and set the same filter there, and the "lrSv.filter.svLen" cart namespace went unused. This change wires that up: - hgTrackUi.c (superTrackUi): after listing the subtracks, if the supertrack tdb declares any filter.* settings call scoreCfgUi() to render the standard filter UI. Cart variables land under the supertrack's own name (e.g. "lrSv.filter.svLen.min"), and subtracks already inherit them through cartOptionalStringClosestToHome() walking tdb->parent. Subtrack-level values continue to override. - hui.c: - buildFilterBy() / filterByValues(): tolerate a NULL autoSql object, so supertracks (which have no data table) don't errAbort when they declare filterValues.* of virtual aggregated fields. Missing-field errAbort still fires in the normal subtrack case. - scoreCfgUi() / cfgByCfgType(): when called with title == NULL (the supertrack filter path), suppress the default "
" separator and
the " %s ", title );
-else
- printf(" ");
+// When !boxed and title==NULL, emit nothing: the caller (e.g. supertrack
+// filter UI) already renders its own heading and doesn't want a stray
+// empty paragraph.
return boxed;
}
void cfgEndBox(boolean boxed)
// Handle end of box and title for individual track type settings
{
if (boxed)
puts("
" between the title bar and the filter block; the caller
renders its own section heading.
- asForTdb(): handle conn == NULL by returning NULL rather than
crashing, since supertrack filter rendering has no associated
sqlConnection.
refs #37426
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index cac6a74ed83..9ce7959c8b6 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -4001,34 +4001,37 @@
static filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, struct trackDbFilter *tdbFilter, char *name)
/* Build a filterBy_t structure from a
");
}
if (boxed)
{
printf("
");
}
void snakeOption(struct cart *cart, char *name, char *title, struct trackDb *tdb)
/* let the user choose to see the track in snake mode */
{
if (!cfgOptionBooleanDefault("canSnake", TRUE))
return;
@@ -6905,30 +6911,34 @@
}
static int numericFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean *opened,
boolean boxed, boolean parentLevel,char *name, char *title,
boolean isHighlight)
// Shows all *Filter style filters. Note that these are in random order and have no graceful title
{
int count = 0;
struct trackDbFilter *trackDbFilters = NULL;
if (isHighlight)
trackDbFilters = tdbGetTrackNumHighlights(tdb);
else
trackDbFilters = tdbGetTrackNumFilters(tdb);
if (trackDbFilters)
{
+ // The ", COLOR_BG_ALTDEFAULT);
if (title)
printf("
is a separator under the track's "Configuration" block title.
+ // Callers that don't emit a title (e.g. the supertrack filter UI that
+ // owns its own heading) pass title==NULL and don't want the extra break.
+ if (title != NULL)
puts("
");
struct trackDbFilter *filter = NULL;
struct sqlConnection *conn = NULL;
if (!isHubTrack(db))
conn = hAllocConnTrack(db, tdb);
struct asObject *as = asForTdb(conn, tdb);
hFreeConn(&conn);
while ((filter = slPopHead(&trackDbFilters)) != NULL)
{
char *field = filter->fieldName;
char *scoreName = cloneString(filter->name);
char *trackDbLabel = getLabelSetting(cart, tdb, field);
if (as != NULL)
@@ -10301,30 +10311,34 @@
char *db = sqlGetDatabase(conn);
int exists = hashIntValDefault(hash, db, -1);
if (exists < 0)
{
exists = sqlTableExists(conn, "tableDescriptions");
hashAddInt(hash, db, exists);
}
return (boolean)exists;
}
struct asObject *asFromTableDescriptions(struct sqlConnection *conn, char *table)
// If there is a tableDescriptions table and it has an entry for table, return
// a parsed autoSql object; otherwise return NULL.
{
struct asObject *asObj = NULL;
+// Callers occasionally invoke asForTdb with conn=NULL (e.g. superTrack filter
+// rendering that isn't tied to a data table). Nothing to look up in that case.
+if (conn == NULL)
+ return NULL;
if (tableDescriptionsExists(conn))
{
char query[PATH_LEN*2];
// Try unsplit table first.
sqlSafef(query, sizeof(query),
"select autoSqlDef from tableDescriptions where tableName='%s'", table);
char *asText = sqlQuickString(conn, query);
// If no result try split table.
if (asText == NULL)
{
sqlSafef(query, sizeof(query),
"select autoSqlDef from tableDescriptions where tableName='chrN_%s'", table);
asText = sqlQuickString(conn, query);
}
if (isNotEmpty(asText))