1bda3889beb9b9247998d9153dee94ca7d13810c
braney
  Sun Nov 10 16:16:48 2019 -0800
allow yet another way to specify bigBed filters.  This allows
specifications of the form "filter*.fieldName"

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 3891014..1679f97 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -3621,121 +3621,130 @@
             if (val == filterBy->slValues) // First one
                 filterBy->valueAndLabel = TRUE;
             if (filterBy->valueAndLabel == FALSE)
                 errAbort("filterBy values either all have labels (as value|label) "
                          "or none do.");
             *chipper++ = 0;  // The label is found inside 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.");
         }
     }
 }
 
-char *extractFieldName(char *cartVariable, char *filterType)
-/* Extract field name from a filter cart variable.  Variables can either be
+char *extractFieldNameNew(char *trackDbVariable, char *filterType)
+/* Extract field name from a filter trackDb variable.  Variables are filter*.column */
+{
+char *ptr = strchr(trackDbVariable, '.');
+if (ptr == NULL)
+    errAbort("%s doesn't have a '.' in it", trackDbVariable);
+
+return ptr + 1;
+}
+
+char *extractFieldNameOld(char *trackDbVariable, char *filterType)
+/* Extract field name from a filter trackDb variable.  Variables can either be
  * <columnName>Filter* or <columnName>.Filter* */
 {
-char *field = cloneString(cartVariable);
+char *field = cloneString(trackDbVariable);
 int ix = strlen(field) - strlen(filterType); 
 field[ix] = '\0';
 if (field[ix - 1] == '.')
     field[ix - 1] = '\0';
 
 return field;
 }
 
 static char *getLabelSetting(struct cart *cart, struct trackDb *tdb, char *field)
 {
 char labelSetting[4096];
 safef(labelSetting, sizeof labelSetting, "%s%s", field, FILTER_LABEL_NAME);
 char *trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL);
 if (trackDbLabel == NULL)
     {
     safef(labelSetting, sizeof labelSetting, "%s.%s", field, FILTER_LABEL_NAME);
     trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL);
     }
 return trackDbLabel;
 }
 
-filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, char *filterName, char *name)
+static filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, struct trackDbFilter *tdbFilter, char *name)
 /* Build a filterBy_t structure from a <column>FilterValues statement. */
 {
-char *field = extractFieldName(filterName, FILTER_VALUES_NAME);
-char *setting = trackDbSetting(tdb, filterName);
-if (isEmpty(setting))
-    errAbort("FilterValues setting of field '%s' must have a value.", field);
+char *field = tdbFilter->fieldName;
+if (isEmpty(tdbFilter->setting))
+    errAbort("FilterValues setting of field '%s' must have a value.", tdbFilter->fieldName);
 
-char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filterName, setting);
+char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, tdbFilter->name, tdbFilter->setting);
 
 filterBy_t *filterBy;
 AllocVar(filterBy);
-filterBy->column = field;
-filterBy->title = field; ///  title should come from AS file, or trackDb variable
+filterBy->column = cloneString(field);
+filterBy->title = cloneString(field); ///  title should come from AS file, or trackDb variable
 struct asColumn *asCol = asColumnFind(as, field);
 if (asCol != NULL)
     filterBy->title = asCol->comment;
 else
     errAbort("Building filter on field %s which is not in AS file.", field);
 char *trackDbLabel = getLabelSetting(cart, tdb, field);
 if (trackDbLabel)
     filterBy->title = trackDbLabel;
 filterBy->useIndex = FALSE;
 filterBy->slValues = slNameListFromCommaEscaped(value);
 chopUpValues(filterBy);
 if (cart != NULL)
     {
     char suffix[256];
     safef(suffix, sizeof(suffix), "%s.%s", "filterBy", filterBy->column);
     boolean parentLevel = isNameAtParentLevel(tdb,tdb->track);
     if (cartLookUpVariableClosestToHome(cart,tdb,parentLevel,suffix,&(filterBy->htmlName)))
         {
         filterBy->slChoices = cartOptionalSlNameList(cart,filterBy->htmlName);
         freeMem(filterBy->htmlName);
         }
     }
 
 struct dyString *dy = newDyString(128);
 dyStringPrintf(dy, "%s.%s.%s", name, "filterBy", filterBy->column);
 filterBy->htmlName = dy->string;
 
 return filterBy;
 }
 
-filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct slName *filterValues, char *name)
+filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct trackDbFilter *trackDbFilters, char *name)
 /* Build a filterBy_t list from tdb variables of the form *FilterValues */
 {
 struct asObject *as = asForTdb(NULL, tdb);
 filterBy_t *filterByList = NULL, *filter;
-struct slName *fieldFilter;
-while ((fieldFilter = slPopHead(&filterValues)) != NULL)
+struct trackDbFilter *fieldFilter;
+while ((fieldFilter = slPopHead(&trackDbFilters)) != NULL)
     {
-    if ((filter = buildFilterBy(tdb, cart, as, fieldFilter->name, name)) != NULL)
+    if ((filter = buildFilterBy(tdb, cart, as, fieldFilter, name)) != NULL)
         slAddHead(&filterByList, filter);
     }
 return filterByList;
 }
 
 filterBy_t *filterBySetGetGuts(struct trackDb *tdb, struct cart *cart, char *name, char *subName, char *settingName)
 // Gets one or more "filterBy" settings (ClosestToHome).  returns NULL if not found
 {
 // first check to see if this tdb is using "new" FilterValues cart variables
-struct slName *filterValues = trackDbSettingsWildMatch(tdb, FILTER_VALUES_WILDCARD);
-if (filterValues)
-    return filterByValues(tdb, cart, filterValues, name);
+struct trackDbFilter *trackDbFilters = tdbGetTrackFilterByFilters( tdb);
+if (trackDbFilters)
+    return filterByValues(tdb, cart, trackDbFilters, name);
 
 filterBy_t *filterBySet = NULL;
 char *setting = trackDbSettingClosestToHome(tdb, settingName);
 if(setting == NULL)
     return NULL;
 if ( name == NULL )
     name = tdb->track;
 
 setting = cloneString(setting);
 char *filters[10];
 // multiple filterBys are delimited by space but spaces inside filter can be protected "by quotes"
 int filterCount = chopByWhiteRespectDoubleQuotes(setting, filters, ArraySize(filters));
 int ix;
 for (ix=0;ix<filterCount;ix++)
     {
@@ -3957,38 +3966,31 @@
     {
     char *styler = label + strlen(label)+1;
     if (*styler != '\0')
 	{
 	if (*styler == '#') // Legacy: just the color that follows
 	    printf(" style='color: %s;'",styler);
 	else
 	    printf(" style='%s'",styler);
 	}
     }
 printf(">%s</OPTION>\n",label);
 }
 
 static boolean filterByColumnIsMultiple(struct cart *cart, struct trackDb *tdb, char *column)
 {
-char settingString[4096];
-safef(settingString, sizeof settingString, "%s%s", column, FILTER_TYPE_NAME);
-char *setting = cartOrTdbString(cart, tdb, settingString, NULL);
-if (setting == NULL)
-    {
-    safef(settingString, sizeof settingString, "%s.%s", column, FILTER_TYPE_NAME);
-    setting = cartOrTdbString(cart, tdb, settingString, FILTERBY_MULTIPLE_LIST_AND);
-    }
+char *setting =  getFilterType(cart, tdb, column, FILTERBY_MULTIPLE_LIST_AND);
 return (sameString(setting, FILTERBY_MULTIPLE) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_OR) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_AND));
 }
 
 void filterBySetCfgUiGuts(struct cart *cart, struct trackDb *tdb,
 		      filterBy_t *filterBySet, boolean onOneLine,
 		      char *filterTypeTitle, char *selectIdPrefix, char *allLabel, char *prefix)
 // Does the UI for a list of filterBy structure for either filterBy or highlightBy controls
 {
 if (filterBySet == NULL)
     return;
 
 #define FILTERBY_HELP_LINK "<A HREF=\"../goldenPath/help/multiView.html\" TARGET=ucscHelp>help</A>"
 int count = slCount(filterBySet);
@@ -4027,35 +4029,33 @@
         safef(selectStatement, sizeof selectStatement, " (select multiple items - %s)", FILTERBY_HELP_LINK);
     else
         selectStatement[0] = 0;
     if(count == 1)
 	printf("<B>%s by %s</B>%s",filterTypeTitle,filterBy->title,selectStatement);
     else
 	printf("<B>%s</B>",filterBy->title);
     puts("</TD>");
     }
 puts("</tr><tr>");
 for (filterBy = filterBySet;  filterBy != NULL;  filterBy = filterBy->next)
     {
     puts("<td>");
     if (filterByColumnIsMultiple(cart, tdb, filterBy->column) && tdbIsBigBed(tdb))
         {
-        char settingString[4096];
-        safef(settingString, sizeof settingString, "%s%s", filterBy->column, FILTER_TYPE_NAME);
-        char *setting = cartOrTdbString(cart, tdb, settingString, FILTERBY_MULTIPLE_LIST_AND);
+        char *setting =  getFilterType(cart, tdb, filterBy->column, FILTERBY_MULTIPLE_LIST_AND);
         char cartSettingString[4096];
-        safef(cartSettingString, sizeof cartSettingString, "%s.%s", prefix, settingString);
+        safef(cartSettingString, sizeof cartSettingString, "%s.%s.%s", prefix, filterBy->column, FILTER_TYPE_NAME_CAP);
         printf("<div class='advanced' style='display:none'><b>Match if  ");
         cgiMakeRadioButton(cartSettingString, FILTERBY_MULTIPLE_LIST_AND, sameString(setting, FILTERBY_MULTIPLE_LIST_AND));
         printf(" all ");
         cgiMakeRadioButton(cartSettingString, FILTERBY_MULTIPLE_LIST_OR, sameString(setting, FILTERBY_MULTIPLE_LIST_OR));
         printf(" one or more match</b></div> ");
         }
     puts("</td>");
     }
 puts("</tr><tr>");
 int ix=0;
 for (filterBy = filterBySet;  filterBy != NULL;  filterBy = filterBy->next, ix++)
     {
     puts("<td>");
     // value is always "All", even if label is different, to simplify javascript code
     int valIx = 0;
@@ -5832,58 +5832,74 @@
     *min = NULL; // default these outs!
 if (max)
     *max = NULL;
 char *setting = trackDbSettingClosestToHome(tdb, scoreName);
 if (setting)
     {
     if (strchr(setting,':') != NULL)
         return colonPairToStrings(setting,min,max);
     else if (min)
         *min = cloneString(setting);
     return TRUE;
     }
 return FALSE;
 }
 
