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);
     }