7bc24686024ec14d351ab208b5179ab251d7c463
tdreszer
  Fri Oct 14 16:34:15 2011 -0700
Major work on bedFilt, psl and wigMaf to make them properly composite ready.
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index d62ee79..5641573 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -1112,31 +1112,31 @@
 				    BASE_COLOR_DRAW_OFF);
 stringVal = cartUsualStringClosestToHome(cart, tdb, FALSE, BASE_COLOR_VAR_SUFFIX,stringVal);
 
 return baseColorDrawOptStringToEnum(stringVal);
 }
 
 
 /*** Control of fancy indel display code: ***/
 
 static boolean tdbOrCartBoolean(struct cart *cart, struct trackDb *tdb,
                                 char *settingName, char *defaultOnOff)
 /* Query cart & trackDb to determine if a boolean variable is set. */
 {
 boolean alreadySet;
 alreadySet = !sameString("off",trackDbSettingOrDefault(tdb, settingName, defaultOnOff));
-alreadySet = cartUsualBooleanClosestToHome(cart, tdb, FALSE, settingName, alreadySet); // NOTE: compositeLevel=FALSE because tdb param already is at appropriate level
+alreadySet = cartUsualBooleanClosestToHome(cart, tdb, FALSE, settingName, alreadySet); // NOTE: viewLevel=FALSE because tdb param already is at appropriate level
 return alreadySet;
 }
 
 static boolean indelAppropriate(struct trackDb *tdb)
 /* Return true if it makes sense to offer indel display options for tdb. */
 {
 return (tdb && (startsWith("psl", tdb->type) || sameString("bam", tdb->type)) &&
 	(cfgOptionDefault("browser.indelOptions", NULL) != NULL));
 }
 
 static void indelEnabledByName(struct cart *cart, struct trackDb *tdb, char *name,
                   float basesPerPixel, boolean *retDoubleInsert, boolean *retQueryInsert,
                   boolean *retPolyA)
 /* Query cart & trackDb to determine what indel display (if any) is enabled. Set
  * basesPerPixel to 0.0 to disable check for zoom level.  */
@@ -1769,63 +1769,59 @@
 void chimpDropDown(char *var, char *curVal)
 /* Make drop down of options. */
 {
 cgiMakeDropList(var, chimpOptions, ArraySize(chimpOptions),
 	curVal);
 }
 
 
 
 /****** Some stuff for mRNA and EST related controls *******/
 
 static void addMrnaFilter(struct mrnaUiData *mud, char *track, char *label, char *key, char *table)
 /* Add an mrna filter */
 {
 struct mrnaFilter *fil;
-char buf[128];
 AllocVar(fil);
 fil->label = label;
-safef(buf, sizeof(buf), "%s_%s", track, key);
-fil->key = cloneString(buf);
+fil->suffix = cloneString(key);
 fil->table = table;
 slAddTail(&mud->filterList, fil);
 }
 
-struct mrnaUiData *newBedUiData(char *track)
+static struct mrnaUiData *newEmptyMrnaUiData(char *track)
 /* Make a new  in extra-ui data structure for a bed. */
 {
 struct mrnaUiData *mud;
-char buf[128];  /* Expand me here */
 AllocVar(mud);
-safef(buf, sizeof(buf), "%sFt", track);
-mud->filterTypeVar = cloneString(buf);
-safef(buf, sizeof(buf), "%sLt", track);
-mud->logicTypeVar = cloneString(buf);
+mud->filterTypeSuffix = cloneString("Ft");
+mud->logicTypeSuffix = cloneString("Lt");
+return mud;
+}
+
+struct mrnaUiData *newBedUiData(char *track)
+/* Make a new  in extra-ui data structure for a bed. */
+{
+struct mrnaUiData *mud = newEmptyMrnaUiData(track);
 addMrnaFilter(mud, track, "name", "name",track);
 return mud;
 }
 
 struct mrnaUiData *newMrnaUiData(char *track, boolean isXeno)
 /* Make a new  in extra-ui data structure for mRNA. */
 {
-struct mrnaUiData *mud;
-char buf[128];
-AllocVar(mud);
-safef(buf, sizeof(buf), "%sFt", track);
-mud->filterTypeVar = cloneString(buf);
-safef(buf, sizeof(buf), "%sLt", track);
-mud->logicTypeVar = cloneString(buf);
+struct mrnaUiData *mud = newEmptyMrnaUiData(track);
 if (isXeno)
     addMrnaFilter(mud, track, "organism", "org", "organism");
 addMrnaFilter(mud, track, "accession", "acc", "acc");
 addMrnaFilter(mud, track, "author", "aut", "author");
 addMrnaFilter(mud, track, "library", "lib", "library");
 addMrnaFilter(mud, track, "tissue", "tis", "tissue");
 addMrnaFilter(mud, track, "cell", "cel", "cell");
 addMrnaFilter(mud, track, "keyword", "key", "keyword");
 addMrnaFilter(mud, track, "gene", "gen", "geneName");
 addMrnaFilter(mud, track, "product", "pro", "productName");
 addMrnaFilter(mud, track, "description", "des", "description");
 return mud;
 }
 
 int trackNameAndLabelCmp(const void *va, const void *vb)
@@ -3326,32 +3322,32 @@
                     errAbort("filterBy values either all have labels (as value|label) or none do.");
                 *chipper++ = 0;  // The label is found inside the filters->svValues as the next string
                 strSwapChar(chipper,'_',' '); // Title does not have underscores
                 }
             else if (filterBy->valueAndLabel)
                 errAbort("filterBy values either all have labels in form of value|label or none do.");
         }
         }
 
     slAddTail(&filterBySet,filterBy); // Keep them in order (only a few)
 
     if(cart != NULL)
         {
         char suffix[256];
         safef(suffix, sizeof(suffix), "filterBy.%s", filterBy->column);
-        boolean compositeLevel = isNameAtCompositeLevel(tdb,name);
-        if(cartLookUpVariableClosestToHome(cart,tdb,compositeLevel,suffix,&(filterBy->htmlName)))
+        boolean viewLevel = isNameAtCompositeLevel(tdb,name);
+        if(cartLookUpVariableClosestToHome(cart,tdb,viewLevel,suffix,&(filterBy->htmlName)))
             filterBy->slChoices = cartOptionalSlNameList(cart,filterBy->htmlName);
         }
     if(filterBy->htmlName == NULL)
         {
         int len = strlen(name) + strlen(filterBy->column) + 15;
         filterBy->htmlName = needMem(len);
         safef(filterBy->htmlName, len, "%s.filterBy.%s", name,filterBy->column);
         }
     }
 freeMem(setting);
 
 return filterBySet;
 }
 
 void filterBySetFree(filterBy_t **filterBySet)
@@ -3718,61 +3714,60 @@
 {
 #ifdef SUBTRACK_CFG
 // When only one subtrack, then show it's cfg settings instead of composite/view level settings
 // This simplifies the UI where hgTrackUi won't have 2 levels of cfg,
 // while hgTracks still supports rightClick cfg of the subtrack.
 if (configurableByAjax(tdb,cType) > 0) // Only if subtrack's configurable by ajax do we consider this option
     {
     if (tdbIsComposite(tdb)                       // called for the composite
     && !tdbIsCompositeView(tdb->subtracks)        // and there is no view level
     && slCount(tdb->subtracks) == 1)              // and there is only one subtrack
         {
         //warn("What do you mean by having a composite (%s) with only one subtrack (%s) ???",tdb->track,tdb->subtracks->track);
         tdb = tdb->subtracks; // show subtrack cfg instead
         prefix = tdb->track;
         }
-    else if (tdb->parent != NULL
+    else if (tdb->parent != NULL                 // called with subtrack (tdb is never a view)
         && tdbIsCompositeView(tdb->parent)       // subtrack has view
-        && differentString(prefix,tdb->track)    // and this has been called for the view
-        && slCount(tdb->parent->subtracks) == 1) // and there is only one subtrack
+        && differentString(prefix,tdb->track)    // and this has been called FOR the view
+        && slCount(tdb->parent->subtracks) == 1) // and view has only one subtrack
         prefix = tdb->track; // removes reference to view level
     }
 #endif///def SUBTRACK_CFG
 switch(cType)
     {
     case cfgBedScore:
 	{
 	char *scoreMax = trackDbSettingClosestToHome(tdb, SCORE_FILTER _MAX);
 	int maxScore = (scoreMax ? sqlUnsigned(scoreMax):1000);
 	scoreCfgUi(db, cart,tdb,prefix,title,maxScore,boxed);
 	}
 	break;
     case cfgPeak:
                         encodePeakCfgUi(cart,tdb,prefix,title,boxed);
                         break;
     case cfgWig:        wigCfgUi(cart,tdb,prefix,title,boxed);
                         break;
-    case cfgWigMaf:     // NOTE: wigMaf is using non-standard view level naming methods so isn't configurable by ajax
-                        wigMafCfgUi(cart,tdb,prefix,title,boxed, db);
+    case cfgWigMaf:     wigMafCfgUi(cart,tdb,prefix,title,boxed, db);
                         break;
     case cfgGenePred:   genePredCfgUi(cart,tdb,prefix,title,boxed);
                         break;
     case cfgChain:      chainCfgUi(db,cart,tdb,prefix,title,boxed, NULL);
                         break;
     case cfgNetAlign:	netAlignCfgUi(db,cart,tdb,prefix,title,boxed);
                         break;
-    case cfgBedFilt:    bedUi(tdb,cart,title, boxed);
+    case cfgBedFilt:    bedFiltCfgUi(cart,tdb,prefix,title, boxed);
                  	break;
 #ifdef USE_BAM
     case cfgBam:        bamCfgUi(cart, tdb, prefix, title, boxed);
 			break;
 #endif
     case cfgVcf:	vcfCfgUi(cart, tdb, prefix, title, boxed);
 			break;
     case cfgPsl:	pslCfgUi(db,cart,tdb,prefix,title,boxed);
                         break;
     default:            warn("Track type is not known to multi-view composites. type is: %d ", cType);
                         break;
     }
 }
 
 char *encodeRestrictionDate(char *db,struct trackDb *trackDb,boolean excludePast)
