e506d31b6278bb1251d8b260f75c67fa38c99a27
jcasper
  Thu Jul 23 10:20:01 2026 -0700
Making it possible for multiWig containers to be children of composites, refs #36320

diff --git src/hg/lib/trackDbCustom.c src/hg/lib/trackDbCustom.c
index 77927d3a81d..a365a217bc9 100644
--- src/hg/lib/trackDbCustom.c
+++ src/hg/lib/trackDbCustom.c
@@ -1041,35 +1041,30 @@
 
 /* Do subtrack hierarchy - filling in parent and subtracks fields. */
 for (tdb = superlessList; tdb != NULL; tdb = next)
     {
     next = tdb->next;
     char *subtrackSetting = trackDbLocalSetting(tdb, "parent");
     if (subtrackSetting != NULL
     && !tdbIsSuperTrackChild(tdb)) // superChildren cannot be in both subtracks list AND tdbList
         {
 	char *parentName = cloneFirstWord(subtrackSetting);
 	if (sameString(parentName, tdb->track))
 	    errAbort("Track %s lists itself as its own parent", tdb->track);
 	struct trackDb *parent = hashFindVal(trackHash, parentName);
 	if (parent != NULL)
             {
-        if (trackDbLocalSetting(tdb, "container"))
-            {
-            errAbort("Composite track '%s' cannot have child track '%s',"
-                " which is a container  multiWig.", parentName, tdb->track);
-            }
             slAddHead(&parent->subtracks, tdb); // composite/multiWig children are ONLY subtracks
             tdb->parent = parent;
             }
 	else
 	    {
 	    errAbort("Parent track %s of child %s doesn't exist", parentName, tdb->track);
 	    }
 	freez(&parentName);
 	}
     else
         {
 	slAddHead(&forest, tdb);
 	}
     }
 
@@ -1209,30 +1204,55 @@
 	rGetRefsToDescendantLeaves(pList, tdb->subtracks);
     else
 	refAdd(pList, tdb);
     }
 }
 
 struct slRef *trackDbListGetRefsToDescendantLeaves(struct trackDb *tdbList)
 /* Return reference list all leaves in forest. Do slFreeList when done. */
 {
 struct slRef *refList = NULL;
 rGetRefsToDescendantLeaves(&refList, tdbList);
 slReverse(&refList);
 return refList;
 }
 
+static void rGetRefsToDescendantLeavesOrContainers(struct slRef **pList, struct trackDb *tdbList)
+/* Like rGetRefsToDescendantLeaves, but stop at (and include) container (multiWig) nodes
+ * instead of descending into them.  Still recurses through views and other non-container
+ * intermediate nodes. */
+{
+struct trackDb *tdb;
+for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
+    {
+    if (tdb->subtracks != NULL && trackDbLocalSetting(tdb, "container") == NULL)
+	rGetRefsToDescendantLeavesOrContainers(pList, tdb->subtracks);
+    else
+	refAdd(pList, tdb);   // a leaf, or a container node we stop at
+    }
+}
+
+struct slRef *trackDbListGetRefsToDescendantLeavesOrContainers(struct trackDb *tdbList)
+/* Return reference list of all leaves in forest, plus any container (multiWig) nodes,
+ * not descending into containers. Do slFreeList when done. */
+{
+struct slRef *refList = NULL;
+rGetRefsToDescendantLeavesOrContainers(&refList, tdbList);
+slReverse(&refList);
+return refList;
+}
+
 int trackDbCountDescendantLeaves(struct trackDb *tdb)
 /* Count the number of leaves in children list and their children. */
 {
 struct slRef *leafRefs = trackDbListGetRefsToDescendantLeaves(tdb->subtracks);
 int result = slCount(leafRefs);
 slFreeList(&leafRefs);
 return result;
 }
 
 int trackDbRefCmp(const void *va, const void *vb)
 /* Do trackDbCmp on list of references as opposed to actual trackDbs. */
 {
 const struct slRef *aRef = *((struct slRef **)va);
 const struct slRef *bRef = *((struct slRef **)vb);
 struct trackDb *a = aRef->val, *b = bRef->val;