Index: src/hg/lib/hui.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/lib/hui.c,v
retrieving revision 1.252
retrieving revision 1.253
diff -b -B -U 4 -r1.252 -r1.253
--- src/hg/lib/hui.c	13 Dec 2009 20:20:46 -0000	1.252
+++ src/hg/lib/hui.c	4 Jan 2010 19:12:29 -0000	1.253
@@ -79,14 +79,10 @@
 	    tableName = "all_mrna";
     char *hint = " title='Open table schema in new window'";
     if( label == NULL)
         label = " View table schema";
-
-    if(tdbIsCompositeChild(tdb))
-        printf(SCHEMA_LINKED, db, tdb->parent->grp, tdb->parent->tableName,tableName,hint,label);
-    else
-        printf(SCHEMA_LINKED, db, tdb->grp, tdb->tableName,tableName,hint,label);
-
+    struct trackDb *topLevel = trackDbTopLevelSelfOrParent(tdb);
+    printf(SCHEMA_LINKED, db, topLevel->grp, topLevel->tableName,tableName,hint,label);
     return TRUE;
     }
 return FALSE;
 }
@@ -108,9 +104,9 @@
         {
         if(sameString(metadata->tags[ix],"fileName"))
             {
             printf("<tr onmouseover=\"this.style.cursor='text';\"><td align=right><i>%s:</i></td><td nowrap>",metadata->tags[ix]);
-            makeNamedDownloadsLink(tdb->parent != NULL? tdb->parent :tdb ,metadata->values[ix]);
+            makeNamedDownloadsLink(trackDbTopLevelSelfOrParent(tdb), metadata->values[ix]);
             printf("</td></tr>");
             }
         else
             if(!sameString(metadata->tags[ix],"subId")
@@ -147,9 +143,9 @@
         printf(", ");
     }
 if(downloadLink)
     {
-    struct trackDb *trueTdb = tdbIsCompositeChild(tdb)? tdb->parent: tdb;
+    struct trackDb *trueTdb = trackDbTopLevelSelfOrParent(tdb);
     makeNamedDownloadsLink(trueTdb,(moreThanOne ? "downloads":"Downloads"));
     if(metadataLink)
         printf(",");
     }
@@ -1728,48 +1724,39 @@
 slFreeList(&trackList);
 return trackName;
 }
 
-struct hash *makeTrackHashWithComposites(char *database, char *chrom,
-                                        bool withComposites)
-/* Make hash of trackDb items for this chromosome, including composites,
-   not just the subtracks. */
+static void rAddTrackListToHash(struct hash *trackHash, struct trackDb *tdbList, char *chrom,
+	boolean leafOnly)
+/* Recursively add trackList to trackHash */
 {
-struct trackDb *tdbs = hTrackDb(database, chrom);
-struct hash *trackHash = newHash(7);
-
-while (tdbs != NULL)
+struct trackDb *tdb;
+for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
     {
-    struct trackDb *tdb = slPopHead(&tdbs);
     if (hTrackOnChrom(tdb, chrom))
         {
-        if (tdb->subtracks)
-            {
-            struct trackDb *subtrack;
-            for (subtrack = tdb->subtracks; subtrack != NULL;
-                        subtrack = subtrack->next)
-                {
-                /* need track description for hgc */
-                subtrack->html = cloneString(tdb->html);
-                hashAdd(trackHash, subtrack->tableName, subtrack);
-                }
-            if (withComposites)
-                hashAdd(trackHash, tdb->tableName, tdb);
-            }
-        else
+	if (tdb->subtracks == NULL || !leafOnly)
             hashAdd(trackHash, tdb->tableName, tdb);
         }
-    else
-        trackDbFree(&tdb);
+    rAddTrackListToHash(trackHash, tdb->subtracks, chrom, leafOnly);
     }
+}
 
+struct hash *makeTrackHashWithComposites(char *database, char *chrom,
+                                        bool withComposites)
+/* Make hash of trackDb items for this chromosome, including composites,
+   not just the subtracks. */
+{
+struct trackDb *tdbs = hTrackDb(database, chrom);
+struct hash *trackHash = newHash(7);
+rAddTrackListToHash(trackHash, tdbs, chrom, !withComposites);
 return trackHash;
 }
 
 struct hash *makeTrackHash(char *database, char *chrom)
 /* Make hash of trackDb items for this chromosome. */
 {
-    return makeTrackHashWithComposites(database, chrom, FALSE);
+return makeTrackHashWithComposites(database, chrom, FALSE);
 }
 
 /****** Stuff for acembly related options *******/
 
@@ -1800,9 +1787,9 @@
 cgiMakeDropList(var, acemblyOptions, ArraySize(acemblyOptions),
 	curVal);
 }
 