+char *getScoreNameAdd(struct trackDb *tdb, char *scoreName, char *add)
+// Add a suffix to a filter for more information
+{
+char scoreLimitName[1024];
+char *name = cloneString(scoreName);
+if (tdb->isNewFilterType)
+    {
+    char *dot = strchr(name, '.');
+    *dot++ = 0;
+    safef(scoreLimitName, sizeof(scoreLimitName), "%s%s.%s", name, add, dot);
+    }
+else
+    safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, add);
+return cloneString(scoreLimitName);
+}
+
 static boolean getScoreLimitsFromTdb(struct trackDb *tdb, char *scoreName,char *defaults,
                                      char**min,char**max)
 // returns TRUE if limits exist and sets the string pointer (because they may be float or int)
 // if min or max are set, then they should be freed
 {
 if (min)
     *min = NULL; // default these outs!
 if (max)
     *max = NULL;
-char scoreLimitName[128];
-safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _LIMITS);
+
+char *scoreLimitName = getScoreNameAdd(tdb, scoreName, _LIMITS);
 char *setting = trackDbSettingClosestToHome(tdb, scoreLimitName);
 if (setting)
     {
     return colonPairToStrings(setting,min,max);
     }
 else
     {
     if (min)
         {
-        safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _MIN);
+        scoreLimitName = getScoreNameAdd(tdb, scoreName, _MIN);
         setting = trackDbSettingClosestToHome(tdb, scoreLimitName);
         if (setting)
             *min = cloneString(setting);
         }
     if (max)
         {
-        safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _MAX);
+        scoreLimitName = getScoreNameAdd(tdb, scoreName, _MAX);
         setting = trackDbSettingClosestToHome(tdb, scoreLimitName);
         if (setting)
             *max = cloneString(setting);
         }
     return TRUE;
     }
 if (defaults != NULL && ((min && *min == NULL) || (max && *max == NULL)))
     {
     char *minLoc=NULL;
     char *maxLoc=NULL;
     if (colonPairToStrings(defaults,&minLoc,&maxLoc))
         {
         if (min && *min == NULL && minLoc != NULL)
             *min=minLoc;
         else
@@ -5944,60 +5960,60 @@
 && *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;
 }
 
 void getScoreFloatRangeFromCart(struct cart *cart, struct trackDb *tdb, boolean parentLevel,
                          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);
+    char *scoreLimitName = getScoreNameAdd(tdb, scoreName, _MAX);
+    
     deMax = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreLimitName);
     if (deMax != NULL)
         *max = strtod(deMax,NULL);
     }
 if (min)
     {                                                // name is always {filterName}Min
-    safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _MIN);
+    char *scoreLimitName = getScoreNameAdd(tdb, scoreName, _MIN);
     deMin = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreLimitName);
     if (deMin == NULL)
         deMin = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreName);
     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
