b3ef92d687a2a170eab7758ff6b197e229675eee braney Fri May 8 13:23:17 2026 -0700 add filterPriority and highlightPriority trackDb settings, refs #29223 New filterPriority.<fieldName> setting controls the display order of filter controls on the track configuration page. Lower values display first; filters without an explicit priority sort after those that have one. A companion highlightPriority.<fieldName> setting orders highlight controls the same way. A single priority entry covers all filter styles on the field (filter.*, filterText.*, filterValues.*). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> diff --git src/hg/lib/hui.c src/hg/lib/hui.c index fe7f06d8f3c..9335dccda83 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -4061,32 +4061,34 @@ return filterBy; } filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct trackDbFilter *trackDbFilters, char *name) /* Build a filterBy_t list from tdb variables of the form *FilterValues */ { // Not every tdb has an autoSql: superTracks and tracks pointing at a // bigData file that isn't reachable at UI time both return NULL here. // That's fine for filterValues.* settings as long as a filterLabel.* // override is provided; buildFilterBy() already tolerates a NULL `as`. struct asObject *as = asForTdb(NULL, tdb); filterBy_t *filterByList = NULL, *filter; struct trackDbFilter *fieldFilter; while ((fieldFilter = slPopHead(&trackDbFilters)) != NULL) { + // slAddTail (not slAddHead) keeps the priority-sorted order from tdbGetTrackFilters + // since filterBySetCfgUi displays the list head-to-tail. if ((filter = buildFilterBy(tdb, cart, as, fieldFilter, name)) != NULL) - slAddHead(&filterByList, filter); + slAddTail(&filterByList, filter); } return filterByList; } filterBy_t *filterBySetGetGuts(struct trackDb *tdb, struct cart *cart, char *name, char *subName, char *settingName) // Gets one or more "filterBy" settings (ClosestToHome). returns NULL if not found { // first check to see if this tdb is using "new" FilterValues cart variables if (differentString(subName, "highlightBy")) { struct trackDbFilter *trackDbFilters = tdbGetTrackFilterByFilters( tdb); if (trackDbFilters) return filterByValues(tdb, cart, trackDbFilters, name); } else @@ -6785,73 +6787,114 @@ safef(altLabel, sizeof(altLabel), "%s", (filterByRange?"": "colspan=3")); if (minLimit != NO_VALUE && maxLimit != NO_VALUE) printf("<TD align='left'%s> (%s to %s)",altLabel,shorterDouble(minLimit), shorterDouble(maxLimit)); else if (minLimit != NO_VALUE) printf("<TD align='left'%s> (minimum %s)",altLabel,shorterDouble(minLimit)); else if (maxLimit != NO_VALUE) printf("<TD align='left'%s> (maximum %s)",altLabel,shorterDouble(maxLimit)); else printf("<TD align='left'%s",altLabel); puts("</TR>"); return TRUE; } return FALSE; } +static boolean isHighlightFilterPrefix(char *lowName) +// Distinguish highlight filter passes from filter passes so we look up the right priority var. +{ +return startsWith("highlight", lowName); +} + +static double lookupFilterPriority(struct trackDb *tdb, char *fieldName, boolean isHighlight) +// Find a filterPriority.<field> / <field>FilterPriority setting (or highlight equivalent) in tdb. +// Returns TRACKDB_FILTER_DEFAULT_PRIORITY when no setting is found. +{ +char setting[1024]; +char *priorityLow = isHighlight ? HIGHLIGHT_PRIORITY_NAME_LOW : FILTER_PRIORITY_NAME_LOW; +char *priorityCap = isHighlight ? HIGHLIGHT_PRIORITY_NAME_CAP : FILTER_PRIORITY_NAME_CAP; +safef(setting, sizeof setting, "%s.%s", priorityLow, fieldName); +char *val = trackDbSetting(tdb, setting); +if (val == NULL) + { + safef(setting, sizeof setting, "%s%s", fieldName, priorityCap); + val = trackDbSetting(tdb, setting); + } +if (val == NULL) + return TRACKDB_FILTER_DEFAULT_PRIORITY; +return atof(val); +} + +static int trackDbFilterPriCmp(const void *va, const void *vb) +// Sort trackDbFilters by priority ascending; tiebreak by name for determinism. +{ +const struct trackDbFilter *a = *((struct trackDbFilter **)va); +const struct trackDbFilter *b = *((struct trackDbFilter **)vb); +if (a->priority < b->priority) + return -1; +if (a->priority > b->priority) + return 1; +return strcmp(a->name, b->name); +} + struct trackDbFilter *tdbGetTrackFilters( struct trackDb *tdb, char * lowWild, char * lowName, char * capWild, char * capName) // figure out which of the ways to specify trackDb filter variables we're using // and return the setting { struct trackDbFilter *trackDbFilterList = NULL; +boolean isHighlight = isHighlightFilterPrefix(lowName); struct slName *filterSettings = trackDbSettingsWildMatch(tdb, lowWild); if (filterSettings) { struct trackDbFilter *tdbFilter; struct slName *filter = NULL; while ((filter = slPopHead(&filterSettings)) != NULL) { AllocVar(tdbFilter); slAddHead(&trackDbFilterList, tdbFilter); tdbFilter->name = cloneString(filter->name); tdbFilter->setting = trackDbSetting(tdb, filter->name); tdbFilter->fieldName = extractFieldNameNew(filter->name, lowName); + tdbFilter->priority = lookupFilterPriority(tdb, tdbFilter->fieldName, isHighlight); setAsNewFilterType(tdb, tdbFilter->name, tdbFilter->fieldName); } } filterSettings = trackDbSettingsWildMatch(tdb, capWild); if (filterSettings) { struct trackDbFilter *tdbFilter; struct slName *filter = NULL; while ((filter = slPopHead(&filterSettings)) != NULL) { if (differentString(filter->name,NO_SCORE_FILTER)) { AllocVar(tdbFilter); slAddHead(&trackDbFilterList, tdbFilter); tdbFilter->name = cloneString(filter->name); tdbFilter->setting = trackDbSetting(tdb, filter->name); tdbFilter->fieldName = extractFieldNameOld(filter->name, capName); + tdbFilter->priority = lookupFilterPriority(tdb, tdbFilter->fieldName, isHighlight); char *name; if ((name = isNewFilterType(tdb, tdbFilter->fieldName) ) != NULL) errAbort("error specifying a field's filters in both old (%s) and new format (%s).", tdbFilter->name, name); } } } +slSort(&trackDbFilterList, trackDbFilterPriCmp); return trackDbFilterList; } struct trackDbFilter *tdbGetTrackNumFilters( struct trackDb *tdb) // get the number filters out of trackDb { return tdbGetTrackFilters( tdb, FILTER_NUMBER_WILDCARD_LOW, FILTER_NUMBER_NAME_LOW, FILTER_NUMBER_WILDCARD_CAP, FILTER_NUMBER_NAME_CAP); } struct trackDbFilter *tdbGetTrackTextFilters( struct trackDb *tdb) // get the text filters out of trackDb { return tdbGetTrackFilters( tdb, FILTER_TEXT_WILDCARD_LOW, FILTER_TEXT_NAME_LOW, FILTER_TEXT_WILDCARD_CAP, FILTER_TEXT_NAME_CAP); }