2521d696f5073ce8cee3f59f423f161fdb77d550 max Fri Sep 25 02:48:37 2026 -0700 VCF tracks: the bigBed trackDb filters (filter.*, filterByRange, filterLimits, filterValues, filterType, filterText, filterLabel) now work on INFO fields, plus ID and QUAL. The field list and types come from the VCF header; the bigBed filter code is reused, bigBed behavior unchanged, refs #37617 diff --git src/hg/lib/hui.c src/hg/lib/hui.c index 54b082a5564..8cacb88d5c3 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -4031,30 +4031,41 @@ safef(labelSetting, sizeof labelSetting, "%s.%s", FILTER_LABEL_NAME_LOW, field); char *trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL); if (trackDbLabel == NULL) { safef(labelSetting, sizeof labelSetting, "%s.%s", field, FILTER_LABEL_NAME_CAP); trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL); } if (trackDbLabel == NULL) { safef(labelSetting, sizeof labelSetting, "%s%s", field, FILTER_LABEL_NAME_CAP); trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL); } return trackDbLabel; } +struct asObject *asForTrackDbFilters(struct cart *cart, struct sqlConnection *conn, struct trackDb *tdb) +/* Return the autoSql object whose column names the trackDb filter settings (filter., + * filterText., filterValues., ...) refer to, or NULL. For VCF tracks these are the + * INFO fields declared in the VCF header (plus ID and QUAL), for all other tracks the track's + * autoSql. */ +{ +if (tdbIsVcf(tdb)) + return vcfInfoAsObj(cart, tdb); +return asForTdb(conn, tdb); +} + 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 FilterValues statement. */ { boolean isHighlight = startsWith("highlightValues.", tdbFilter->name); char *field = tdbFilter->fieldName; if (isEmpty(tdbFilter->setting)) errAbort("track %s: FilterValues setting of field '%s' must have a value.", tdb->track, tdbFilter->fieldName); char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, tdbFilter->name, tdbFilter->setting); filterBy_t *filterBy; AllocVar(filterBy); filterBy->column = cloneString(field); filterBy->title = cloneString(field); /// title should come from AS file, or trackDb variable struct asColumn *asCol = (as != NULL) ? asColumnFind(as, field) : NULL; @@ -4100,31 +4111,31 @@ struct dyString *dy = dyStringNew(128); dyStringPrintf(dy, "%s.%s.%s", name, isHighlight ? "highlightBy": "filterBy", filterBy->column); filterBy->htmlName = dy->string; 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); +struct asObject *as = asForTrackDbFilters(cart, 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) 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 { @@ -7038,31 +7049,31 @@ // 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) { puts("
"); struct trackDbFilter *filter = NULL; struct sqlConnection *conn = NULL; if (!isHubTrack(db) && !isGenArk(db)) conn = hAllocConnTrack(db, tdb); - struct asObject *as = asForTdb(conn, tdb); + struct asObject *as = asForTrackDbFilters(cart, 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) { struct asColumn *asCol = asColumnFind(as, field); if (asCol != NULL) { // Found label so replace field; strip "|..." suffix used for detail page field = asCol->comment; char *pipe = strchr(field, '|'); @@ -7182,31 +7193,31 @@ /* Show all the text filters for this track. */ { int count = 0; struct trackDbFilter *trackDbFilters = NULL; if (isHighlight) trackDbFilters = tdbGetTrackTextHighlights(tdb); else trackDbFilters = tdbGetTrackTextFilters(tdb); if (trackDbFilters) { puts("
"); struct trackDbFilter *filter = NULL; struct sqlConnection *conn = NULL; if (!isHubTrack(db) && !isGenArk(db)) conn = hAllocConnTrack(db, tdb); - struct asObject *as = asForTdb(conn, tdb); + struct asObject *as = asForTrackDbFilters(cart, conn, tdb); hFreeConn(&conn); while ((filter = slPopHead(&trackDbFilters)) != NULL) { char *trackDbLabel = getLabelSetting(cart, tdb, filter->fieldName); char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, filter->setting); if (as != NULL) { struct asColumn *asCol = asColumnFind(as, filter->fieldName); if (asCol != NULL) { if (trackDbLabel == NULL) trackDbLabel = asCol->comment; } else if (defaultFieldLocation(filter->fieldName) < 0) errAbort("Building filter on field %s which is not in AS file.", filter->fieldName); @@ -7232,69 +7243,103 @@ printf(""); printf("  \n", tdb->track); printf("