@@ -6009,198 +6025,269 @@
                                char *label, char *scoreName)
 // 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);
+    char *filterName = getScoreNameAdd(tdb, scoreName, _BY_RANGE);
+    boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, filterName);
     double minLimit=NO_VALUE,maxLimit=NO_VALUE;
     double minVal=minLimit,maxVal=maxLimit;
     colonPairToDoubles(setting,&minVal,&maxVal);
     getScoreFloatRangeFromCart(cart,tdb,parentLevel,scoreName,&minLimit,&maxLimit,
                                                               &minVal,  &maxVal);
-    safef(varName, sizeof(varName), "%s.%s%s", name, scoreName, filterByRange ? _MIN:"");
+    filterName = getScoreNameAdd(tdb, scoreName, filterByRange ? _MIN:"");
+    safef(varName, sizeof(varName), "%s.%s", name, filterName);
     safef(altLabel, sizeof(altLabel), "%s%s", (filterByRange ? "Minimum " : ""),
           htmlEncode(htmlTextStripTags(label)));
     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);
+        filterName = getScoreNameAdd(tdb, scoreName, _MAX);
+        safef(varName, sizeof(varName), "%s.%s", name, filterName);
         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);
     puts("</TR>");
     return TRUE;
     }
 return FALSE;
 }
 
+struct trackDbFilter *tdbGetTrackFilters( struct trackDb *tdb, char * lowWild, char * lowName, char * capWild, char * capName)
+// figure out which of the ways to specify trackDb filter variables we're using
+// and return the setting
+{
+struct trackDbFilter *trackDbFilterList = NULL;
+struct slName *filterSettings = trackDbSettingsWildMatch(tdb, lowWild);
+
+if (filterSettings)
+    {
+    struct trackDbFilter *tdbFilter;
+    struct slName *filter = NULL;
+    while ((filter = slPopHead(&filterSettings)) != NULL)
+        {
+        tdb->isNewFilterType = TRUE;
+
+        AllocVar(tdbFilter);
+        slAddHead(&trackDbFilterList, tdbFilter);
+        tdbFilter->name = cloneString(filter->name);
+        tdbFilter->setting = trackDbSetting(tdb, filter->name);
+        tdbFilter->fieldName = extractFieldNameNew(filter->name, lowName);
+        }
+    }
+filterSettings = trackDbSettingsWildMatch(tdb, capWild);
+
+if (filterSettings)
+    {
+    struct trackDbFilter *tdbFilter;
+    struct slName *filter = NULL;
+    while ((filter = slPopHead(&filterSettings)) != NULL)
+        {
+        if (differentString(filter->name,NO_SCORE_FILTER))
+            {
+            if (tdb->isNewFilterType)
+                errAbort("browser doesn't support specifying filters in both old and new format.");
+            AllocVar(tdbFilter);
+            slAddHead(&trackDbFilterList, tdbFilter);
+            tdbFilter->name = cloneString(filter->name);
+            tdbFilter->setting = trackDbSetting(tdb, filter->name);
+            tdbFilter->fieldName = extractFieldNameOld(filter->name, capName);
+            }
+        }
+    }
+
+return trackDbFilterList;
+}
+
+struct trackDbFilter *tdbGetTrackNumFilters( struct trackDb *tdb)
+// get the number filters out of trackDb
+{
+return tdbGetTrackFilters( tdb, FILTER_NUMBER_WILDCARD_LOW, FILTER_NUMBER_NAME_LOW, FILTER_NUMBER_WILDCARD_CAP, FILTER_NUMBER_NAME_CAP);
+}
+
+struct trackDbFilter *tdbGetTrackTextFilters( struct trackDb *tdb)
+// get the text filters out of trackDb
+{
+return tdbGetTrackFilters( tdb, FILTER_TEXT_WILDCARD_LOW, FILTER_TEXT_NAME_LOW, FILTER_TEXT_WILDCARD_CAP, FILTER_TEXT_NAME_CAP);
+}
+
+struct trackDbFilter *tdbGetTrackFilterByFilters( struct trackDb *tdb)
+// get the values filters out of trackDb
+{
+return tdbGetTrackFilters( tdb, FILTER_VALUES_WILDCARD_LOW, FILTER_VALUES_NAME_LOW, FILTER_VALUES_WILDCARD_CAP, FILTER_VALUES_NAME_CAP);
+}
 
 static int numericFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean *opened,
                                  boolean boxed, boolean parentLevel,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_NUMBER_WILDCARD);
