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("Set Coalescent Ancestor to:");
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 , 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[] = "\n";
const char openJSON[] = "\n";
const char openDataTypesJSON[] = "\"dataTypes\":{";
const char closeDataTypesJSON[] = "}"; // closing a dict
const char openDataElementsJSON[] = "\"dataElements\":[";
const char closeDataElementsJSON[] = "]"; // closing an array
const char metadataTableScriptElement[] =
"\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().