-boolean parseAssignment(char *words, char **name, char **value)
+static boolean parseAssignment(char *words, char **name, char **value)
 /* parse <name>=<value>, destroying input words in the process */
 {
 char *p;
 if ((p = index(words, '=')) == NULL)
@@ -1817,19 +1804,23 @@
 
 static char *getPrimaryType(char *primarySubtrack, struct trackDb *tdb)
 /* Do not free when done. */
 {
-struct trackDb *subtrack = NULL;
 char *type = NULL;
 if (primarySubtrack)
-    for (subtrack = tdb->subtracks; subtrack != NULL; subtrack = subtrack->next)
 	{
+    struct slRef *tdbRef, *tdbRefList = trackDbListGetRefsToDescendants(tdb->subtracks);
+    for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
+	{
+	struct trackDb *subtrack = tdbRef->val;
 	if (sameString(subtrack->tableName, primarySubtrack))
 	    {
 	    type = subtrack->type;
 	    break;
 	    }
 	}
+    slFreeList(&tdbRefList);
+    }
 return type;
 }
 
 boolean hSameTrackDbType(char *type1, char *type2)
@@ -2040,27 +2032,31 @@
 
 char * subgroupSettingByTagOrName(struct trackDb *parentTdb, char *groupNameOrTag)
 /* look for a subGroup by name (ie subGroup1) or tag (ie view) and return an unallocated char* */
 {
-int ix;
-char *setting = NULL;
-if(startsWith("subGroup",groupNameOrTag))
+struct trackDb *ancestor;
+for (ancestor = parentTdb; ancestor != NULL; ancestor = ancestor->parent)
+    {
+    int ix;
+    char *setting = NULL;
+    if(startsWith("subGroup",groupNameOrTag))
     {
-    setting = trackDbSetting(parentTdb, groupNameOrTag);
+	setting = trackDbSetting(ancestor, groupNameOrTag);
     if(setting != NULL)
         return setting;
     }
-for(ix=1;ix<=SUBGROUP_MAX;ix++) // How many do we support?
+    for(ix=1;ix<=SUBGROUP_MAX;ix++) // How many do we support?
     {
     char subGrp[16];
     safef(subGrp, ArraySize(subGrp), "subGroup%d",ix);
-    setting = trackDbSetting(parentTdb, subGrp);
+	setting = trackDbSetting(ancestor, subGrp);
     if(setting != NULL)  // Doesn't require consecutive subgroups
         {
         if(startsWithWord(groupNameOrTag,setting))
             return setting;
         }
     }
+    }
 return NULL;
 }
 
 boolean subgroupingExists(struct trackDb *parentTdb, char *groupNameOrTag)
@@ -2911,26 +2910,58 @@
     }
 return newDate;  // newDate is never freed!
 }
 
+boolean tdbAddPrioritiesFromCart(struct cart *cart, struct trackDb *tdbList)
+/* Updates the tdb->priority from cart for all tracks in list and their
+ * descendents. */
+{
+char htmlIdentifier[128];
+struct trackDb *tdb;
+boolean cartPriorities = FALSE;
+for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
+    {
+    safef(htmlIdentifier, sizeof(htmlIdentifier), "%s.priority", tdb->tableName);
+    char *cartHas = cartOptionalString(cart,htmlIdentifier);
+    if(cartHas != NULL)
+	{
+	tdb->priority = atof(cartHas);
+	cartPriorities = TRUE;
+	}
+    if (tdbAddPrioritiesFromCart(cart, tdb->subtracks))
+        cartPriorities = TRUE;
+    }
+return cartPriorities;
+}
+
 boolean tdbSortPrioritiesFromCart(struct cart *cart, struct trackDb **tdbList)
 /* Updates the tdb->priority from cart then sorts the list anew.
    Returns TRUE if priorities obtained from cart */
 {
+boolean cartPriorities = tdbAddPrioritiesFromCart(cart, *tdbList);
+slSort(tdbList, trackDbCmp);
+return cartPriorities;
+}
+
+boolean tdbRefSortPrioritiesFromCart(struct cart *cart, struct slRef **tdbRefList)
+/* Updates the tdb->priority from cart then sorts the list anew.
+   Returns TRUE if priorities obtained from cart */
+{
 char htmlIdentifier[128];
-struct trackDb *tdb;
+struct slRef *tdbRef;
 boolean cartPriorities = FALSE;
-for (tdb = *tdbList; tdb != NULL; tdb = tdb->next)
+for (tdbRef = *tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
     {
+    struct trackDb *tdb = tdbRef->val;
         safef(htmlIdentifier, sizeof(htmlIdentifier), "%s.priority", tdb->tableName);
         char *cartHas = cartOptionalString(cart,htmlIdentifier);
         if(cartHas != NULL)
             {
             tdb->priority = atof(cartHas);
             cartPriorities = TRUE;
             }
     }
-    slSort(tdbList, trackDbCmp);
+slSort(tdbRefList, trackDbRefCmp);
 return cartPriorities;
 }
 
 static void cfgByCfgType(eCfgType cType,char *db, struct cart *cart, struct trackDb *tdb,char *prefix, char *title, boolean boxed)
@@ -3010,9 +3041,10 @@
 }
 
 static void compositeUiSubtracks(char *db, struct cart *cart, struct trackDb *parentTdb,
                  boolean selectedOnly, char *primarySubtrack)
-/* Show checkboxes for subtracks. */
+/* Display list of subtracks and descriptions with checkboxes to control visibility and possibly other
+ * nice things including links to schema and metadata and a release date. */
 {
 struct trackDb *subtrack;
 char *primaryType = getPrimaryType(primarySubtrack, parentTdb);
 char htmlIdentifier[SMALLBUF];
@@ -3022,8 +3054,11 @@
                       COLOR_BG_ALTDEFAULT };
 int colorIx = COLOR_BG_DEFAULT_IX; // Start with non-default allows alternation
 boolean dependentCfgsNeedBinding = FALSE;
 
