abe6cb020492b82545875d59c570d0a3acc51376 jcasper Wed Feb 4 14:41:49 2026 -0800 Adjusting faceted composite cart logic to support tracks that are on by default, refs #36320 diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c index 16966016e54..bb587ff2872 100644 --- src/hg/hgTrackUi/hgTrackUi.c +++ src/hg/hgTrackUi/hgTrackUi.c @@ -2928,141 +2928,160 @@ struct slName *dataTypes = parseDataTypes(tdb); boolean hasDataTypes = (dataTypes != NULL); // optional const char *colorSettingsUrl = (const char *)hashFindVal(tdb->settingsHash, "colorSettingsUrl"); const char *maxCheckboxes = (const char *)hashFindVal(tdb->settingsHash, "maxCheckboxes"); // --- done parsing values from trackDb.settings --- const char *metaDataId = tdb->track; const int metaDataIdLen = strlen(metaDataId); printf(pageStyle); // css printf(placeholderDiv); // placholder -// start by figuring out what's on by default +// start by figuring out what's on by default and hasn't been overridden struct hash *defaultOn = hashNew(0); for (struct trackDb *st = tdb->subtracks; st != NULL; st = st->next) { char *setting = NULL; char *words[2]; boolean enabled = TRUE; if ((setting = trackDbLocalSetting(st, "parent")) != NULL) { char *clone = NULL; if (chopLine(clone = cloneString(setting), words) >= 2) if (sameString(words[1], "off")) enabled = FALSE; freeMem(clone); } if (enabled) { char val[1024]; safef(val, sizeof(val), "%s_sel", st->track); + if (!cartVarExists(cart, val)) hashAdd(defaultOn, val, NULL); } } /* --- START embedded JSON data --- */ printf(openJSON); printf(openDataTypesJSON); // find selected data types int not_first = 0; -struct slName *anySelDataType = NULL; // non-null val will be used as flag +struct slName *selectedDataTypes = NULL; // non-null val will be used as flag if (hasDataTypes) { for (struct slName *thisType = dataTypes; thisType != NULL; thisType = thisType->next) { char toMatch[token_size]; + boolean selected = FALSE; safef(toMatch, token_size, "%s_*_%s_sel", metaDataId, thisType->name); - boolean dataTypeSel = cartVarExistsLike(cart, toMatch) || hashItemExistsLike(defaultOn, toMatch); - printf("%s\"%s\": %d", COMMA_IF(not_first), thisType->name, dataTypeSel ? 1 : 0); - anySelDataType = dataTypeSel ? thisType : anySelDataType; + // Easy case - check if there's a defaultOn track still active with this track type + if (hashItemExistsLike(defaultOn, toMatch)) + { + slNameAddHead(&selectedDataTypes, thisType->name); + selected = TRUE; + } + else + { + // Now we have to check for any cart variables that turn on a track with this data type + struct slPair *dt_vars = cartVarsLike(cart, toMatch); + struct slPair *this_var = dt_vars; + while (this_var != NULL) + { + if (cartBoolean(cart, this_var->name)) + { + slNameAddHead(&selectedDataTypes, thisType->name); + selected = TRUE; + break; + } + this_var = this_var->next; + } + slPairFreeList(&dt_vars); + } + printf("%s\"%s\": %d", COMMA_IF(not_first), thisType->name, selected ? 1 : 0); } } // else: dataTypes dict is empty - JS will detect this printf(closeDataTypesJSON); printf(","); // add separator + // find selected data sets printf(openDataElementsJSON); not_first = 0; if (hasDataTypes) - { - if (anySelDataType != NULL) { char toMatch[token_size]; - safef(toMatch, token_size, "%s_*_%s_sel", metaDataId, anySelDataType->name); + safef(toMatch, token_size, "%s_*_*_sel", metaDataId); struct slPair *mdidVars = cartVarsLike(cart, toMatch); for (struct slPair *le = mdidVars; le != NULL; le = le->next) { if (cartBoolean(cart, le->name)) { const char *nameStart = le->name + metaDataIdLen + 1; const char *nameEnd = strchr(nameStart, '_'); if (nameEnd && nameEnd > nameStart) { const int nameLen = nameEnd - nameStart; printf("%s\"%.*s\"", COMMA_IF(not_first), nameLen, nameStart); } } } slPairFreeList(&mdidVars); // Now add data elements on by default that haven't been modified struct hashEl *el, *elList = hashElListHash(defaultOn); - slSort(&elList, hashElCmp); for (el = elList; el != NULL; el = el->next) { if (!cartVarExistsLike(cart, el->name)) { const char *nameStart = el->name + metaDataIdLen + 1; const char *nameEnd = strchr(nameStart, '_'); if (nameEnd && nameEnd > nameStart) { const int nameLen = nameEnd - nameStart; printf("%s\"%.*s\"", COMMA_IF(not_first), nameLen, nameStart); } } } hashElFreeList(&elList); } - } else { // No data types - look for {mdid}_{de}_sel pattern (no dataType component) char toMatch[token_size]; safef(toMatch, token_size, "%s_*_sel", metaDataId); struct slPair *mdidVars = cartVarsLike(cart, toMatch); for (struct slPair *le = mdidVars; le != NULL; le = le->next) { if (cartBoolean(cart, le->name)) { // Extract data element name: between mdid_ and _sel const char *nameStart = le->name + metaDataIdLen + 1; const char *nameEnd = strstr(nameStart, "_sel"); if (nameEnd && nameEnd > nameStart) { const int nameLen = nameEnd - nameStart; printf("%s\"%.*s\"", COMMA_IF(not_first), nameLen, nameStart); } } } slPairFreeList(&mdidVars); // Now add data elements on by default that haven't been modified struct hashEl *el, *elList = hashElListHash(defaultOn); - slSort(&elList, hashElCmp); for (el = elList; el != NULL; el = el->next) { if (!cartVarExistsLike(cart, el->name)) { const char *nameStart = el->name + metaDataIdLen + 1; const char *nameEnd = strstr(nameStart, "_sel"); if (nameEnd && nameEnd > nameStart) { const int nameLen = nameEnd - nameStart; printf("%s\"%.*s\"", COMMA_IF(not_first), nameLen, nameStart); } } } hashElFreeList(&elList); }