07c318cffa6769b6a4b650a88a7887ae01de608d jcasper Wed Feb 4 20:07:42 2026 -0800 Impose an overall limit on active track count for featured composites, and allow alternate labels for the data type selectors, refs #36320 diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c index bb587ff2872..0634c6a7df1 100644 --- src/hg/hgTrackUi/hgTrackUi.c +++ src/hg/hgTrackUi/hgTrackUi.c @@ -2844,46 +2844,64 @@ char **ancestors; AllocArray(ancestors, count); count = 0; for(sp=speciesList; sp; sp = sp->next) { ancestors[count] = sp->name; count++; } char *coalescent = cartOptionalString(cart, codeVarName); printf("<B>Set Coalescent Ancestor to:</B>"); cgiMakeDropListFull(codeVarName, ancestors, ancestors, count, coalescent, NULL, NULL); } #endif -static struct slName *parseDataTypes(struct trackDb *tdb) +static struct slPair *parseDataTypes(struct trackDb *tdb) { /* Parse the 'dataTypes' trackDb setting into an slName list. * 'dataTypes' is a space separated list of words, each indicating a * data type. Return value is NULL on error. */ char *tdbDataTypes = cloneString(trackDbSetting(tdb, "dataTypes")); if (tdbDataTypes == NULL) return NULL; // A bit awkward to go through chopByWhite into slNameListFromStringArray, but // the slNameList family of functions doesn't have a chopByWhite equivalent. int n_datatypes = chopByWhiteRespectDoubleQuotes(tdbDataTypes, NULL, 0); char **datatypes = calloc(n_datatypes, sizeof(char *)); chopByWhiteRespectDoubleQuotes(tdbDataTypes, datatypes, n_datatypes); -struct slName *list = slNameListFromStringArray(datatypes, n_datatypes); +struct slPair *list = NULL; + +// At this point, each row in datatypes is the string for one data type. +// Either a (possibly quoted) label, or else that followed by | followed +// by (possibly quoted) text to display with the data type checkox. + +for (int i = 0; i < n_datatypes; i++) + { + // If it's just a name, use that for the name and the display title. + // But if it's of the form <name|title>, then split them apart. + char *dtParts[2]; + int partCount = chopByCharRespectDoubleQuotes(datatypes[i], '|', + dtParts, 2); + char *name, *title; + name = title = dtParts[0]; + if (partCount > 1) + title = dtParts[1]; + slPairAdd(&list, name, cloneString(title)); + } freeMem(tdbDataTypes); return list; } unsigned int cartDbParseId(char *, char **); // ADS: avoid extra include #define COMMA_IF(x) (((x)++) ? "," : "") // ADS: pattern for JSON comma static void facetedCompositeUi(struct trackDb *tdb) { /* ADS: How facetedComposite differs from other track types * - compositeTrack track setting is "faceted" * - Required fields in the 'settings' longblob for the trackDb entry: * - 'metaDataUrl': a non-blocked URL (can be server-local) with * metadata to generate the table. This might change to an existing metadata @@ -2914,31 +2932,31 @@ const char placeholderDiv[] = "<div id='metadata-placeholder'></div>\n"; const char openJSON[] = "<script id=\"app-data\" type=\"application/json\">{"; const char closeJSON[] = "}</script>\n"; const char openDataTypesJSON[] = "\"dataTypes\":{"; const char closeDataTypesJSON[] = "}"; // closing a dict const char openDataElementsJSON[] = "\"dataElements\":["; const char closeDataElementsJSON[] = "]"; // closing an array const char metadataTableScriptElement[] = "<script type='text/javascript' src='/js/facetedComposite.js'></script>\n"; // --- Get data from 'settings' field in 'trackDb' entry --- // required const char *metaDataUrl = trackDbSetting(tdb, "metaDataUrl"); const char *primaryKey = trackDbSetting(tdb, "primaryKey"); -struct slName *dataTypes = parseDataTypes(tdb); +struct slPair *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 and hasn't been overridden struct hash *defaultOn = hashNew(0); @@ -2960,59 +2978,60 @@ 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 *selectedDataTypes = NULL; // non-null val will be used as flag if (hasDataTypes) { - for (struct slName *thisType = dataTypes; thisType != NULL; thisType = thisType->next) + for (struct slPair *thisType = dataTypes; thisType != NULL; thisType = thisType->next) { char toMatch[token_size]; boolean selected = FALSE; safef(toMatch, token_size, "%s_*_%s_sel", metaDataId, thisType->name); // 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); + printf("%s\"%s\": {\"active\":%d, \"title\":\"%s\"}", COMMA_IF(not_first), thisType->name, + selected ? 1 : 0, stripEnclosingDoubleQuotes(thisType->val)); } } // 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) { 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) @@ -3087,31 +3106,31 @@ } printf(closeDataElementsJSON); printf(",\"mdid\": \"%s\"", metaDataId); printf(",\"primaryKey\": \"%s\"", primaryKey); // must exist if (maxCheckboxes) // only if present in trackDb.settings entry printf(",\"maxCheckboxes\": \"%s\"", maxCheckboxes); if (colorSettingsUrl) // only if present in trackDb.settings entry printf(",\"colorSettingsUrl\": \"%s\"", colorSettingsUrl); printf(",\"metadataUrl\": \"%s\"", metaDataUrl); printf(closeJSON); /* --- END embedded JSON data --- */ printf(metadataTableScriptElement); // cleanup -slFreeList(&dataTypes); +slPairFreeValsAndList(&dataTypes); hashFree(&defaultOn); } void specificUi(struct trackDb *tdb, struct trackDb *tdbList, struct customTrack *ct, boolean ajax) /* Draw track specific parts of UI. */ { char *track = tdb->track; char *db = database; char *liftDb = cloneString(trackDbSetting(tdb, "quickLiftDb")); if (liftDb != NULL) db = liftDb; // Ideally check cfgTypeFromTdb()/cfgByCfgType() first, but with all these special cases already in // place, lets be cautious at this time. // NOTE: Developer, please try to use cfgTypeFromTdb()/cfgByCfgType().