@@ -4505,77 +4500,119 @@
 radioButton(filterTypeVar, filterTypeVal, "green");
 radioButton(filterTypeVar, filterTypeVal, "blue");
 radioButton(filterTypeVar, filterTypeVal, "exclude");
 radioButton(filterTypeVar, filterTypeVal, "include");
 if (none)
     radioButton(filterTypeVar, filterTypeVal, "none");
 }
 
 void radioButton(char *var, char *val, char *ourVal)
 /* Print one radio button */
 {
 cgiMakeRadioButton(var, ourVal, sameString(ourVal, val));
 printf("%s ", ourVal);
 }
 
-void oneMrnaFilterUi(struct controlGrid *cg, char *text, char *var, struct cart *cart)
+void oneMrnaFilterUi(struct controlGrid *cg, struct trackDb *tdb, char *text, char *var, char *suffix, struct cart *cart)
 /* Print out user interface for one type of mrna filter. */
 {
 controlGridStartCell(cg);
 printf("%s:<BR>", text);
-cgiMakeTextVar(var, cartUsualString(cart, var, ""), 19);
+boolean viewLevel = isNameAtCompositeLevel(tdb,var);
+cgiMakeTextVar(var, cartUsualStringClosestToHome(cart, tdb, viewLevel,suffix, ""), 19);
 controlGridEndCell(cg);
 }
 
-void bedUi(struct trackDb *tdb, struct cart *cart, char *title, boolean boxed)
-/* Put up UI for an mRNA (or EST) track. */
+void bedFiltCfgUi(struct cart *cart, struct trackDb *tdb, char *prefix, char *title, boolean boxed)
+/* Put up UI for an "bedFilter" tracks. */
 {
-struct mrnaUiData *mud = newBedUiData(tdb->track);
+struct mrnaUiData *mud = newBedUiData(prefix);
 struct mrnaFilter *fil;
 struct controlGrid *cg = NULL;
-char *filterTypeVar = mud->filterTypeVar;
-char *filterTypeVal = cartUsualString(cart, filterTypeVar, "red");
+boolean viewLevel = isNameAtCompositeLevel(tdb,prefix);
+char *filterTypeVal = cartUsualStringClosestToHome(cart, tdb, viewLevel, mud->filterTypeSuffix, "red");
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 /* Define type of filter. */
 printf("<table width=400><tr><td align='left'>\n");
-filterButtons(filterTypeVar, filterTypeVal, FALSE);
+char buffer[256];
+safef(buffer, sizeof buffer,"%s.%s",prefix,mud->filterTypeSuffix);
+filterButtons(buffer, filterTypeVal, FALSE);
 printf("</br>");
 /* List various fields you can filter on. */
 cg = startControlGrid(4, NULL);
 for (fil = mud->filterList; fil != NULL; fil = fil->next)
-    oneMrnaFilterUi(cg, fil->label, fil->key, cart);
+    {
+    safef(buffer, sizeof buffer,"%s.%s",prefix,fil->suffix);
+    oneMrnaFilterUi(cg, tdb, fil->label, buffer, fil->suffix, cart);
+    }
 endControlGrid(&cg);
 cfgEndBox(boxed);
 }
 