-if (filterSettings)
+struct trackDbFilter *trackDbFilters = tdbGetTrackNumFilters(tdb);
+if (trackDbFilters)
     {
     puts("<BR>");
-    struct slName *filter = NULL;
+    struct trackDbFilter *filter = NULL;
     struct sqlConnection *conn = NULL;
     if (!isHubTrack(db))
         conn = hAllocConnTrack(db, tdb);
     struct asObject *as = asForTdb(conn, tdb);
     hFreeConn(&conn);
 
-    while ((filter = slPopHead(&filterSettings)) != NULL)
-        {
-        if (differentString(filter->name,NO_SCORE_FILTER))
+    while ((filter = slPopHead(&trackDbFilters)) != NULL)
         {
+        char *field = filter->fieldName;
         char *scoreName = cloneString(filter->name);
-            char *field = extractFieldName(filter->name, FILTER_NUMBER_NAME);
         char *trackDbLabel = getLabelSetting(cart, tdb, field);
 
         if (as != NULL)
             {
             struct asColumn *asCol = asColumnFind(as, field);
             if (asCol != NULL)
                 { // Found label so replace field
                 field = asCol->comment;
                 }
             else 
                 errAbort("Building filter on field %s which is not in AS file.", field);
             }
-            char varName[256];
         char labelBuf[1024];
         char *label = labelBuf;
-            safef(varName, sizeof(varName), "%s%s", scoreName, _BY_RANGE);
-            boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, varName);
+        char *filterName = getScoreNameAdd(tdb, scoreName, _BY_RANGE);
+        boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, filterName);
 
         if (trackDbLabel)
             label = trackDbLabel;
         else
             safef(labelBuf, sizeof(labelBuf),"%s%s", filterByRange ? "": "Minimum ", field);
 
         showScoreFilter(cart,tdb,opened,boxed,parentLevel,name,title,label,scoreName);
-            freeMem(scoreName);
         count++;
         }
-        slNameFree(&filter);
-        }
     if (as != NULL)
         asObjectFree(&as);
     }
 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
 if (trackDbSettingClosestToHome(tdb, FILTER_BY))
     return TRUE;
 if (trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN))
     return TRUE;
 boolean blocked = FALSE;
-struct slName *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_NUMBER_WILDCARD);
+struct trackDbFilter *filterSettings = tdbGetTrackNumFilters( tdb);
+
 if (filterSettings != NULL)
     {
     boolean one = FALSE;
-    struct slName *oneFilter = filterSettings;
+    struct trackDbFilter *oneFilter = filterSettings;
+    char *noScoreFilter = trackDbSetting(tdb, NO_SCORE_FILTER);
+    if (noScoreFilter)
+        blocked = TRUE;
+
     for (;oneFilter != NULL;oneFilter=oneFilter->next)
         {
-        if (sameWord(NO_SCORE_FILTER,oneFilter->name))
-            {
-            blocked = TRUE;
-            continue;
-            }
-        if (differentString(oneFilter->name,SCORE_FILTER)) // scoreFilter is implicit
+        if (differentString(oneFilter->fieldName,"score")) // scoreFilter is implicit
             {                                              // but could be blocked
             one = TRUE;
             break;
             }
         }
-    slNameFreeList(&filterSettings);
     if (one)
         return TRUE;
     }
 if (!blocked)  // scoreFilter is implicit unless NO_SCORE_FILTER
     return TRUE;
 
 return FALSE;
 }
 
 