"); } // using jquery id= syntax to make sure that selector works even if trackname has a dot in it jsInlineF("$('[class=\"buttonClear-%s\"]').click( function(ev) { \n" "$(ev.target).prevAll('input').val('*').trigger('change');\n" "$(ev.target).prevAll('select').val('%s');\n" "ev.preventDefault();\n" "});", tdb->track, FILTERTEXT_WILDCARD); } return count; } +static boolean trackDbFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, + boolean *pIsBoxOpened, boolean boxed, boolean parentLevel, + char *name, char *title) +// Show the numeric (filter.*), text (filterText.*) and multi-select (filterValues.*, filterBy) +// filters of a track, but no highlights. Returns TRUE if any filter was shown. +{ +boolean gotFilter = FALSE; +if (numericFiltersShowAll(db, cart, tdb, pIsBoxOpened, boxed, parentLevel, name, title, FALSE) > 0) + gotFilter = TRUE; + +if (textFiltersShowAll(db, cart, tdb, FALSE)) + gotFilter = TRUE; + +// Add any multi-selects next +filterBy_t *filterBySet = filterBySetGet(tdb,cart,name); +if (filterBySet != NULL) + { + if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL) + jsIncludeFile("hui.js",NULL); + + if (!*pIsBoxOpened) // Note filterBy boxes are not double "boxed", + printf("
"); // if there are no other filters + filterBySetCfgUi(cart,tdb,filterBySet,TRUE, name); + filterBySetFree(&filterBySet); + gotFilter = TRUE; + } +return gotFilter; +} + +boolean trackDbFiltersCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, + boolean boxed) +// Put up only the generic trackDb filter controls of a track (filter.*, filterText.*, +// filterValues.* and their older *Filter forms), without the score filter and highlights. +// For track types with their own configuration UI, e.g. VCF, where the fields are INFO keys. +// Returns TRUE if any filter control was shown. +{ +if (cartOptionalString(cart, "ajax") == NULL) + { + webIncludeResourceFile("ui.dropdownchecklist.css"); + jsIncludeFile("ui.dropdownchecklist.js",NULL); + jsIncludeFile("ddcl.js",NULL); + } +boolean parentLevel = isNameAtParentLevel(tdb,name); +boolean isBoxOpened = FALSE; +boolean gotFilter = trackDbFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, + name, NULL); +if (isBoxOpened) + cfgEndBox(boxed); +return gotFilter; +} + void scoreCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title, int maxScore, boolean boxed) // Put up UI for filtering bed track based on a score { char option[256]; if (cartOptionalString(cart, "ajax") == NULL) { webIncludeResourceFile("ui.dropdownchecklist.css"); jsIncludeFile("ui.dropdownchecklist.js",NULL); jsIncludeFile("ddcl.js",NULL); } boolean parentLevel = isNameAtParentLevel(tdb,name); if (parentLevel) if (trackDbSettingOn(tdb->parent, "noParentConfig")) return; boolean skipScoreFilter = FALSE; -// Numeric filters are first +// Numeric filters are first, then text filters, then the multi-selects boolean isBoxOpened = FALSE; -if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, FALSE) > 0) - skipScoreFilter = TRUE; - -if (textFiltersShowAll(db, cart, tdb, FALSE)) - skipScoreFilter = TRUE; - -// Add any multi-selects next -filterBy_t *filterBySet = filterBySetGet(tdb,cart,name); -if (filterBySet != NULL) - { - if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL) - jsIncludeFile("hui.js",NULL); - - if (!isBoxOpened) // Note filterBy boxes are not double "boxed", - printf("
"); // if there are no other filters - filterBySetCfgUi(cart,tdb,filterBySet,TRUE, name); - filterBySetFree(&filterBySet); +if (trackDbFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title)) skipScoreFilter = TRUE; - } // add any highlights: // Numeric highlights are first if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, TRUE) > 0) { didHighlightSelector = TRUE; skipScoreFilter = TRUE; } if (textFiltersShowAll(db, cart, tdb, TRUE)) { didHighlightSelector = TRUE; skipScoreFilter = TRUE; }