+void mrnaCfgUi(struct cart *cart, struct trackDb *tdb, char *prefix, char *title, boolean boxed)
+/* Put up UI for an mRNA (or EST) track. */
+{
+boolean isXeno = (sameString(tdb->track, "xenoMrna") ||  sameString(tdb->track, "xenoEst"));
+struct mrnaUiData *mud = newMrnaUiData(prefix, isXeno);
+struct mrnaFilter *fil;
+struct controlGrid *cg = NULL;
+boolean viewLevel = isNameAtCompositeLevel(tdb,prefix);
+char *filterTypeVal = cartUsualStringClosestToHome(cart, tdb, viewLevel, mud->filterTypeSuffix,"red");
+char *logicTypeVal  = cartUsualStringClosestToHome(cart, tdb, viewLevel, mud->logicTypeSuffix, "and");
+
+boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
+/* Define type of filter. */
+char buffer[256];
+safef(buffer,sizeof buffer,"%s.%s",prefix,mud->filterTypeSuffix);
+filterButtons(buffer, filterTypeVal, FALSE);
+printf("  <B>Combination Logic:</B> ");
+safef(buffer,sizeof buffer,"%s.%s",prefix,mud->logicTypeSuffix);
+radioButton(buffer, logicTypeVal, "and");
+radioButton(buffer, logicTypeVal, "or");
+printf("<BR>\n");
+
+/* List various fields you can filter on. */
+printf("<table border=0 cellspacing=1 cellpadding=1 width=%d>\n", CONTROL_TABLE_WIDTH);
+cg = startControlGrid(4, NULL);
+for (fil = mud->filterList; fil != NULL; fil = fil->next)
+    {
+    safef(buffer,sizeof buffer,"%s.%s",prefix,fil->suffix);
+    oneMrnaFilterUi(cg, tdb, fil->label, buffer, fil->suffix, cart);
+    }
+endControlGrid(&cg);
+baseColorDrawOptDropDown(cart, tdb);
+indelShowOptions(cart, tdb);
+cfgEndBox(boxed);
+}
+
 
 void scoreGrayLevelCfgUi(struct cart *cart, struct trackDb *tdb, char *prefix, int scoreMax)
 /* If scoreMin has been set, let user select the shade of gray for that score, in case
  * the default is too light to see or darker than necessary. */
 {
-boolean compositeLevel = isNameAtCompositeLevel(tdb,prefix);
+boolean viewLevel = isNameAtCompositeLevel(tdb,prefix);
 char *scoreMinStr = trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN);
 if (scoreMinStr != NULL)
     {
     int scoreMin = atoi(scoreMinStr);
     // maxShade=9 taken from hgTracks/simpleTracks.c.  Ignore the 10 in shadesOfGray[10+1] --
     // maxShade is used to access the array.
     int maxShade = 9;
     int scoreMinGrayLevel = scoreMin * maxShade/scoreMax;
     if (scoreMinGrayLevel <= 0) scoreMinGrayLevel = 1;
     char *setting = trackDbSettingClosestToHome(tdb, MIN_GRAY_LEVEL);
-    int minGrayLevel = cartUsualIntClosestToHome(cart, tdb, compositeLevel, MIN_GRAY_LEVEL,
+    int minGrayLevel = cartUsualIntClosestToHome(cart, tdb, viewLevel, MIN_GRAY_LEVEL,
                         setting ? atoi(setting) : scoreMinGrayLevel);
     if (minGrayLevel <= 0) minGrayLevel = 1;
     if (minGrayLevel > maxShade) minGrayLevel = maxShade;
     puts("\n<B>Shade of lowest-scoring items: </B>");
     // Add javascript to select so that its color is consistent with option colors:
     int level = 255 - (255*minGrayLevel / maxShade);
     printf("<SELECT NAME=\"%s.%s\" STYLE='color: #%02x%02x%02x' class='normalText'",
 	   prefix, MIN_GRAY_LEVEL, level, level, level);
     int i;
 #ifdef OMIT
     // IE works without this code and FF doesn't work with it.
     printf(" onchange=\"switch(this.value) {");
     for (i = 1;  i < maxShade;  i++)
         {
         level = 255 - (255*i / maxShade);
@@ -4661,205 +4698,205 @@
         {
         if(min && *min == NULL && minLoc != NULL)
             *min=minLoc;
         else
             freeMem(minLoc);
         if(max && *max == NULL && maxLoc != NULL)
             *max=maxLoc;
         else
             freeMem(maxLoc);
         return TRUE;
         }
     }
 return FALSE;
 }
 
-static void getScoreIntRangeFromCart(struct cart *cart, struct trackDb *tdb, char *scoreName,
-                                 int *limitMin, int *limitMax,int *min,int *max)
+static void getScoreIntRangeFromCart(struct cart *cart, struct trackDb *tdb, boolean viewLimit,
+                                 char *scoreName, int *limitMin, int *limitMax,int *min,int *max)
 /* gets an integer score range from the cart, but the limits from trackDb
    for any of the pointers provided, will return a value found, if found, else it's contents
    are undisturbed (use NO_VALUE to recognize unavaliable values) */
 {
 char scoreLimitName[128];
 char *deMin=NULL,*deMax=NULL;
 if((limitMin || limitMax) && getScoreLimitsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
     {
     if(deMin != NULL && limitMin)
         *limitMin = atoi(deMin);
     if(deMax != NULL && limitMax)
         *limitMax = atoi(deMax);
     freeMem(deMin);
     freeMem(deMax);
     }
 if((min || max) && getScoreDefaultsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
     {
     if(deMin != NULL && min)
         *min = atoi(deMin);
     if(deMax != NULL && max)
         *max =atoi(deMax);
     freeMem(deMin);
     freeMem(deMax);
     }
 if(max)
     {
     safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _MAX);
-    deMax = cartOptionalStringClosestToHome(cart, tdb,FALSE,scoreLimitName);
+    deMax = cartOptionalStringClosestToHome(cart, tdb,viewLimit,scoreLimitName);
     if(deMax != NULL)
         *max = atoi(deMax);
     }
 if(min)
     {
     safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, (max && deMax? _MIN:"")); // Warning: name changes if max!
-    deMin = cartOptionalStringClosestToHome(cart, tdb,FALSE,scoreLimitName);
+    deMin = cartOptionalStringClosestToHome(cart, tdb,viewLimit,scoreLimitName);
     if(deMin != NULL)
         *min = atoi(deMin);
     }
 // Defaulting min and max within limits.  Sorry for the horizontal ifs, but stacking the group makes them easier to follow
 if (min && limitMin && *limitMin != NO_VALUE && (*min == NO_VALUE || *min < *limitMin)) *min = *limitMin;
 if (min && limitMax && *limitMax != NO_VALUE &&                      *min > *limitMax)  *min = *limitMax;
 if (max && limitMax && *limitMax != NO_VALUE && (*max == NO_VALUE || *max > *limitMax)) *max = *limitMax;
 if (max && limitMin && *limitMin != NO_VALUE &&                      *max < *limitMin)  *max = *limitMin;
 }
 
-static void getScoreFloatRangeFromCart(struct cart *cart, struct trackDb *tdb, char *scoreName,
-                                   double *limitMin,double *limitMax,double*min,double*max)
+static void getScoreFloatRangeFromCart(struct cart *cart, struct trackDb *tdb, boolean viewLevel,
+                         char *scoreName, double *limitMin,double *limitMax,double*min,double*max)
 /* gets an double score range from the cart, but the limits from trackDb
    for any of the pointers provided, will return a value found, if found, else it's contents
    are undisturbed (use NO_VALUE to recognize unavaliable values) */
 {
 char scoreLimitName[128];
 char *deMin=NULL,*deMax=NULL;
 if((limitMin || limitMax) && getScoreLimitsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
     {
     if(deMin != NULL && limitMin)
         *limitMin = strtod(deMin,NULL);
     if(deMax != NULL && limitMax)
         *limitMax =strtod(deMax,NULL);
     freeMem(deMin);
     freeMem(deMax);
     }
 if((min || max) && getScoreDefaultsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
     {
     if(deMin != NULL && min)
         *min = strtod(deMin,NULL);
     if(deMax != NULL && max)
         *max =strtod(deMax,NULL);
     freeMem(deMin);
     freeMem(deMax);
     }
 if(max)
     {
     safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _MAX);
-    deMax = cartOptionalStringClosestToHome(cart, tdb,FALSE,scoreLimitName);
+    deMax = cartOptionalStringClosestToHome(cart, tdb,viewLevel,scoreLimitName);
     if(deMax != NULL)
         *max = strtod(deMax,NULL);
     }
 if(min)
     {
     safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _MIN); // name is always {filterName}Min
-    deMin = cartOptionalStringClosestToHome(cart, tdb,FALSE,scoreLimitName);
+    deMin = cartOptionalStringClosestToHome(cart, tdb,viewLevel,scoreLimitName);
     if(deMin != NULL)
         *min = strtod(deMin,NULL);
     }
 // Defaulting min and max within limits.  Sorry for the horizontal ifs, but stacking the group makes them easier to follow
 if (min && limitMin && (int)(*limitMin) != NO_VALUE && ((int)(*min) == NO_VALUE || *min < *limitMin)) *min = *limitMin;
 if (min && limitMax && (int)(*limitMax) != NO_VALUE &&                             *min > *limitMax)  *min = *limitMax;
 if (max && limitMax && (int)(*limitMax) != NO_VALUE && ((int)(*max) == NO_VALUE || *max > *limitMax)) *max = *limitMax;
 if (max && limitMin && (int)(*limitMin) != NO_VALUE &&                             *max < *limitMin)  *max = *limitMin;
 }
 
 static boolean showScoreFilter(struct cart *cart, struct trackDb *tdb, boolean *opened, boolean boxed,
-                               boolean compositeLevel,char *name, char *title, char *label,
+                               boolean viewLevel,char *name, char *title, char *label,
                                char *scoreName, boolean isFloat)
 /* Shows a score filter control with minimum value and optional range */
 {
 char *setting = trackDbSetting(tdb, scoreName);
 if(setting)
     {
     if(*opened == FALSE)
         {
         boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
         puts("<TABLE>");
         *opened = TRUE;
         }
     printf("<TR><TD align='right'><B>%s:</B><TD align='left'>",label);
     char varName[256];
     char altLabel[256];
     safef(varName, sizeof(varName), "%s%s", scoreName, _BY_RANGE);
     boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, varName);
     // NOTE: could determine isFloat = (strchr(setting,'.') != NULL);  However, historical trackDb settings of pValueFilter did not always contain '.'
     if (isFloat)
         {
         double minLimit=NO_VALUE,maxLimit=NO_VALUE;
         double minVal=minLimit,maxVal=maxLimit;
         colonPairToDoubles(setting,&minVal,&maxVal);
-        getScoreFloatRangeFromCart(cart,tdb,scoreName,&minLimit,&maxLimit,&minVal,&maxVal);
+        getScoreFloatRangeFromCart(cart,tdb,viewLevel,scoreName,&minLimit,&maxLimit,&minVal,&maxVal);
         safef(varName, sizeof(varName), "%s.%s%s", name, scoreName, _MIN);
         safef(altLabel, sizeof(altLabel), "%s%s", (filterByRange?"Minimum ":""), htmlEncodeText(htmlTextStripTags(label),FALSE));
         cgiMakeDoubleVarWithLimits(varName,minVal, altLabel, 0,minLimit, maxLimit);
         if(filterByRange)
             {
             printf("<TD align='left'>to<TD align='left'>");
             safef(varName, sizeof(varName), "%s.%s%s", name, scoreName, _MAX);
             safef(altLabel, sizeof(altLabel), "%s%s", (filterByRange?"Maximum ":""), label);
             cgiMakeDoubleVarWithLimits(varName,maxVal, altLabel, 0,minLimit, maxLimit);
             }
         safef(altLabel, sizeof(altLabel), "%s", (filterByRange?"": "colspan=3"));
         if(minLimit != NO_VALUE && maxLimit != NO_VALUE)
             printf("<TD align='left'%s> (%g to %g)",altLabel,minLimit, maxLimit);
         else if(minLimit != NO_VALUE)
             printf("<TD align='left'%s> (minimum %g)",altLabel,minLimit);
         else if(maxLimit != NO_VALUE)
             printf("<TD align='left'%s> (maximum %g)",altLabel,maxLimit);
         else
             printf("<TD align='left'%s",altLabel);
         }
     else
         {
         int minLimit=NO_VALUE,maxLimit=NO_VALUE;
         int minVal=minLimit,maxVal=maxLimit;
         colonPairToInts(setting,&minVal,&maxVal);
-        getScoreIntRangeFromCart(cart,tdb,scoreName,&minLimit,&maxLimit,&minVal,&maxVal);
+        getScoreIntRangeFromCart(cart,tdb,viewLevel,scoreName,&minLimit,&maxLimit,&minVal,&maxVal);
         safef(varName, sizeof(varName), "%s.%s%s", name, scoreName, filterByRange ? _MIN:"");
         safef(altLabel, sizeof(altLabel), "%s%s", (filterByRange?"Minimum ":""), label);
         cgiMakeIntVarWithLimits(varName,minVal, altLabel, 0,minLimit, maxLimit);
         if(filterByRange)
             {
             printf("<TD align='left'>to<TD align='left'>");
             safef(varName, sizeof(varName), "%s.%s%s", name, scoreName, _MAX);
             safef(altLabel, sizeof(altLabel), "%s%s", (filterByRange?"Maximum ":""), label);
             cgiMakeIntVarWithLimits(varName,maxVal, altLabel, 0,minLimit, maxLimit);
             }
         safef(altLabel, sizeof(altLabel), "%s", (filterByRange?"": "colspan=3"));
         if(minLimit != NO_VALUE && maxLimit != NO_VALUE)
             printf("<TD align='left'%s> (%d to %d)",altLabel,minLimit, maxLimit);
         else if(minLimit != NO_VALUE)
             printf("<TD align='left'%s> (minimum %d)",altLabel,minLimit);
         else if(maxLimit != NO_VALUE)
             printf("<TD align='left'%s> (maximum %d)",altLabel,maxLimit);
         else
             printf("<TD align='left'%s",altLabel);
         }
     puts("</TR>");
     return TRUE;
     }
 return FALSE;
 }
 
 
 static int numericFiltersShowAll(struct cart *cart, struct trackDb *tdb, boolean *opened, boolean boxed,
-                               boolean compositeLevel,char *name, char *title)
+                               boolean viewLevel,char *name, char *title)
 // Shows all *Filter style filters.  Note that these are in random order and have no graceful title
 {
 int count = 0;
 struct slName *filterSettings = trackDbSettingsWildMatch(tdb, "*Filter");
 if (filterSettings)
     {
     puts("<BR>");
     struct slName *filter = NULL;
     struct slPair *extras = NULL;
     char *extraFields = trackDbSetting(tdb, "extraFields");  // TODo: seems like there should be a cleaner way
     if (extraFields != NULL)
         extras = slPairListFromString(extraFields,TRUE); // Quoted strings may be okay
 
     while ((filter = slPopHead(&filterSettings)) != NULL)
         {
@@ -4878,31 +4915,31 @@
                     freeMem(field);
                     field = strchr(foundLabel,']');
                     if (field == NULL)
                         field = cloneString(foundLabel);
                     else
                         field = cloneString(field + 1);
                     strSwapChar(field,'_',' ');
                     }
                 }
             char label[128];
             safef(label,sizeof(label),"Minimum %s",field);
             freeMem(field);
             // Determine floating point or integer
             char *setting = trackDbSetting(tdb, filter->name);
             boolean isFloat = (strchr(setting,'.') != NULL);
-            showScoreFilter(cart,tdb,opened,boxed,compositeLevel,name,title,label,filter->name,isFloat);
+            showScoreFilter(cart,tdb,opened,boxed,viewLevel,name,title,label,filter->name,isFloat);
             count++;
             }
         slNameFree(&filter);
         }
     }
 if (count > 0)
     puts("</TABLE>");
 return count;
 }
 
 
 boolean bedScoreHasCfgUi(struct trackDb *tdb)
 // Confirms that this track has a bedScore Cfg UI
 {
 // Assumes that cfgType == cfgBedScore
@@ -4932,37 +4969,37 @@
     slNameFreeList(&filterSettings);
     if (one)
         return TRUE;
     }
 if (!blocked)  // scoreFilter is implicit unless NO_SCORE_FILTER
     return TRUE;
 
 return FALSE;
 }
 
 
 void scoreCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title,  int maxScore, boolean boxed)
 /* Put up UI for filtering bed track based on a score */
 {
 char option[256];
-boolean compositeLevel = isNameAtCompositeLevel(tdb,name);
+boolean viewLevel = isNameAtCompositeLevel(tdb,name);
 boolean skipScoreFilter = FALSE;
 filterBy_t *filterBySet = filterBySetGet(tdb,cart,name);
 
 // Numeric filters are first
 boolean isBoxOpened = FALSE;
-if (numericFiltersShowAll(cart, tdb, &isBoxOpened, boxed, compositeLevel, name, title) > 0)
+if (numericFiltersShowAll(cart, tdb, &isBoxOpened, boxed, viewLevel, name, title) > 0)
     skipScoreFilter = TRUE;
 
 // Add any multi-selects next
 if(filterBySet != NULL)
     {
     if(!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL)
         jsIncludeFile("hui.js",NULL);
 
     if (!isBoxOpened)   // Note filterBy boxes are not double "boxed", if there are no other filters
         printf("<BR>");
     filterBySetCfgUi(cart,tdb,filterBySet,TRUE);
     filterBySetFree(&filterBySet);
     skipScoreFilter = TRUE;
     }
 
@@ -4974,31 +5011,31 @@
         cfgEndBox(boxed);
 
     return; // Cannot have both '*filter' and 'scoreFilter'
     }
 
 boolean scoreFilterOk = (trackDbSettingClosestToHome(tdb, NO_SCORE_FILTER) == NULL);
 boolean glvlScoreMin = (trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN) != NULL);
 if (! (scoreFilterOk || glvlScoreMin))
     return;
 
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
 if (scoreFilterOk)
     {
     int minLimit=0,maxLimit=maxScore,minVal=0,maxVal=maxScore;
-    getScoreIntRangeFromCart(cart,tdb,SCORE_FILTER,&minLimit,&maxLimit,&minVal,&maxVal);
+    getScoreIntRangeFromCart(cart,tdb,viewLevel,SCORE_FILTER,&minLimit,&maxLimit,&minVal,&maxVal);
 
     boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, SCORE_FILTER _BY_RANGE);
     if (filterByRange)
         {
         puts("<B>Filter score range:  min:</B>");
         snprintf(option, sizeof(option), "%s.%s", name,SCORE_FILTER _MIN);
         cgiMakeIntVarWithLimits(option, minVal, "Minimum score",0, minLimit,maxLimit);
         puts("<B>max:</B>");
         snprintf(option, sizeof(option), "%s.%s", name,SCORE_FILTER _MAX);
         cgiMakeIntVarWithLimits(option, maxVal, "Maximum score",0,minLimit,maxLimit);
         printf("(%d to %d)\n",minLimit,maxLimit);
         }
     else
         {
         printf("<b>Show only items with score at or above:</b> ");
@@ -5012,40 +5049,40 @@
 
 if (glvlScoreMin)
     scoreGrayLevelCfgUi(cart, tdb, name, maxScore);
 
 /* filter top-scoring N items in track */
 char *scoreCtString = trackDbSettingClosestToHome(tdb, "filterTopScorers");
 if (scoreCtString != NULL)
     {
     /* show only top-scoring items. This option only displayed if trackDb
      * setting exists.  Format:  filterTopScorers <on|off> <count> <table> */
     char *words[2];
     char *scoreFilterCt = NULL;
     chopLine(cloneString(scoreCtString), words);
     safef(option, sizeof(option), "%s.filterTopScorersOn", name);
     bool doScoreCtFilter =
-        cartUsualBooleanClosestToHome(cart, tdb, compositeLevel, "filterTopScorersOn", sameString(words[0], "on"));
+        cartUsualBooleanClosestToHome(cart, tdb, viewLevel, "filterTopScorersOn", sameString(words[0], "on"));
     puts("<P>");
     cgiMakeCheckBox(option, doScoreCtFilter);
     safef(option, sizeof(option), "%s.filterTopScorersCt", name);
-    scoreFilterCt = cartUsualStringClosestToHome(cart, tdb, compositeLevel, "filterTopScorersCt", words[1]);
+    scoreFilterCt = cartUsualStringClosestToHome(cart, tdb, viewLevel, "filterTopScorersCt", words[1]);
 
     puts("&nbsp; <B> Show only items in top-scoring </B>");
     cgiMakeIntVarWithLimits(option,atoi(scoreFilterCt),"Top-scoring count",0,1,100000);
     /* Only check size of table if track does not have subtracks */
-    if ( !compositeLevel && hTableExists(db, tdb->table))
+    if ( !viewLevel && hTableExists(db, tdb->table))
         printf("&nbsp; (range: 1 to 100,000 total items: %d)\n",getTableSize(db, tdb->table));
     else
         printf("&nbsp; (range: 1 to 100,000)\n");
     }
 cfgEndBox(boxed);
 }
 
 // Moved from hgTrackUi for consistency
 static void filterByChromCfgUi(struct cart *cart, struct trackDb *tdb)
 {
 char *filterSetting;
 char filterVar[256];
 char *filterVal = "";
 
 printf("<p><b>Filter by chromosome (e.g. chr10):</b> ");
@@ -5084,94 +5121,94 @@
 char *words[8];
 int wordCount = wordCount = chopLine(typeLine, words);
 if (wordCount == 3 && sameWord(words[1], "xeno"))
     crossSpeciesCfgUi(cart,tdb);
 baseColorDropLists(cart, tdb, name);
 indelShowOptionsWithName(cart, tdb, name);
 cfgEndBox(boxed);
 }
 
 
 void netAlignCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *prefix, char *title, boolean boxed)
 /* Put up UI for net tracks */
 {
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
-boolean compositeLevel = isNameAtCompositeLevel(tdb,prefix);
+boolean viewLevel = isNameAtCompositeLevel(tdb,prefix);
 
-enum netColorEnum netColor = netFetchColorOption(cart, tdb, compositeLevel);
+enum netColorEnum netColor = netFetchColorOption(cart, tdb, viewLevel);
 
 char optString[256];	/*	our option strings here	*/
 safef(optString, ArraySize(optString), "%s.%s", prefix, NET_COLOR );
 printf("<p><b>Color nets by:&nbsp;</b>");
 netColorDropDown(optString, netColorEnumToString(netColor));
 
 #ifdef NOT_YET
-enum netLevelEnum netLevel = netFetchLevelOption(cart, tdb, compositeLevel);
+enum netLevelEnum netLevel = netFetchLevelOption(cart, tdb, viewLevel);
 
 safef( optString, ArraySize(optString), "%s.%s", prefix, NET_LEVEL );
 printf("<p><b>Limit display of nets to:&nbsp;</b>");
 netLevelDropDown(optString, netLevelEnumToString(netLevel));
 #endif
 
 cfgEndBox(boxed);
 }
 
 void chainCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *prefix, char *title, boolean boxed, char *chromosome)
 /* Put up UI for chain tracks */
 {
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
-boolean compositeLevel = isNameAtCompositeLevel(tdb,prefix);
+boolean viewLevel = isNameAtCompositeLevel(tdb,prefix);
 
 enum chainColorEnum chainColor =
-	chainFetchColorOption(cart, tdb, compositeLevel);
+	chainFetchColorOption(cart, tdb, viewLevel);
 
 /* check if we have normalized scores available */
 boolean normScoreAvailable = chainDbNormScoreAvailable(tdb);
 
 char optString[256];
 if (normScoreAvailable)
     {
     safef(optString, ArraySize(optString), "%s.%s", prefix, OPT_CHROM_COLORS );
     printf("<p><b>Color chains by:&nbsp;</b>");
     chainColorDropDown(optString, chainColorEnumToString(chainColor));
     }
 else
     {
     printf("<p><b>Color track based on chromosome:</b>&nbsp;");
 
     char optString[256];
     /* initial value of chromosome coloring option is "on", unless
      * overridden by the colorChromDefault setting in the track */
     char *binaryColorDefault =
 	    trackDbSettingClosestToHomeOrDefault(tdb, "colorChromDefault", "on");
     /* allow cart to override trackDb setting */
     safef(optString, sizeof(optString), "%s.color", prefix);
     char * colorSetting = cartUsualStringClosestToHome(cart, tdb,
-	compositeLevel, "color", binaryColorDefault);
+	viewLevel, "color", binaryColorDefault);
     cgiMakeRadioButton(optString, "on", sameString(colorSetting, "on"));
     printf(" on ");
     cgiMakeRadioButton(optString, "off", sameString(colorSetting, "off"));
     printf(" off ");
     printf("<br>\n");
     }
 
 printf("<p><b>Filter by chromosome (e.g. chr10):</b> ");
 safef(optString, ArraySize(optString), "%s.%s", prefix, OPT_CHROM_FILTER);
 cgiMakeTextVar(optString,
-    cartUsualStringClosestToHome(cart, tdb, compositeLevel,
+    cartUsualStringClosestToHome(cart, tdb, viewLevel,
 	OPT_CHROM_FILTER, ""), 15);
 
 if (normScoreAvailable)
     scoreCfgUi(db, cart,tdb,prefix,NULL,CHAIN_SCORE_MAXIMUM,FALSE);
 
 cfgEndBox(boxed);
 }
 
 struct dyString *dyAddFilterAsInt(struct cart *cart, struct trackDb *tdb,
        struct dyString *extraWhere,char *filter,char *defaultLimits, char*field, boolean *and)
 /* creates the where clause condition to support numeric int filter range.
    Filters are expected to follow
         {fiterName}: trackDb min or min:max - default value(s);
         {filterName}Min or {filterName}: min (user supplied) cart variable;
         {filterName}Max: max (user supplied) cart variable;
@@ -5189,31 +5226,31 @@
 if(trackDbSettingClosestToHome(tdb, filterLimitName) != NULL)
     return extraWhere;
 
 char *setting = NULL;
 if(differentWord(filter,SCORE_FILTER))
     setting = trackDbSettingClosestToHome(tdb, filter);
 else
     setting = trackDbSettingClosestToHomeOrDefault(tdb, filter,"0:1000");
 if(setting || sameWord(filter,NO_SCORE_FILTER))
     {
     boolean invalid = FALSE;
     int minValueTdb = 0,maxValueTdb = NO_VALUE;
     colonPairToInts(setting,&minValueTdb,&maxValueTdb);
     int minLimit=NO_VALUE,maxLimit=NO_VALUE,min=minValueTdb,max=maxValueTdb;
     colonPairToInts(defaultLimits,&minLimit,&maxLimit);
-    getScoreIntRangeFromCart(cart,tdb,filter,&minLimit,&maxLimit,&min,&max);
+    getScoreIntRangeFromCart(cart,tdb,FALSE,filter,&minLimit,&maxLimit,&min,&max);
     if(minLimit != NO_VALUE || maxLimit != NO_VALUE)
         {
         // assume tdb default values within range! (don't give user errors that have no consequence)
         if((min != minValueTdb && ((minLimit != NO_VALUE && min < minLimit)
                                 || (maxLimit != NO_VALUE && min > maxLimit)))
         || (max != maxValueTdb && ((minLimit != NO_VALUE && max < minLimit)
                                 || (maxLimit != NO_VALUE && max > maxLimit))))
             {
             invalid = TRUE;
             char value[64];
             if(max == NO_VALUE) // min only is allowed, but max only is not
                 safef(value, sizeof(value), "entered minimum (%d)", min);
             else
                 safef(value, sizeof(value), "entered range (min:%d and max:%d)", min, max);
             char limits[64];
@@ -5269,31 +5306,31 @@
         {fiterName}: trackDb min or min:max - default value(s);
         {filterName}Min or {filterName}: min (user supplied) cart variable;
         {filterName}Max: max (user supplied) cart variable;
         {filterName}Limits: trackDb allowed range "0.0:10.0" Optional
             uses:  defaultLimits: function param if no tdb limits settings found)
    The 'and' param and dyString in/out allows stringing multiple where clauses together */
 {
 char *setting = trackDbSettingClosestToHome(tdb, filter);
 if(setting)
     {
     boolean invalid = FALSE;
     double minValueTdb = 0,maxValueTdb = NO_VALUE;
     colonPairToDoubles(setting,&minValueTdb,&maxValueTdb);
     double minLimit=NO_VALUE,maxLimit=NO_VALUE,min=minValueTdb,max=maxValueTdb;
     colonPairToDoubles(defaultLimits,&minLimit,&maxLimit);
-    getScoreFloatRangeFromCart(cart,tdb,filter,&minLimit,&maxLimit,&min,&max);
+    getScoreFloatRangeFromCart(cart,tdb,FALSE,filter,&minLimit,&maxLimit,&min,&max);
     if((int)minLimit != NO_VALUE || (int)maxLimit != NO_VALUE)
         {
         // assume tdb default values within range! (don't give user errors that have no consequence)
         if((min != minValueTdb && (((int)minLimit != NO_VALUE && min < minLimit)
                                 || ((int)maxLimit != NO_VALUE && min > maxLimit)))
         || (max != maxValueTdb && (((int)minLimit != NO_VALUE && max < minLimit)
                                 || ((int)maxLimit != NO_VALUE && max > maxLimit))))
             {
             invalid = TRUE;
             char value[64];
             if((int)max == NO_VALUE) // min only is allowed, but max only is not
                 safef(value, sizeof(value), "entered minimum (%g)", min);
             else
                 safef(value, sizeof(value), "entered range (min:%g and max:%g)", min, max);
             char limits[64];
@@ -5385,49 +5422,49 @@
 ||  sameWord("gappedPeak",tdb->type))
     {
     return (trackDbSettingClosestToHome(tdb, SCORE_FILTER )
         ||  trackDbSettingClosestToHome(tdb, SIGNAL_FILTER)
         ||  trackDbSettingClosestToHome(tdb, PVALUE_FILTER)
         ||  trackDbSettingClosestToHome(tdb, QVALUE_FILTER)
         ||  trackDbSettingClosestToHome(tdb, SCORE_FILTER ));
     }
     return FALSE;
 }
 
 
 void encodePeakCfgUi(struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed)
 /* Put up UI for filtering wgEnocde peaks based on score, Pval and Qval */
 {
-boolean compositeLevel = isNameAtCompositeLevel(tdb,name);
+boolean viewLevel = isNameAtCompositeLevel(tdb,name);
 boolean opened = FALSE;
-showScoreFilter(cart,tdb,&opened,boxed,compositeLevel,name,title,"Minimum Signal value",     SIGNAL_FILTER,TRUE);
-showScoreFilter(cart,tdb,&opened,boxed,compositeLevel,name,title,"Minimum P-Value (<code>-log<sub>10</sub></code>)",PVALUE_FILTER,TRUE);
-showScoreFilter(cart,tdb,&opened,boxed,compositeLevel,name,title,"Minimum Q-Value (<code>-log<sub>10</sub></code>)",QVALUE_FILTER,TRUE);
+showScoreFilter(cart,tdb,&opened,boxed,viewLevel,name,title,"Minimum Signal value",     SIGNAL_FILTER,TRUE);
+showScoreFilter(cart,tdb,&opened,boxed,viewLevel,name,title,"Minimum P-Value (<code>-log<sub>10</sub></code>)",PVALUE_FILTER,TRUE);
+showScoreFilter(cart,tdb,&opened,boxed,viewLevel,name,title,"Minimum Q-Value (<code>-log<sub>10</sub></code>)",QVALUE_FILTER,TRUE);
 
 char *setting = trackDbSettingClosestToHomeOrDefault(tdb, SCORE_FILTER,NULL);//"0:1000");
 if(setting)
     {
     if(!opened)
         {
         boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
         puts("<TABLE>");
         opened = TRUE;
         }
     char varName[256];
     int minLimit=0,maxLimit=1000,minVal=0,maxVal=NO_VALUE;
     colonPairToInts(setting,&minVal,&maxVal);
-    getScoreIntRangeFromCart(cart,tdb,SCORE_FILTER,&minLimit,&maxLimit,&minVal,&maxVal);
+    getScoreIntRangeFromCart(cart,tdb,viewLevel,SCORE_FILTER,&minLimit,&maxLimit,&minVal,&maxVal);
     if(maxVal != NO_VALUE)
         puts("<TR><TD align='right'><B>Score range: min:</B><TD align='left'>");
     else
         puts("<TR><TD align='right'><B>Minimum score:</B><TD align='left'>");
     safef(varName, sizeof(varName), "%s%s", SCORE_FILTER, _BY_RANGE);
     boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, varName);
     safef(varName, sizeof(varName), "%s.%s%s", name, SCORE_FILTER, (filterByRange?_MIN:""));
     cgiMakeIntVarWithLimits(varName, minVal, "Minimum score", 0, minLimit, maxLimit);
     if(filterByRange)
         {
         if(maxVal == NO_VALUE)
             maxVal = maxLimit;
         puts("<TD align='right'>to<TD align='left'>");
         safef(varName, sizeof(varName), "%s.%s%s", name, SCORE_FILTER,_MAX);
         cgiMakeIntVarWithLimits(varName, maxVal, "Maximum score", 0, minLimit, maxLimit);
@@ -5439,37 +5476,37 @@
         scoreGrayLevelCfgUi(cart, tdb, name, 1000);
         puts("</TR>");
         }
     }
 if(opened)
     {
     puts("</TABLE>");
     cfgEndBox(boxed);
     }
 }
 
 void genePredCfgUi(struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed)
 /* Put up gencode-specific controls */
 {
 char varName[64];
-boolean compositeLevel = isNameAtCompositeLevel(tdb,name);
-char *geneLabel = cartUsualStringClosestToHome(cart, tdb,compositeLevel, "label", "gene");
+boolean viewLevel = isNameAtCompositeLevel(tdb,name);
+char *geneLabel = cartUsualStringClosestToHome(cart, tdb,viewLevel, "label", "gene");
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
 if (sameString(name, "acembly"))
     {
-    char *acemblyClass = cartUsualStringClosestToHome(cart,tdb,compositeLevel,"type", acemblyEnumToString(0));
+    char *acemblyClass = cartUsualStringClosestToHome(cart,tdb,viewLevel,"type", acemblyEnumToString(0));
     printf("<p><b>Gene Class: </b>");
     acemblyDropDown("acembly.type", acemblyClass);
     printf("  ");
     }
 else if(startsWith("wgEncodeGencode", name)
      || sameString("wgEncodeSangerGencode", name)
      || (startsWith("encodeGencode", name) && !sameString("encodeGencodeRaceFrags", name)))
     {
     printf("<B>Label:</B> ");
     safef(varName, sizeof(varName), "%s.label", name);
     cgiMakeRadioButton(varName, "gene", sameString("gene", geneLabel));
     printf("%s ", "gene");
     cgiMakeRadioButton(varName, "accession", sameString("accession", geneLabel));
     printf("%s ", "accession");
     cgiMakeRadioButton(varName, "both", sameString("both", geneLabel));
@@ -5494,161 +5531,130 @@
 
 filterBy_t *filterBySet = filterBySetGet(tdb,cart,name);
 if(filterBySet != NULL)
     {
     printf("<BR>");
     filterBySetCfgUi(cart,tdb,filterBySet,FALSE);
     filterBySetFree(&filterBySet);
     }
 
 cfgEndBox(boxed);
 }
 
 static boolean isSpeciesOn(struct cart *cart, struct trackDb *tdb, char *species, char *option, int optionSize, boolean defaultState)
 /* check the cart to see if species is turned off or on (default is defaultState) */
 {
-#ifdef SUBTRACK_CFG
-        // FIXME: works now in configByAjax but affects not seen in track image!!!
-boolean compositeLevel = isNameAtCompositeLevel(tdb,option);
+boolean viewLevel = isNameAtCompositeLevel(tdb,option);
 if (*option == '\0')
     safef(option, optionSize, "%s.%s", tdb->track, species);
 else
     {
     char *suffix = option + strlen(option);
     int suffixSize = optionSize - strlen(option);
     safef(suffix,suffixSize,".%s",species);
     }
-return cartUsualBooleanClosestToHome(cart,tdb, compositeLevel, species,defaultState);
-#else///ifndef SUBTRACK_CFG
-boolean ret = defaultState;
-safef(option, optionSize, "%s.%s", tdb->track, species);
-
-/* see if this is a simple multiz (not composite track) */
-char *s = cartOptionalString(cart, option);
-if (s != NULL)
-    ret =  (sameString(s, "on") || atoi(s) > 0);
-else
-    {
-    /* check parent to see if it has these variables */
-    if (tdb->parent != NULL)
-	{
-	char *viewString;
-	if (subgroupFind(tdb, "view", &viewString))
-	    {
-	    safef(option, optionSize, "%s.%s.%s",
-		tdb->parent->track, viewString,  species);
-	    ret = cartUsualBoolean(cart, option, ret);
-	    }
-	}
-    }
-
-return ret;
-#endif///ndef SUBTRACK_CFG
+return cartUsualBooleanClosestToHome(cart,tdb, viewLevel, species,defaultState);
 }
 
-char **wigMafGetSpecies(struct cart *cart, struct trackDb *tdb, char *db, struct wigMafSpecies **list, int *groupCt)
+char **wigMafGetSpecies(struct cart *cart, struct trackDb *tdb, char *prefix, char *db, struct wigMafSpecies **list, int *groupCt)
 {
 int speciesCt = 0;
-char *speciesGroup = trackDbSettingClosestToHome(tdb, SPECIES_GROUP_VAR);
-char *speciesUseFile = trackDbSettingClosestToHome(tdb, SPECIES_USE_FILE);
-char *speciesOrder = trackDbSettingClosestToHome(tdb, SPECIES_ORDER_VAR);
+char *speciesGroup   = trackDbSetting(tdb, SPECIES_GROUP_VAR);
+char *speciesUseFile = trackDbSetting(tdb, SPECIES_USE_FILE);
+char *speciesOrder   = trackDbSetting(tdb, SPECIES_ORDER_VAR);
 char sGroup[24];
 //Ochar *groups[20];
 struct wigMafSpecies *wmSpecies, *wmSpeciesList = NULL;
 int group;
 int i;
 #define MAX_SP_SIZE 2000
 #define MAX_GROUPS 20
 char *species[MAX_SP_SIZE];
 char option[MAX_SP_SIZE];
 
 /* determine species and groups for pairwise -- create checkboxes */
 if (speciesOrder == NULL && speciesGroup == NULL && speciesUseFile == NULL)
     {
     if (isCustomTrack(tdb->track))
 	return NULL;
     errAbort("Track %s missing required trackDb setting: speciesOrder, speciesGroups, or speciesUseFile", tdb->track);
     }
 
 char **groups = needMem(MAX_GROUPS * sizeof (char *));
 *groupCt = 1;
 if (speciesGroup)
     *groupCt = chopByWhite(speciesGroup, groups, MAX_GROUPS);
 
 if (speciesUseFile)
     {
     if ((speciesGroup != NULL) || (speciesOrder != NULL))
 	errAbort("Can't specify speciesUseFile and speciesGroup or speciesOrder");
-    speciesOrder = cartGetOrderFromFile(db, cart, speciesUseFile);
-    }
+    speciesOrder = cartGetOrderFromFile(db, cart, speciesUseFile);  // Not sure why this is in cart
+    }                                                               // not tdb based so no ClosestToHome
 
 for (group = 0; group < *groupCt; group++)
     {
     if (*groupCt != 1 || !speciesOrder)
         {
         safef(sGroup, sizeof sGroup, "%s%s",
                                 SPECIES_GROUP_PREFIX, groups[group]);
         speciesOrder = trackDbRequiredSetting(tdb, sGroup);
         }
     speciesCt = chopLine(speciesOrder, species);
     for (i = 0; i < speciesCt; i++)
         {
         AllocVar(wmSpecies);
         wmSpecies->name = cloneString(species[i]);
-#ifdef SUBTRACK_CFG
-        *option = '\0'; // signal to look for the lowest level
-#endif///def SUBTRACK_CFG
+        safecpy(option,sizeof option,prefix);
 	wmSpecies->on = isSpeciesOn(cart, tdb, wmSpecies->name, option, sizeof option, TRUE);
         wmSpecies->group = group;
         slAddHead(&wmSpeciesList, wmSpecies);
         }
     }
 slReverse(&wmSpeciesList);
 *list = wmSpeciesList;
 
 return groups;
 }
 
 
 struct wigMafSpecies * wigMafSpeciesTable(struct cart *cart,
     struct trackDb *tdb, char *name, char *db)
 {
 int groupCt;
 #define MAX_SP_SIZE 2000
 char option[MAX_SP_SIZE];
 int group, prevGroup;
 int i,j;
+boolean viewLevel = isNameAtCompositeLevel(tdb,name);
 
 bool lowerFirstChar = TRUE;
-char *speciesTarget = trackDbSetting(tdb, SPECIES_TARGET_VAR);
-char *speciesTree = trackDbSetting(tdb, SPECIES_TREE_VAR);
 
 struct wigMafSpecies *wmSpeciesList;
-char **groups = wigMafGetSpecies(cart, tdb, db, &wmSpeciesList, &groupCt);
+char **groups = wigMafGetSpecies(cart, tdb, name, db, &wmSpeciesList, &groupCt);
 struct wigMafSpecies *wmSpecies = wmSpeciesList;
 struct slName *speciesList = NULL;
 
 for(; wmSpecies; wmSpecies = wmSpecies->next)
     {
     struct slName *newName = slNameNew(wmSpecies->name);
     slAddHead(&speciesList, newName);
     //printf("%s<BR>\n",speciesList->name);
     }
 slReverse(&speciesList);
 
 int numberPerRow;
-struct phyloTree *tree;
 boolean lineBreakJustPrinted;
 char trackName[255];
 char query[256];
 char **row;
 struct sqlConnection *conn;
 struct sqlResult *sr;
 char *words[MAX_SP_SIZE];
 int defaultOffSpeciesCnt = 0;
 
 if(cartOptionalString(cart, "ajax") == NULL)
     jsIncludeFile("utils.js",NULL);
 //jsInit();
 puts("\n<P><B>Species selection:</B>&nbsp;");
 
 PLUS_BUTTON( "id", "plus_pw","cb_maf_","_maf_");
@@ -5660,56 +5666,62 @@
 char *defaultOffSpecies = trackDbSetting(tdb, "speciesDefaultOff");
 struct hash *offHash = NULL;
 if (defaultOffSpecies)
     {
     offHash = newHash(5);
     DEFAULT_BUTTON( "id", "default_pw","cb_maf_","_maf_");
     int wordCt = chopLine(defaultOffSpecies, words);
     defaultOffSpeciesCnt = wordCt;
 
     /* build hash of species that should be off */
     int ii;
     for(ii=0; ii < wordCt; ii++)
         hashAdd(offHash, words[ii], NULL);
     }
 
+#define BRANEY_SAYS_USETARG_IS_OBSOLETE
+#ifndef BRANEY_SAYS_USETARG_IS_OBSOLETE
+char *speciesTarget = trackDbSetting(tdb, SPECIES_TARGET_VAR);
+char *speciesTree = trackDbSetting(tdb, SPECIES_TREE_VAR);
+struct phyloTree *tree;
 if ((speciesTree != NULL) && ((tree = phyloParseString(speciesTree)) != NULL))
     {
     char buffer[128];
     char *nodeNames[512];
     int numNodes = 0;
     char *path, *orgName;
     int ii;
 
     safef(buffer, sizeof(buffer), "%s.vis",name);
-    cartMakeRadioButton(cart, buffer,"useTarg", "useTarg");
+    cartMakeRadioButton(cart, buffer,"useTarg", "useTarg");// not closestToHome because BRANEY_SAYS_USETARG_IS_OBSOLETE
     printf("Show shortest path to target species:  ");
     path = phyloNodeNames(tree);
     numNodes = chopLine(path, nodeNames);
     for(ii=0; ii < numNodes; ii++)
         {
         if ((orgName = hOrganism(nodeNames[ii])) != NULL)
             nodeNames[ii] = orgName;
         nodeNames[ii][0] = toupper(nodeNames[ii][0]);
         }
 
     cgiMakeDropList(SPECIES_HTML_TARGET, nodeNames, numNodes,
-	cartUsualString(cart, SPECIES_HTML_TARGET, speciesTarget));
+	cartUsualString(cart, SPECIES_HTML_TARGET, speciesTarget));// not closestToHome because BRANEY_SAYS_USETARG_IS_OBSOLETE
     puts("<br>");
-    cartMakeRadioButton(cart,buffer,"useCheck", "useTarg");
+    cartMakeRadioButton(cart,buffer,"useCheck", "useTarg");   // not closestToHome because BRANEY_SAYS_USETARG_IS_OBSOLETE
     printf("Show all species checked : ");
     }
+#endif///ndef BRANEY_SAYS_USETARG_IS_OBSOLETE
 
 if (groupCt == 1)
     puts("\n<TABLE><TR>");
 group = -1;
 lineBreakJustPrinted = FALSE;
 for (wmSpecies = wmSpeciesList, i = 0, j = 0; wmSpecies != NULL;
 		    wmSpecies = wmSpecies->next, i++)
     {
     char *label;
     prevGroup = group;
     group = wmSpecies->group;
     if (groupCt != 1 && group != prevGroup)
 	{
 	i = 0;
 	j = 0;
@@ -5771,180 +5783,168 @@
         /* test if the entry actually is part of the specific maf track data */
         if (chp != NULL)
             {
             *chp = '\0';
             safef(query, sizeof(query),
             "select id from %sMsa where id = 'ss.%s'", trackName, label);
 
             conn = hAllocConn(db);
             sr = sqlGetResult(conn, query);
             row = sqlNextRow(sr);
 
             /* offer it only if the entry is found in current maf data set */
             if (row != NULL)
                 {
                 puts("<TD>");
-               cgiMakeCheckBoxWithId(option, cartUsualBoolean(cart, option, checked),id);
+               cgiMakeCheckBoxWithId(option,cartUsualBooleanClosestToHome(
+                                     cart, tdb, viewLevel,wmSpecies->name, checked),id);
                 printf ("%s", label);
                 puts("</TD>");
                 fflush(stdout);
                 lineBreakJustPrinted = FALSE;
                 j++;
                 }
             sqlFreeResult(&sr);
             hFreeConn(&conn);
             }
         }
     else
     	{
     	puts("<TD>");
 	boolean defaultState = TRUE;
 	if (offHash != NULL)
 	    defaultState = (hashLookup(offHash, wmSpecies->name) == NULL);
-#ifdef SUBTRACK_CFG
         safecpy(option, sizeof(option), name);
-#endif///def SUBTRACK_CFG
         wmSpecies->on = isSpeciesOn(cart, tdb, wmSpecies->name, option, sizeof option, defaultState );
         cgiMakeCheckBoxWithId(option, wmSpecies->on,id);
     	label = hOrganism(wmSpecies->name);
     	if (label == NULL)
 		label = wmSpecies->name;
         if (lowerFirstChar)
             *label = tolower(*label);
     	printf ("%s<BR>", label);
     	puts("</TD>");
         lineBreakJustPrinted = FALSE;
         j++;
         }
     }
 puts("</TR></TABLE><BR>\n");
 return wmSpeciesList;
 }
 
 void wigMafCfgUi(struct cart *cart, struct trackDb *tdb,char *name, char *title, boolean boxed, char *db)
 /* UI for maf/wiggle track
  * NOTE: calls wigCfgUi */
 {
 bool lowerFirstChar = TRUE;
 int i;
 char option[MAX_SP_SIZE];
-char *viewString = NULL;
-
-// FIXME: wigMaf is using non-standard view level naming methods so isn't configurable by ajax
-#ifndef SUBTRACK_CFG
-subgroupFind(tdb, "view", &viewString);
-#endif///ndef SUBTRACK_CFG
+boolean viewLevel = isNameAtCompositeLevel(tdb,name);
 
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
 char *defaultCodonSpecies = trackDbSetting(tdb, SPECIES_CODON_DEFAULT);
 char *framesTable = trackDbSetting(tdb, "frames");
 char *firstCase = trackDbSetting(tdb, ITEM_FIRST_CHAR_CASE);
 if (firstCase != NULL)
     {
     if (sameWord(firstCase, "noChange")) lowerFirstChar = FALSE;
     }
 char *treeImage = NULL;
 struct consWiggle *consWig, *consWiggles = wigMafWiggles(db, tdb);
 
 
 boolean isWigMafProt = FALSE;
 
 if (strstr(tdb->type, "wigMafProt")) isWigMafProt = TRUE;
 
 puts("<TABLE><TR><TD VALIGN=\"TOP\">");
 
 if (consWiggles && consWiggles->next)
     {
     /* check for alternate conservation wiggles -- create checkboxes */
     puts("<P STYLE=\"margin-top:10;\"><B>Conservation:</B>" );
     boolean first = TRUE;
     for (consWig = consWiggles; consWig != NULL; consWig = consWig->next)
         {
-        char *wigVar = wigMafWiggleVar(tdb, consWig);
-        cgiMakeCheckBox(wigVar, cartUsualBoolean(cart, wigVar, first));
+        char *wigVar = wigMafWiggleVar(name, consWig);
+        char *wigVarSuffix = wigVar + strlen(name) + 1;
+        cgiMakeCheckBox(wigVar,
+                        cartUsualBooleanClosestToHome(cart, tdb, viewLevel, wigVarSuffix, first));
         first = FALSE;
         subChar(consWig->uiLabel, '_', ' ');
         printf ("%s&nbsp;", consWig->uiLabel);
         }
     }
 
 struct wigMafSpecies *wmSpeciesList = wigMafSpeciesTable(cart, tdb, name, db);
 struct wigMafSpecies *wmSpecies;
 
 if (isWigMafProt)
     puts("<B>Multiple alignment amino acid-level:</B><BR>" );
 else
     puts("<B>Multiple alignment base-level:</B><BR>" );
 
-if (viewString != NULL)
-    safef(option, sizeof option, "%s.%s.%s", name, viewString, MAF_DOT_VAR);
-else
     safef(option, sizeof option, "%s.%s", name, MAF_DOT_VAR);
-cgiMakeCheckBox(option, cartCgiUsualBoolean(cart, option, FALSE));
+cgiMakeCheckBox(option, cartUsualBooleanClosestToHome(cart, tdb, viewLevel,MAF_DOT_VAR, FALSE));
 
 if (isWigMafProt)
     puts("Display amino acids identical to reference as dots<BR>" );
 else
     puts("Display bases identical to reference as dots<BR>" );
 
-if (viewString != NULL)
-    safef(option, sizeof option, "%s.%s.%s", name, viewString, MAF_CHAIN_VAR);
-else
     safef(option, sizeof option, "%s.%s", name, MAF_CHAIN_VAR);
-cgiMakeCheckBox(option, cartCgiUsualBoolean(cart, option, TRUE));
+cgiMakeCheckBox(option, cartUsualBooleanClosestToHome(cart, tdb, viewLevel, MAF_CHAIN_VAR, TRUE));
 
 char *irowStr = trackDbSetting(tdb, "irows");
 boolean doIrows = (irowStr == NULL) || !sameString(irowStr, "off");
 if (isCustomTrack(tdb->track) || doIrows)
     puts("Display chains between alignments<BR>");
 else
     {
     if (isWigMafProt)
 	puts("Display unaligned amino acids with spanning chain as 'o's<BR>");
     else
 	puts("Display unaligned bases with spanning chain as 'o's<BR>");
     }
 
 safef(option, sizeof option, "%s.%s", name, "codons");
 if (framesTable)
     {
     char *nodeNames[512];
     char buffer[128];
 
     printf("<BR><B>Codon Translation:</B><BR>");
     printf("Default species to establish reading frame: ");
     nodeNames[0] = db;
     for (wmSpecies = wmSpeciesList, i = 1; wmSpecies != NULL;
 			wmSpecies = wmSpecies->next, i++)
 	{
 	nodeNames[i] = wmSpecies->name;
 	}
     cgiMakeDropList(SPECIES_CODON_DEFAULT, nodeNames, i,
-	cartUsualString(cart, SPECIES_CODON_DEFAULT, defaultCodonSpecies));
+	cartUsualString(cart, SPECIES_CODON_DEFAULT, defaultCodonSpecies)); // tdb independent var
     puts("<br>");
-    if (viewString != NULL)
-	safef(buffer, sizeof(buffer), "%s.%s.codons",name, viewString);
-    else
+    char *cartVal = cartUsualStringClosestToHome(cart, tdb, viewLevel, "codons","codonDefault");
 	safef(buffer, sizeof(buffer), "%s.codons",name);
-    cartMakeRadioButton(cart, buffer,"codonNone", "codonDefault");
+    cgiMakeRadioButton(buffer,"codonNone",     sameWord(cartVal,"codonNone"));
     printf("No codon translation<BR>");
-    cartMakeRadioButton(cart, buffer,"codonDefault", "codonDefault");
+    cgiMakeRadioButton(buffer,"codonDefault",  sameWord(cartVal,"codonDefault"));
     printf("Use default species reading frames for translation<BR>");
-    cartMakeRadioButton(cart, buffer,"codonFrameNone", "codonDefault");
+    cgiMakeRadioButton(buffer,"codonFrameNone",sameWord(cartVal,"codonFrameNone"));
     printf("Use reading frames for species if available, otherwise no translation<BR>");
-    cartMakeRadioButton(cart, buffer,"codonFrameDef", "codonDefault");
+    cgiMakeRadioButton(buffer,"codonFrameDef", sameWord(cartVal,"codonFrameDef"));
     printf("Use reading frames for species if available, otherwise use default species<BR>");
     }
 else
     {
     /* Codon highlighting does not apply to wigMafProt type */
     if (!strstr(tdb->type, "wigMafProt"))
 	{
     	puts("<P><B>Codon highlighting:</B><BR>" );
 
 #ifdef GENE_FRAMING
 
     	safef(option, sizeof(option), "%s.%s", name, MAF_FRAME_VAR);
     	char *currentCodonMode = cartCgiUsualString(cart, option, MAF_FRAME_GENE);
 
     	/* Disable codon highlighting */
@@ -6137,85 +6137,84 @@
 {
 char *visibility = NULL;
 compositeViewCfgExpandedByDefault(parentTdb,view,&visibility);
 enum trackVisibility vis = hTvFromString(visibility);
 freeMem(visibility);
 return vis;
 }
 
 static boolean hCompositeDisplayViewDropDowns(char *db, struct cart *cart, struct trackDb *parentTdb)
 /* UI for composite view drop down selections. */
 {
 int ix;
 char varName[SMALLBUF];
 char classes[SMALLBUF];
 char javascript[JBUFSIZE];
-#define CFG_LINK  "<B><A HREF=\"#a_cfg_%s\" onclick=\"return (showConfigControls('%s') == false);\" title=\"%s Configuration\">%s</A><INPUT TYPE=HIDDEN NAME='%s.%s.showCfg' value='%s'></B>"
-#define MAKE_CFG_LINK(name,title,tbl,open) printf(CFG_LINK, (name),(name),(title),(title),(tbl),(name),((open)?"on":"off"))
+#define CFG_LINK  "<B><A HREF=\"#a_cfg_%s\" onclick=\"return (showConfigControls('%s') == false);\" title=\"%s Configuration\">%s</A><INPUT TYPE=HIDDEN NAME='%s.showCfg' value='%s'></B>"
+#define MAKE_CFG_LINK(name,title,viewTrack,open) printf(CFG_LINK, (name),(name),(title),(title),(viewTrack),((open)?"on":"off"))
 
 membersForAll_t *membersForAll = membersForAllSubGroupsGet(parentTdb, cart); // membersForAll is generated once per track, then cached
 members_t *membersOfView = membersForAll->members[dimV];
 if(membersOfView == NULL)
     return FALSE;
 
 char configurable[membersOfView->count];
 memset(configurable,cfgNone,sizeof(configurable));
 int firstOpened = -1;
 boolean makeCfgRows = FALSE;
 struct trackDb **matchedViewTracks = needMem(sizeof(struct trackDb *)*membersOfView->count);
 
 for (ix = 0; ix < membersOfView->count; ix++)
     {
-    char *viewName = membersOfView->tags[ix];
     if (membersOfView->subtrackList     != NULL
     &&  membersOfView->subtrackList[ix] != NULL)
         {
         struct trackDb *subtrack = membersOfView->subtrackList[ix]->val;
         matchedViewTracks[ix] = subtrack->parent;
         configurable[ix] = (char)cfgTypeFromTdb(subtrack, TRUE);
         if(configurable[ix] != cfgNone)
             {
             if(firstOpened == -1)
                 {
-                safef(varName, sizeof(varName), "%s.%s.showCfg", parentTdb->track, viewName);
-                if(cartUsualBoolean(cart,varName,FALSE))
+                safef(varName, sizeof(varName), "%s.showCfg", matchedViewTracks[ix]->track);
+                if(cartUsualBoolean(cart,varName,FALSE)) // No need for closestToHome: view level input
                     firstOpened = ix;
                 }
             makeCfgRows = TRUE;
             }
         }
     }
 
 toLowerN(membersOfView->groupTitle, 1);
 printf("<B>Select %s</B> (<A HREF='../goldenPath/help/multiView.html' title='Help on views' TARGET=_BLANK>help</A>):\n", membersOfView->groupTitle);
 printf("<TABLE><TR style='text-align:left;'>\n");
 // Make row of vis drop downs
 for (ix = 0; ix < membersOfView->count; ix++)
     {
     char *viewName = membersOfView->tags[ix];
     if (matchedViewTracks[ix] != NULL)
         {
         printf("<TD>");
         if(configurable[ix] != cfgNone)
             {
-            MAKE_CFG_LINK(membersOfView->tags[ix],membersOfView->titles[ix],parentTdb->track,(firstOpened == ix));
+            MAKE_CFG_LINK(membersOfView->tags[ix],membersOfView->titles[ix],matchedViewTracks[ix]->track,(firstOpened == ix));
             }
         else
             printf("<B>%s</B>",membersOfView->titles[ix]);
         puts("</TD>");
 
-        safef(varName, sizeof(varName), "%s.%s.vis", parentTdb->track, viewName);
+        safef(varName, sizeof(varName), "%s.%s.vis", parentTdb->track, viewName);  // FIXME: Time to get rid of "track.view.vis" since viewInTheMiddle
         enum trackVisibility tv =
             hTvFromString(cartUsualString(cart, varName,hStringFromTv(visCompositeViewDefault(parentTdb,viewName))));
 
         safef(javascript, sizeof(javascript), "onchange=\"matSelectViewForSubTracks(this,'%s');\" onfocus='this.lastIndex=this.selectedIndex;'", viewName);
 
         printf("<TD>");
         safef(classes, sizeof(classes), "viewDD normalText %s", membersOfView->tags[ix]);
         hTvDropDownClassWithJavascript(varName, tv, parentTdb->canPack,classes,javascript);
         puts(" &nbsp; &nbsp; &nbsp;</TD>");
         }
     }
 puts("</TR>");
 
 // Make row of cfg boxes if needed
 if(makeCfgRows)
@@ -6224,37 +6223,36 @@
     for (ix = 0; ix < membersOfView->count; ix++)
         {
         struct trackDb *view = matchedViewTracks[ix];
         if (view != NULL)
             {
             char *viewName = membersOfView->tags[ix];
             printf("<TR id=\"tr_cfg_%s\"", viewName);
             if((firstOpened == -1 && !compositeViewCfgExpandedByDefault(parentTdb,membersOfView->tags[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(varName, sizeof(varName), "%s", view->track);
             if(configurable[ix] != cfgNone)
-                {
-                cfgByCfgType(configurable[ix],db,cart,view->subtracks,varName,
+                {                                  // Hint: subtrack is model but named for view
+                cfgByCfgType(configurable[ix],db,cart,view->subtracks,view->track,
                         membersOfView->titles[ix],TRUE);
             #ifndef SUBTRACK_CFG
-                cfgLinkToDependentCfgs(cart,parentTdb,varName);
+                cfgLinkToDependentCfgs(cart,parentTdb,view->track);
             #endif///ndef SUBTRACK_CFG
                 }
             }
         }
     }
 puts("</TABLE>");
 freeMem(matchedViewTracks);
 return TRUE;
 }
 
 char *compositeLabelWithVocabLink(char *db,struct trackDb *parentTdb, struct trackDb *childTdb,
 	char *vocabType, char *label)
 /* If the parentTdb has a controlledVocabulary setting and the vocabType is found,
    then label will be wrapped with the link to display it.  Return string is cloned. */
 {