+// Get list of leaf subtracks to work with
+struct slRef *subtrackRef, *subtrackRefList = trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
+
 // Look for dividers, heirarchy, dimensions, sort and dragAndDrop!
 char **lastDivide = NULL;
 dividers_t *dividers = dividersSettingGet(parentTdb);
 if(dividers)
@@ -3092,10 +3127,11 @@
     {
     char javascript[JBUFSIZE];
     boolean displayAll = sameString(cartUsualString(cart, "displaySubtracks", "all"), "all");
     boolean restrictions = FALSE;
-    for (subtrack = parentTdb->subtracks; subtrack != NULL; subtrack = subtrack->next)
+    for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
         {
+	subtrack = subtrackRef->val;
         char *date = metadataSettingFind(subtrack,"dateUnrestricted");
         if(date != NULL)
             {
             freeMem(date);
@@ -3124,9 +3160,12 @@
         int sIx=0;
         for(sIx=0;sIx<sortOrder->count;sIx++)
             {
             printf ("<TH id='%s.%s.sortTh' abbr='%c' nowrap><A HREF='#nowhere' onclick=\"tableSortAtButtonPress(this,'%s');return false;\">%s</A><sup>%s",
-                parentTdb->tableName,sortOrder->column[sIx],(sortOrder->forward[sIx]?'-':'+'),sortOrder->column[sIx],sortOrder->title[sIx],(sortOrder->forward[sIx]?"&darr;":"&uarr;"));
+                parentTdb->tableName,sortOrder->column[sIx],
+		(sortOrder->forward[sIx]?'-':'+'),
+		sortOrder->column[sIx],sortOrder->title[sIx],
+		(sortOrder->forward[sIx]?"&darr;":"&uarr;"));
             if (sortOrder->count > 1)
                 printf ("%d",sortOrder->order[sIx]);
             puts ("</sup></TH>");
             }
@@ -3144,27 +3183,29 @@
     }
 
 if(sortOrder != NULL || useDragAndDrop)
     {
-    preSorted = tdbSortPrioritiesFromCart(cart, &(parentTdb->subtracks)); // preserves user's prev sort/drags
+    preSorted = tdbRefSortPrioritiesFromCart(cart, &subtrackRefList); // preserves user's prev sort/drags
     puts("</TR></THEAD><TBODY id='sortable_tbody'>");
     }
 else
     {
-    slSort(&(parentTdb->subtracks), trackDbCmp);  // straight from trackDb.ra
+    slSort(&subtrackRefList, trackDbRefCmp);  // straight from trackDb.ra
     preSorted = TRUE;
     puts("</TR></THEAD><TBODY>");
     }
 
-for (subtrack = parentTdb->subtracks; subtrack != NULL; subtrack = subtrack->next)
+// cgiMakeOnClickButton("matChkBoxesNormalizeAll(); return 1;", "jkTest");
+
+for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
     {
+    subtrack = subtrackRef->val;
     boolean checkedCB = TRUE;
     boolean enabledCB = TRUE;
     boolean isPrimary = FALSE;
     char *setting;
     int ix;
-
-    if ((setting = trackDbSetting(subtrack, "subTrack")) != NULL)
+    if ((setting = trackDbLocalSetting(subtrack, "subTrack")) != NULL)
         {
         if (chopLine(cloneString(setting), words) >= 2)
             checkedCB = differentString(words[1], "off");
         }
@@ -3189,9 +3230,8 @@
             printf ("</TD><TD>%s [selected on main page]</TD></TR>\n",
                 subtrack->longLabel);
             }
         else if (hSameTrackDbType(primaryType, subtrack->type))
-            // && hTableExists(db, subtrack->tableName))  REMOVED because using parentTdbAbandonTablelessChildren before we are here
             {
             puts("<TR><TD>");
             cgiMakeCheckBox(htmlIdentifier, checkedCB && enabledCB);
             printf ("</TD><TD>%s</TD></TR>\n", subtrack->longLabel);
@@ -3202,9 +3242,8 @@
         eCfgType cType = cfgTypeFromTdb(subtrack,FALSE);
         if(trackDbSettingClosestToHomeOn(subtrack, "configurable") == FALSE)
             cType = cfgNone;
         membership_t *membership = subgroupMembershipGet(subtrack);
-        //if (hTableExists(db, subtrack->tableName))  REMOVED because using parentTdbAbandonTablelessChildren before we are here
             {
             if(sortOrder == NULL && !useDragAndDrop)
                 {
                 if( divisionIfNeeded(lastDivide,dividers,membership) )
@@ -3276,20 +3315,13 @@
 
             if(cType != cfgNone)
                 {
                 dependentCfgsNeedBinding = TRUE; // configurable subtrack needs to be bound to composite settings
-                if(membership)
-                    ix = stringArrayIx("view", membership->subgroups, membership->count);
-                else
-                    ix = -1;
 #define CFG_SUBTRACK_DIV "<DIV id='div_%s_cfg'%s><INPUT TYPE=HIDDEN NAME='%s' value='%s'>\n"
 #define MAKE_CFG_SUBTRACK_DIV(table,cfgVar,open) printf(CFG_SUBTRACK_DIV,(table),((open)?"":" style='display:none'"),(cfgVar),((open)?"on":"off"))
                 safef(htmlIdentifier,sizeof(htmlIdentifier),"%s.childShowCfg",subtrack->tableName);
                 boolean open = cartUsualBoolean(cart, htmlIdentifier,FALSE);
                 MAKE_CFG_SUBTRACK_DIV(subtrack->tableName,htmlIdentifier,open);
-                if(ix >= 0)
-                    safef(htmlIdentifier,sizeof(htmlIdentifier),"%s.%s",subtrack->tableName,membership->membership[ix]);
-                else
                     safef(htmlIdentifier,sizeof(htmlIdentifier),"%s",subtrack->tableName);
                 cfgByCfgType(cType,db,cart,subtrack,htmlIdentifier,"Subtrack",TRUE);
                 puts("</DIV>\n");
                 }
@@ -3308,13 +3340,11 @@
         }
     }
 puts("</TBODY><TFOOT></TFOOT>");
 puts("</TABLE>");
-if(slCount(parentTdb->subtracks) > 5)
+if(slCount(subtrackRefList) > 5)
     puts("&nbsp;&nbsp;&nbsp;&nbsp;<FONT id='subCBcount'></font>");
 puts("<P>");
-//if (!preSorted && sortOrder != NULL)  // No longer need to do this since hgTrackDb should sort composites with sortOrder and set priorities
-//    puts("<script type='text/javascript'>tableSortAtStartup();</script>");
 if (!primarySubtrack)
     puts("<script type='text/javascript'>matInitializeMatrix();</script>");
 if(dependentCfgsNeedBinding)
     cfgLinkToDependentCfgs(parentTdb,parentTdb->tableName);
@@ -3385,9 +3415,9 @@
 if (boxed)
     puts("</td></tr></table>");
 }
 
-void wigCfgUi(struct cart *cart, struct trackDb *tdb,char *name,char *title,boolean boxed)
+void wigCfgUi(struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed)
 /* UI for the wiggle track */
 {
 char *typeLine = NULL;  /*  to parse the trackDb type line  */
 char *words[8];     /*  to parse the trackDb type line  */
@@ -3415,19 +3445,19 @@
 wigFetchMinMaxPixelsWithCart(cart,tdb,name,&minHeightPixels, &maxHeightPixels, &defaultHeight);
 typeLine = cloneString(tdb->type);
 wordCount = chopLine(typeLine,words);
 
-wigFetchMinMaxYWithCart(cart,tdb,name, &minY, &maxY, &tDbMinY, &tDbMaxY, wordCount, words);
+wigFetchMinMaxYWithCart(cart, tdb, name, &minY, &maxY, &tDbMinY, &tDbMaxY, wordCount, words);
 freeMem(typeLine);
 
-(void) wigFetchTransformFuncWithCart(cart,tdb,name, &transformFunc);
-(void) wigFetchAlwaysZeroWithCart(cart,tdb,name, &alwaysZero);
-(void) wigFetchHorizontalGridWithCart(cart,tdb,name, &horizontalGrid);
-(void) wigFetchAutoScaleWithCart(cart,tdb,name, &autoScale);
-(void) wigFetchGraphTypeWithCart(cart,tdb,name, &lineBar);
-(void) wigFetchWindowingFunctionWithCart(cart,tdb,name, &windowingFunction);
-(void) wigFetchSmoothingWindowWithCart(cart,tdb,name, &smoothingWindow);
-(void) wigFetchYLineMarkWithCart(cart,tdb,name, &yLineMarkOnOff);
+wigFetchTransformFuncWithCart(cart,tdb,name, &transformFunc);
+wigFetchAlwaysZeroWithCart(cart,tdb,name, &alwaysZero);
+wigFetchHorizontalGridWithCart(cart,tdb,name, &horizontalGrid);
+wigFetchAutoScaleWithCart(cart,tdb,name, &autoScale);
+wigFetchGraphTypeWithCart(cart,tdb,name, &lineBar);
+wigFetchWindowingFunctionWithCart(cart,tdb,name, &windowingFunction);
+wigFetchSmoothingWindowWithCart(cart,tdb,name, &smoothingWindow);
+wigFetchYLineMarkWithCart(cart,tdb,name, &yLineMarkOnOff);
 wigFetchYLineMarkValueWithCart(cart,tdb,name, &yLineMark);
 
 printf("<TABLE BORDER=0>");
 
@@ -3449,12 +3479,12 @@
 puts("</TD></TR>");
 
 printf("<TR valign=center><th align=right>Vertical viewing range:</th><td align=left>&nbsp;min:&nbsp;");
 snprintf(option, sizeof(option), "%s.%s", name, MIN_Y );
-cgiMakeDoubleVarWithLimits(option, minY, "Range min", 0, NO_VALUE, NO_VALUE);//tDbMinY, tDbMaxY);  // Jim requests this does not enforce limits
+cgiMakeDoubleVarWithLimits(option, minY, "Range min", 0, NO_VALUE, NO_VALUE);  
 printf("</td><td align=leftv colspan=2>max:&nbsp;");
 snprintf(option, sizeof(option), "%s.%s", name, MAX_Y );
-cgiMakeDoubleVarWithLimits(option, maxY, "Range max", 0, NO_VALUE, NO_VALUE);//tDbMinY, tDbMaxY);
+cgiMakeDoubleVarWithLimits(option, maxY, "Range max", 0, NO_VALUE, NO_VALUE);
 printf("&nbsp;(range: %g to %g)",
     tDbMinY, tDbMaxY);
 puts("</TD></TR>");
 
@@ -4287,11 +4317,11 @@
 
 char **wigMafGetSpecies(struct cart *cart, struct trackDb *tdb, char *db, struct wigMafSpecies **list, int *groupCt)
 {
 int speciesCt = 0;
-char *speciesGroup = trackDbSetting(tdb, SPECIES_GROUP_VAR);
-char *speciesUseFile = trackDbSetting(tdb, SPECIES_USE_FILE);
-char *speciesOrder = trackDbSetting(tdb, SPECIES_ORDER_VAR);
+char *speciesGroup = trackDbSettingClosestToHome(tdb, SPECIES_GROUP_VAR);
+char *speciesUseFile = trackDbSettingClosestToHome(tdb, SPECIES_USE_FILE);
+char *speciesOrder = trackDbSettingClosestToHome(tdb, SPECIES_ORDER_VAR);
 char sGroup[24];
 //Ochar *groups[20];
 struct wigMafSpecies *wmSpecies, *wmSpeciesList = NULL;
 int group;
@@ -4701,59 +4731,76 @@
     }
 cfgEndBox(boxed);
 }
 
-static boolean compositeViewCfgExpandedByDefault(struct trackDb *parentTdb,char *view,char **visibility)
-/* returns true if the view cfg is expanded by default.  Optionally allocates string of view setting (eg 'dense') */
+struct trackDb *rFindViewInList(struct trackDb *tdbList, char *view)
+/* Return the trackDb on the list (or on any children of the list) that has matching view tag. */
 {
-int cnt,ix;
-boolean expanded = FALSE;
-if ( visibility != NULL )
-    *visibility = cloneString(hStringFromTv(parentTdb->visibility));
-char *setting = trackDbSetting(parentTdb, "visibilityViewDefaults");
-if(setting == NULL)
-    return FALSE;
-
-char *target = cloneString(setting);
-char *words[SMALLBUF];
-cnt = chopLine(target, words);
-for(ix=0;ix<cnt;ix++)
-    {
-    if(startsWith(view,words[ix]) && words[ix][strlen(view)] == '=')
-        {
-        if (words[ix][strlen(words[ix]) - 1] == '+')
+struct trackDb *tdb;
+for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
             {
-            expanded = TRUE;
-            if ( visibility != NULL )
-                words[ix][strlen(words[ix]) - 1] = 0;
-            }
-        if ( visibility != NULL )
-            *visibility = cloneString(words[ix] + strlen(view) + 1);
-        break;
+    char *viewSetting = trackDbSetting(tdb, "view");
+    if (sameOk(viewSetting, view))
+        return tdb;
         }
+for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
+    {
+    struct trackDb *viewTdb = rFindViewInList(tdb->subtracks, view);
+    if (viewTdb != NULL)
+        return viewTdb;
     }
-// At this point we need to search the cart to see if any others are already expanded.
-// cart var of style "wgEncodeYaleChIPseq.Peaks.showCfg" {parentTable}.{view}.showCfg value='on'
-freeMem(target);
+return NULL;
+}
+
+static boolean compositeViewCfgExpandedByDefault(struct trackDb *parentTdb,char *view,
+	char **retVisibility)
+/* returns true if the view cfg is expanded by default.  Optionally allocates string of view 
+ * setting (eg 'dense') */
+{
+boolean expanded = FALSE;
+if ( retVisibility != NULL )
+    *retVisibility = cloneString(hStringFromTv(parentTdb->visibility));
+struct trackDb *viewTdb = rFindViewInList(parentTdb->subtracks, view);
+if (viewTdb == NULL)
+    return FALSE;
+if (retVisibility != NULL)
+    *retVisibility = cloneString(hStringFromTv(viewTdb->visibility));
+if (trackDbSetting(viewTdb, "viewUi"))
+    expanded = TRUE;
 return expanded;
 }
 
 enum trackVisibility visCompositeViewDefault(struct trackDb *parentTdb,char *view)
 /* returns the default track visibility of particular view within a composite track */
 {
 char *visibility = NULL;
-(void)compositeViewCfgExpandedByDefault(parentTdb,view,&visibility);
+compositeViewCfgExpandedByDefault(parentTdb,view,&visibility);
 enum trackVisibility vis = hTvFromString(visibility);
 freeMem(visibility);
 return vis;
 }
 
+struct trackDb *rFindView(struct trackDb *forest, char *viewName)
+/* Find a descendent with given view. */
+{
+struct trackDb *tdb;
+for (tdb = forest; tdb != NULL; tdb = tdb->next)
+    {
+    char *viewSetting = trackDbSetting(tdb, "view");
+    if (sameOk(viewSetting, viewName))
+        return tdb;
+    struct trackDb *view = rFindView(tdb->subtracks, viewName);
+    if (view)
+        return view;
+    }
+return NULL;
+}
+
 static boolean hCompositeDisplayViewDropDowns(char *db, struct cart *cart, struct trackDb *parentTdb)
 /* UI for composite view drop down selections. */
 {
 int ix;
-struct trackDb *subtrack;
-char objName[SMALLBUF];
+char varName[SMALLBUF];
 char classes[SMALLBUF];
 char javascript[JBUFSIZE];
 #define CFG_LINK  "<B><A NAME=\"a_cfg_%s\"></A><A id='a_cfg_%s' HREF=\"#a_cfg_%s\" onclick=\"return (showConfigControls('%s') == false);\" title=\"%s Configuration\">%s</A><INPUT TYPE=HIDDEN NAME='%s.%s.showCfg' value='%s'></B>\n"
 #define MAKE_CFG_LINK(name,title,tbl,open) printf(CFG_LINK, (name),(name),(name),(name),(title),(title),(tbl),(name),((open)?"on":"off"))
@@ -4766,45 +4813,38 @@
 memset(configurable,cfgNone,sizeof(configurable));
 int firstOpened = -1;
 boolean makeCfgRows = FALSE;
 struct trackDb **matchedSubtracks = needMem(sizeof(struct trackDb *)*membersOfView->count);
-char *setting = trackDbSetting(parentTdb,"settingsByView");
-boolean blockCfgs = (setting != NULL && (sameWord(setting,"off") || sameWord(setting,"disabled") || sameWord(setting,"false")));
 
 for (ix = 0; ix < membersOfView->count; ix++)
     {
-    for (subtrack = parentTdb->subtracks; subtrack != NULL; subtrack = subtrack->next)
+    char *viewName = membersOfView->names[ix];
+    struct trackDb *view = rFindView(parentTdb->subtracks, viewName);
+    if (view != NULL)
         {
-        char *stView;
-        if(!subgroupFind(subtrack,"view",&stView))
-            continue;
-        if(differentString(stView,membersOfView->names[ix]))
-            continue;
-        matchedSubtracks[ix] = subtrack;
-        if(!blockCfgs)
-            {
-            configurable[ix] = (char)cfgTypeFromTdb(subtrack,TRUE); // Warns if not multi-view compatible
+	matchedSubtracks[ix] = view;
+	configurable[ix] = (char)cfgTypeFromTdb(view->subtracks, TRUE); 
             if(configurable[ix] != cfgNone)
                 {
                 if(firstOpened == -1)
                     {
-                    safef(objName, sizeof(objName), "%s.%s.showCfg", parentTdb->tableName,membersOfView->names[ix]);
-                    if(cartUsualBoolean(cart,objName,FALSE))
+		safef(varName, sizeof(varName), "%s.%s.showCfg", parentTdb->tableName, viewName);
+		if(cartUsualBoolean(cart,varName,FALSE))
                         firstOpened = ix;
                     }
                 makeCfgRows = TRUE;
                 }
             }
-        break;
-        }
     }
 
 toLowerN(membersOfView->title, 1);
 printf("<B>Select %s </B>(<A HREF=\"../goldenPath/help/multiView.html\" title='Help on views' TARGET=_BLANK>help</A>):<BR>\n", membersOfView->title);
 puts("<TABLE><TR align=\"LEFT\">");
 for (ix = 0; ix < membersOfView->count; ix++)
     {
-    if(matchedSubtracks[ix] != NULL)
+    struct trackDb *view = matchedSubtracks[ix];
+    char *viewName = membersOfView->names[ix];
+    if (view != NULL)
         {
         printf("<TD>");
         if(configurable[ix] != cfgNone)
             {
@@ -4813,21 +4853,18 @@
         else
             printf("<B>%s</B>\n",membersOfView->values[ix]);
         puts("</TD>");
 
-        safef(objName, sizeof(objName), "%s.%s.vis", parentTdb->tableName,membersOfView->names[ix]);
+	safef(varName, sizeof(varName), "%s.%s.vis", parentTdb->tableName, viewName);
         enum trackVisibility tv =
-            hTvFromString(cartUsualString(cart, objName,hStringFromTv(visCompositeViewDefault(parentTdb,membersOfView->names[ix]))));
+	    hTvFromString(cartUsualString(cart, varName,hStringFromTv(visCompositeViewDefault(parentTdb,viewName))));
 
-        safef(javascript, sizeof(javascript), "onchange=\"matSelectViewForSubTracks(this,'%s');\"", membersOfView->names[ix]);
+	safef(javascript, sizeof(javascript), "onchange=\"matSelectViewForSubTracks(this,'%s');\"", viewName);
 
         printf("<TD>");
         safef(classes, sizeof(classes), "viewDD normalText %s", membersOfView->names[ix]);
-        hTvDropDownClassWithJavascript(objName, tv, parentTdb->canPack,classes,javascript);
+	hTvDropDownClassWithJavascript(varName, tv, parentTdb->canPack,classes,javascript);
         puts(" &nbsp; &nbsp; &nbsp;</TD>");
-        // Until the cfg boxes are inserted here, this divorces the relationship
-        //if(membersOfView->count > 6 && ix == ((membersOfView->count+1)/2)-1)  // An attempt at 2 rows of cfg's No good!
-        //    puts("</tr><tr><td>&nbsp;</td></tr><tr>");
         }
     }
 // Need to do the same for ENCODE Gencode 'filterBy's
 puts("</TR>");
@@ -4835,24 +4872,27 @@
     {
     puts("</TABLE><TABLE>");
     for (ix = 0; ix < membersOfView->count; ix++)
         {
-        if(matchedSubtracks[ix] != NULL)
+	struct trackDb *view = matchedSubtracks[ix];
+	if (view != NULL)
             {
-            printf("<TR id=\"tr_cfg_%s\"",membersOfView->names[ix]);
+	    char *viewName = membersOfView->names[ix];
+	    printf("<TR id=\"tr_cfg_%s\"", viewName);
             if((firstOpened == -1 && !compositeViewCfgExpandedByDefault(parentTdb,membersOfView->names[ix],NULL))
             || (firstOpened != -1 && firstOpened != ix))
                 printf(" style=\"display:none\"");
             printf("><TD width=10>&nbsp;</TD>");
             int ix2=ix;
             while(0 < ix2--)
                 printf("<TD width=100>&nbsp;</TD>");
             printf("<TD colspan=%d>",membersOfView->count+1);
-            safef(objName, sizeof(objName), "%s.%s", parentTdb->tableName,membersOfView->names[ix]);
+	    safef(varName, sizeof(varName), "%s", view->tableName);
             if(configurable[ix] != cfgNone)
                 {
-                cfgByCfgType(configurable[ix],db,cart,matchedSubtracks[ix],objName,membersOfView->values[ix],TRUE);
-                cfgLinkToDependentCfgs(parentTdb,objName);
+		cfgByCfgType(configurable[ix],db,cart,view->subtracks,varName,
+			membersOfView->values[ix],TRUE);
+		cfgLinkToDependentCfgs(parentTdb,varName);
                 }
             }
         }
     }
@@ -5051,9 +5091,9 @@
 else if (left && dimensionY && childTdb != NULL)
     printf("<TH ALIGN=RIGHT nowrap>%s</TH>\n",labelWithVocabLink(parentTdb,childTdb,dimensionY->tag,dimensionY->values[ixY]));
 }
 
-static int displayABCdimensions(struct cart *cart, struct trackDb *parentTdb,int expected)
+static int displayABCdimensions(struct cart *cart, struct trackDb *parentTdb, struct slRef *subtrackRefList, int expected)
 /* This will walk through all declared nonX&Y dimensions (X and Y is the 2D matrix of CBs.
    NOTE: ABC dims are only supported if there are X & Y both.  Also expected number should be passed in */
 {
 int count=0,ix;
@@ -5077,11 +5117,12 @@
     memset(cells, 0, sizeof(cells));
     memset(tdbs,  0, sizeof(tdbs));
 
     int aIx;
-    struct trackDb *subtrack;
-    for (subtrack = parentTdb->subtracks; subtrack != NULL; subtrack = subtrack->next)
+    struct slRef *subtrackRef;
+    for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
         {
+	struct trackDb *subtrack = subtrackRef->val;
         char *value;
         if(subgroupFind(subtrack,dim->tag,&value))
             {
             aIx = stringArrayIx(value,dim->names,dim->count);
@@ -5127,16 +5168,27 @@
     puts("</TABLE>");
 return count;
 }
 
+#ifdef DEBUG
+static void dumpDimension(members_t *dimension, char *name, FILE *f)
+/* Dump out information on dimension. */
+{
+int count = dimension->count;
+fprintf(f, "%s: count=%d tag=%s title=%s setting=%s<BR>\n", name, count, dimension->tag, dimension->title, dimension->setting);
+int i;
+for (i=0; i<count; ++i)
+    fprintf(f, "%s=%s ", dimension->names[i], dimension->values[i]);
+fprintf(f, "<BR>\n");
+}
+#endif /* DEBUG */
 
 static boolean hCompositeUiByMatrix(char *db, struct cart *cart, struct trackDb *parentTdb, char *formName)
 /* UI for composite tracks: matrix of checkboxes. */
 {
 //int ix;
 char objName[SMALLBUF];
 char javascript[JBUFSIZE];
-struct trackDb *subtrack;
 
 dimensions_t *dims = dimensionSettingsGet(parentTdb);
 if(dims == NULL)
     return FALSE;
@@ -5145,8 +5197,11 @@
 members_t *dimensionX = subgroupMembersGetByDimension(parentTdb,'X');
 members_t *dimensionY = subgroupMembersGetByDimension(parentTdb,'Y');
 if(dimensionX == NULL && dimensionY == NULL) // Must be an X or Y dimension
     return FALSE;
+// Get list of leaf subtracks to work with
+struct slRef *subtrackRef, *subtrackRefList = trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
+struct trackDb *subtrack;
 
 // use array of char determine all the cells (in X,Y,Z dimensions) that are actually populated
 char *value;
 int sizeOfX = dimensionX?dimensionX->count:1;
@@ -5156,10 +5211,11 @@
 struct trackDb *tdbsY[sizeOfY];
 memset(cells, 0, sizeof(cells));
 memset(tdbsX, 0, sizeof(tdbsX));
 memset(tdbsY, 0, sizeof(tdbsY));
-for (subtrack = parentTdb->subtracks; subtrack != NULL; subtrack = subtrack->next)
+for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
     {
+    subtrack = subtrackRef->val;
     ixX = (dimensionX ? -1 : 0 );
     ixY = (dimensionY ? -1 : 0 );
     if(dimensionX && subgroupFind(subtrack,dimensionX->tag,&value))
         {
@@ -5194,9 +5250,11 @@
 
 puts("<BR>\n");
 
 if(dims->count > 2)
-    displayABCdimensions(cart,parentTdb,(dims->count - 2));  // No dimABCs without X & Y both
+    {
+    displayABCdimensions(cart,parentTdb,subtrackRefList, dims->count - 2);  // No dimABCs without X & Y both
+    }
 dimensionsFree(&dims);
 
 printf("<TABLE class='greenBox' bgcolor='%s' borderColor='%s'>\n",COLOR_BG_DEFAULT,COLOR_BG_DEFAULT);
 
@@ -5270,9 +5328,9 @@
 
 static boolean hCompositeUiAllButtons(char *db, struct cart *cart, struct trackDb *parentTdb, char *formName)
 /* UI for composite tracks: all/none buttons only (as opposed to matrix or lots of buttons */
 {
-if(slCount(parentTdb->subtracks) <= 1)
+if (trackDbCountDescendantLeaves(parentTdb) <= 1)
     return FALSE;
 
 if(dimensionsExist(parentTdb))
     return FALSE;
@@ -5338,10 +5396,13 @@
     }
 button = cgiOptionalString(buttonVar);
 if (isNotEmpty(button))
     {
-    for (subtrack = parentTdb->subtracks; subtrack != NULL; subtrack = subtrack->next)
+    struct slRef *tdbRefList = trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
+    struct slRef *tdbRef;
+    for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
         {
+	subtrack = tdbRef->val;
         boolean newVal = FALSE;
         safef(option, sizeof(option), "%s_sel", subtrack->tableName);
         newVal = sameString(button, ADD_BUTTON_LABEL);
         if (primarySubtrack)
@@ -5399,11 +5460,13 @@
         puts("</TD></TR>");
         button = cgiOptionalString(buttonVar);
         if (isEmpty(button))
             continue;
-        for (subtrack = parentTdb->subtracks; subtrack != NULL;
-                subtrack = subtrack->next)
+	struct slRef *tdbRefList = trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
+	struct slRef *tdbRef;
+	for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
             {
+	    subtrack = tdbRef->val;
             char *p;
             int n;
             if ((p = trackDbSetting(subtrack, "subGroups")) == NULL)
                 continue;
@@ -5448,16 +5511,14 @@
     sameString(cartUsualString(cart, "displaySubtracks", "all"), "all");
 boolean isMatrix = dimensionsExist(tdb);
 boolean viewsOnly = FALSE;
 
-(void)parentTdbAbandonTablelessChildren(db,tdb);
-
 if(trackDbSetting(tdb, "dragAndDrop") != NULL)
     jsIncludeFile("jquery.tablednd.js", NULL);
 jsIncludeFile("hui.js",NULL);
 
 puts("<P>");
-if (slCount(tdb->subtracks) < MANY_SUBTRACKS && !hasSubgroups)
+if (trackDbCountDescendantLeaves(tdb) < MANY_SUBTRACKS && !hasSubgroups)
     {
     compositeUiAllSubtracks(db, cart, tdb, primarySubtrack);
     return;
     }
@@ -5474,27 +5535,37 @@
         }
     if(!viewsOnly)
         {
         if(trackDbSettingOn(tdb, "allButtonPair"))
+	    {
             hCompositeUiAllButtons(db, cart, tdb, formName);
+	    }
         else if (!hasSubgroups || !isMatrix || primarySubtrack)
+	    {
             hCompositeUiNoMatrix(db, cart,tdb,primarySubtrack,formName);
+	    }
         else
+	    {
             hCompositeUiByMatrix(db, cart, tdb, formName);
         }
     }
+    }
 
 cartSaveSession(cart);
 cgiContinueHiddenVar("g");
 
 if(displayAll)
+    {
     compositeUiAllSubtracks(db, cart, tdb, primarySubtrack);
+    }
 else
+    {
     compositeUiSelectedSubtracks(db, cart, tdb, primarySubtrack);
+    }
 
 if (primarySubtrack == NULL)  // primarySubtrack is set for tableBrowser but not hgTrackUi
     {
-        if (slCount(tdb->subtracks) > 5)
+    if (trackDbCountDescendantLeaves(tdb) > 5)
         {
         cgiMakeButton("Submit", "Submit");
         puts("<P>");
         }
@@ -5571,14 +5642,14 @@
 char *stView   = NULL;
 char *name     = NULL;
 char *rootName = NULL;
 // This routine should give these results: compositeName.viewName or else subtrackName.viewName or else compositeName or else subtrackName
-if(tdbIsCompositeChild(tdb) == TRUE && trackDbSetting(tdb, "subTrack") != NULL)
+if(tdbIsCompositeChild(tdb) == TRUE && trackDbLocalSetting(tdb, "subTrack") != NULL)
     {
     if(trackDbSettingClosestToHomeOn(tdb, "configurable"))
         rootName = tdb->tableName;  // subtrackName
     else
-        rootName = firstWordInLine(cloneString(trackDbSetting(tdb, "subTrack"))); // compositeName
+        rootName = firstWordInLine(cloneString(trackDbLocalSetting(tdb, "subTrack"))); // compositeName
     }
 if(rootName != NULL)
     {
     if(subgroupFind(tdb,"view",&stView))
@@ -5605,11 +5676,11 @@
 boolean isNameAtCompositeLevel(struct trackDb *tdb,char *name)
 /* cfgUi controls are passed a prefix name that may be at the composite or at the subtrack level
    returns TRUE for composite level name */
 {
-if(tdbIsCompositeChild(tdb)
-&& startsWith(tdb->parent->tableName,name)
-&& name[strlen(tdb->parent->tableName)] == '.')  // Cfg name at composite view level
+struct trackDb *parent;
+for (parent = tdb->parent; parent != NULL; parent = parent->parent)
+    if (startsWith(parent->tableName, name) && name[strlen(parent->tableName)] == '.')
     return TRUE;
 return FALSE;
 }
 