+char *getFilterType(struct cart *cart, struct trackDb *tdb, char *field, char *def)
+// figure out how the trackDb is specifying the FILTER_TYPE variable and return its setting
+{
+char settingString[4096];
+safef(settingString, sizeof settingString, "%s.%s", FILTER_TYPE_NAME_LOW, field);
+char *setting = cartOrTdbString(cart, tdb, settingString, NULL);
+if (setting == NULL)
+    {
+    safef(settingString, sizeof settingString, "%s%s", field, FILTER_TYPE_NAME_CAP);
+    setting = cartOrTdbString(cart, tdb, settingString, NULL);
+    }
+if (setting == NULL)
+    {
+    safef(settingString, sizeof settingString, "%s.%s", field, FILTER_TYPE_NAME_CAP);
+    setting = cartOrTdbString(cart, tdb, settingString, def);
+    }
+return setting;
+}
+
 static int textFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb)
 /* Show all the text filters for this track. */
 {
 int count = 0;
-struct slName *filter, *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_TEXT_WILDCARD);
-if (filterSettings)
+struct trackDbFilter *trackDbFilters = tdbGetTrackTextFilters(tdb);
+if (trackDbFilters)
     {
+    puts("<BR>");
+    struct trackDbFilter *filter = NULL;
     struct sqlConnection *conn = NULL;
     if (!isHubTrack(db))
         conn = hAllocConnTrack(db, tdb);
-    while ((filter = slPopHead(&filterSettings)) != NULL)
-        {
-        char *setting = trackDbSetting(tdb, filter->name);
-        char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, setting);
-        char *field = extractFieldName(filter->name, FILTER_TEXT_NAME);
     struct asObject *as = asForTdb(conn, tdb);
-        struct asColumn *asCol = asColumnFind(as, field);
+    hFreeConn(&conn);
+    while ((filter = slPopHead(&trackDbFilters)) != NULL)
+        {
+        char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, filter->setting);
+        struct asColumn *asCol = asColumnFind(as, filter->fieldName);
         if (asCol == NULL)
-            errAbort("Building filter on field %s which is not in AS file.", field);
+            errAbort("Building filter on field %s which is not in AS file.", filter->fieldName);
 
         count++;
-        printf("<P><B>Filter items in '%s' field: ", field);
+        printf("<P><B>Filter items in '%s' field: ", filter->fieldName);
 
         char cgiVar[128];
         safef(cgiVar,sizeof(cgiVar),"%s.%s",tdb->track,filter->name);
         cgiMakeTextVar(cgiVar, value, 45);
 
-        char settingString[4096];
-        safef(settingString, sizeof settingString, "%s%s", field, FILTER_TYPE_NAME);
-        setting = cartOrTdbString(cart, tdb, settingString, NULL);
-        if (setting == NULL)
-            {
-            safef(settingString, sizeof settingString, "%s.%s", field, FILTER_TYPE_NAME);
-            setting = cartOrTdbString(cart, tdb, settingString, FILTERTEXT_WILDCARD);
-            }
-        safef(cgiVar,sizeof(cgiVar),"%s.%s",tdb->track,settingString);
+        char *setting = getFilterType(cart, tdb, filter->fieldName, FILTERTEXT_WILDCARD);
+        safef(cgiVar,sizeof(cgiVar),"%s.%s.%s",tdb->track,filter->fieldName, FILTER_TYPE_NAME_CAP);
         printf(" using ");
         printf("<SELECT name='%s'> ", cgiVar);
         printf("<OPTION %s>%s</OPTION>", sameString(setting, FILTERTEXT_WILDCARD) ? "SELECTED" : "",  FILTERTEXT_WILDCARD );
         printf("<OPTION %s>%s</OPTION>", sameString(setting, FILTERTEXT_REGEXP) ? "SELECTED" : "",  FILTERTEXT_REGEXP );
         printf("</SELECT>");
         printf("</P>");
         }
     }
 
 return count;
 }
 
 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
@@ -6667,31 +6754,31 @@
 //    warn("SELECT FROM %s WHERE %s",tdb->table,dyStringContents(extraWhere));
 return extraWhere;
 }
 
 struct dyString *dyAddAllScoreFilters(struct cart *cart, struct trackDb *tdb,
                                       struct dyString *extraWhere,boolean *and)
 // creates the where clause condition to gather together all random double filters
 // 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;
 //      {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
 {
-struct slName *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_NUMBER_WILDCARD);
+struct slName *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_NUMBER_WILDCARD_CAP);
 if (filterSettings)
     {
     struct slName *filter = NULL;
     while ((filter = slPopHead(&filterSettings)) != NULL)
         {
         if (differentString(filter->name,"noScoreFilter")
         &&  differentString(filter->name,"scoreFilter")) // TODO: scoreFilter could be included
             {
             char *field = cloneString(filter->name);
             int ix = strlen(field) - strlen("filter");
             assert(ix > 0);
             field[ix] = '\0';
             char *setting = trackDbSetting(tdb, filter->name);
             // How to determine float or int ?
             // If actual tracDb setting has decimal places, then float!