7c96305a8cc177ad7792386ab9978f66fc82e0a2
braney
  Thu Nov 8 14:26:53 2018 -0800
make filterBy and labels be smarter about being configured at the view
level

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index d9bb321..8c50b44 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -3605,31 +3605,31 @@
             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.");
         }
     }
 }
 
-filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, char *filterName)
+filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, char *filterName, char *name)
 /* Build a filterBy_t structure from a <column>FilterValues statement. */
 {
 char *setting = trackDbSetting(tdb, filterName);
 char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filterName, setting);
 char *field = cloneString(filterName);
 int ix = strlen(field) - strlen(FILTER_VALUES_NAME);
 assert(ix > 0);
 field[ix] = '\0';
 
 filterBy_t *filterBy;
 AllocVar(filterBy);
 filterBy->column = field;
 filterBy->title = field; ///  title should come from AS file, or trackDb variable
 struct asColumn *asCol = asColumnFind(as, field);
 if (asCol != NULL)
@@ -3640,57 +3640,56 @@
 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);
         }
     }
 
 // Note: cannot use found name above because that may be at a higher (composite/view) level
 int len = strlen(tdb->track) + strlen(filterBy->column) + 15;
 filterBy->htmlName = needMem(len);
-safef(filterBy->htmlName, len, "%s.%s.%s", tdb->track,"filterBy",filterBy->column);
-freeMem(setting);
+safef(filterBy->htmlName, len, "%s.%s.%s", name,"filterBy",filterBy->column);
 
 return filterBy;
 }
 
-filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct slName *filterValues)
+filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct slName *filterValues, 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)
     {
-    if ((filter = buildFilterBy(tdb, cart, as, fieldFilter->name)) != NULL)
+    if ((filter = buildFilterBy(tdb, cart, as, fieldFilter->name, 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);
+    return filterByValues(tdb, cart, filterValues, 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++)
     {
@@ -3912,31 +3911,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);
 }
 
 void filterBySetCfgUiGuts(struct cart *cart, struct trackDb *tdb,
 		      filterBy_t *filterBySet, boolean onOneLine,
-		      char *filterTypeTitle, char *selectIdPrefix, char *allLabel)
+		      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);
 if (count == 1)
     puts("<TABLE cellpadding=3><TR valign='top'>");
 else
     printf("<B>%s items by:</B> (select multiple categories and items - %s)"
 	   "<TABLE cellpadding=3><TR valign='top'>\n",filterTypeTitle,FILTERBY_HELP_LINK);
 
 filterBy_t *filterBy = NULL;
 if (cartOptionalString(cart, "ajax") == NULL)
@@ -3957,31 +3956,31 @@
     puts("<TD>");
     char selectStatement[4096];
     if (isMultiple)
         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);
     printf("<BR>\n");
 
     if (isMultiple && tdbIsBigBed(tdb))
         {
         char cartSettingString[4096];
-        safef(cartSettingString, sizeof cartSettingString, "%s.%s", tdb->track, settingString);
+        safef(cartSettingString, sizeof cartSettingString, "%s.%s", prefix, settingString);
         printf("<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> ");
         }
     // TODO: columnCount (Number of filterBoxes per row) should be configurable through tdb setting
 
     // value is always "All", even if label is different, to simplify javascript code
     int valIx = 0;
     if (isMultiple)
         {
         printf( "<SELECT id='%s%d' name='%s' multiple style='display: none; font-size:.9em;' class='filterBy'><BR>\n", selectIdPrefix,ix,filterBy->htmlName);
         printf("<OPTION%s value=\"All\">%s</OPTION>\n", (filterByAllChosen(filterBy)?" SELECTED":""), allLabel);
         valIx = 1;
@@ -4023,41 +4022,41 @@
 		if (*styler == '#') // Legacy: just the color that follows
 		    printf(" style='color: %s;'",styler);
 		else
 		    printf(" style='%s'",styler);
 		}
 	    }
 	printf(">%s</OPTION>\n",label);
 	}
     }
 printf("</SELECT>\n");
 
 puts("</TR></TABLE>");
 }
 
 void filterBySetCfgUi(struct cart *cart, struct trackDb *tdb,
-		  filterBy_t *filterBySet, boolean onOneLine)
+		  filterBy_t *filterBySet, boolean onOneLine, char *prefix)
 /* Does the filter UI for a list of filterBy structure */
 {
-filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", "fbc", "All");
+filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", "fbc", "All", prefix);
 }
 
 void highlightBySetCfgUi(struct cart *cart, struct trackDb *tdb,
-		     filterBy_t *filterBySet, boolean onOneLine)
+		     filterBy_t *filterBySet, boolean onOneLine, char *prefix)
 /* Does the highlight UI for a list of filterBy structure */
 {
-filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", "hbc", "None");
+filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", "hbc", "None", prefix);
 }
 
 #define COLOR_BG_DEFAULT_IX     0
 #define COLOR_BG_ALTDEFAULT_IX  1
 #define DIVIDING_LINE "<TR valign=\"CENTER\" line-height=\"1\" BGCOLOR=\"%s\"><TH colspan=\"5\" " \
 		  "align=\"CENTER\"><hr noshade color=\"%s\" width=\"100%%\"></TD></TR>\n"
 #define DIVIDER_PRINT(color) printf(DIVIDING_LINE,COLOR_BG_DEFAULT,(color))
 
 static char *checkBoxIdMakeForTrack(struct trackDb *tdb,members_t** dims,int dimMax,
 				membership_t *membership)
 // Creates an 'id' string for subtrack checkbox in style that matrix understand:
 //     "cb_dimX_dimY_view_cb"
 {
 int len = strlen(tdb->track) + 10;
 char *id = needMem(len);
@@ -4211,31 +4210,31 @@
 
 // composite/view must pass in example subtrack
 // NOTE: if subtrack types vary then there shouldn't be cfg at composite/view level!
 while (tdb->subtracks)
 tdb = tdb->subtracks;
 
 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);
 
 			if(startsWith("bigBed", tdb->type))
-			    labelCfgUi(db, cart, tdb);
+			    labelCfgUi(db, cart, tdb, prefix);
 			}
 			break;
     case cfgPeak:
 			encodePeakCfgUi(cart,tdb,prefix,title,boxed);
 			break;
     case cfgWig:        wigCfgUi(cart,tdb,prefix,title,boxed);
 			break;
     case cfgWigMaf:     wigMafCfgUi(cart,tdb,prefix,title,boxed, db);
 			break;
     case cfgGenePred:   genePredCfgUi(db, 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;
@@ -5919,31 +5918,31 @@
 boolean isBoxOpened = FALSE;
 if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title) > 0)
     skipScoreFilter = TRUE;
 
 textFiltersShowAll(db, cart, tdb);
 
 // Add any multi-selects next
 filterBy_t *filterBySet = filterBySetGet(tdb,cart,name);
 if (filterBySet != NULL)
     {
     if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL)
         jsIncludeFile("hui.js",NULL);
 
     if (!isBoxOpened)   // Note filterBy boxes are not double "boxed",
         printf("<BR>"); // if there are no other filters
-    filterBySetCfgUi(cart,tdb,filterBySet,TRUE);
+    filterBySetCfgUi(cart,tdb,filterBySet,TRUE, name);
     filterBySetFree(&filterBySet);
     skipScoreFilter = TRUE;
     }
 
 // For no good reason scoreFilter is incompatible with filterBy and or numericFilters
 // FIXME scoreFilter should be implemented inside numericFilters and is currently specificly
 //       excluded to avoid unexpected changes
 if (skipScoreFilter)
     {
     if (isBoxOpened)
         cfgEndBox(boxed);
 
     return; // Cannot have both '*filter' and 'scoreFilter'
     }
 
@@ -6066,51 +6065,51 @@
 for(thisField = fieldList; thisField; thisField = thisField->next)
     {
     char *trimLabel = trimSpaces(thisField->name);
     unsigned colNum = asColumnFindIx(as->columnList, trimLabel);
     if (colNum == -1)
         errAbort("cannot find field named '%s' in AS file '%s'", 
             trimLabel, as->name);
 
     slAddHead(&list, slPairNew(trimLabel, NULL + colNum));
     }
 
 slReverse(&list);
 return list;
 }
 
-void labelCfgUi(char *db, struct cart *cart, struct trackDb *tdb)
+void labelCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *prefix)
 /* If there is a labelFields for a bigBed, this routine is called to put up the label options. */
 {
 if (trackDbSettingClosestToHomeOn(tdb, "linkIdInName"))
     return;
 
 struct asObject *as = asForDb(tdb, db);  
 if (as == NULL)
     return;
 struct slPair *labelList = buildFieldList(tdb, "labelFields",  as);
 struct slPair *defaultLabelList = buildFieldList(tdb, "defaultLabelFields",  as);
 char varName[1024];
 
 if ((labelList == NULL) || sameString(labelList->name, "none"))
     return;
 
 printf("<B>Label:</B> ");
 struct slPair *thisLabel = labelList;
 for(; thisLabel; thisLabel = thisLabel->next)
     {
-    safef(varName, sizeof(varName), "%s.label.%s", tdb->track, thisLabel->name);
+    safef(varName, sizeof(varName), "%s.label.%s", prefix, thisLabel->name);
     boolean isDefault = FALSE;
     if (defaultLabelList == NULL)
         isDefault = (thisLabel == labelList);
     else if (sameString(defaultLabelList->name, "none"))
         isDefault = FALSE;
     else
         isDefault = (slPairFind(defaultLabelList, thisLabel->name) != NULL);
 
     boolean option = cartUsualBoolean(cart, varName, isDefault);
     cgiMakeCheckBox(varName, option);
 
     // find comment for the column listed
     struct asColumn *col = as->columnList;
     unsigned num = ptToInt(thisLabel->val);
     for(; col && num--; col = col->next)
@@ -6118,31 +6117,31 @@
     assert(col);
     printf(" %s&nbsp;&nbsp;&nbsp;", col->comment);
     }
 }
 
 void pslCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title,
               boolean boxed)
 /* Put up UI for psl tracks */
 {
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
 char *typeLine = cloneString(tdb->type);
 char *words[8];
 int wordCount = wordCount = chopLine(typeLine, words);
 if (sameString(tdb->type, "bigPsl"))
-    labelCfgUi(db, cart, tdb);
+    labelCfgUi(db, cart, tdb, name);
 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 parentLevel = isNameAtParentLevel(tdb,prefix);
@@ -6515,31 +6514,31 @@
     char *value = cartUsualStringClosestToHome(cart, tdb, parentLevel, varSuffix, NULL);
     boolean checked = (value != NULL) && !sameString(value, "0");
     printf("%s%s: ", (i > 0) ? "&nbsp;&nbsp;" : "", labelsNames[i][0]);
     cgiMakeCheckBoxMore(varName, checked, NULL);
     }
 }
 
 void genePredCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed)
 /* Put up genePred-specific controls */
 {
 char varName[64];
 boolean parentLevel = isNameAtParentLevel(tdb,name);
 char *geneLabel = cartUsualStringClosestToHome(cart, tdb,parentLevel, "label", "gene");
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
-labelCfgUi(db, cart, tdb);
+labelCfgUi(db, cart, tdb, name);
 if (sameString(name, "acembly"))
     {
     char *acemblyClass = cartUsualStringClosestToHome(cart,tdb,parentLevel,"type",
                                                       acemblyEnumToString(0));
     printf("<p><b>Gene Class: </b>");
     acemblyDropDown("acembly.type", acemblyClass);
     printf("  ");
     }
 else if (startsWith("wgEncodeGencode", name))
     {
     // new GENCODEs
     gencodeLabelControls(db, cart, tdb, name, title, boxed, parentLevel);
     }
 else if (sameString("wgEncodeSangerGencode", name)
      ||  (startsWith("encodeGencode", name) && !sameString("encodeGencodeRaceFrags", name)))
@@ -6565,38 +6564,38 @@
     nmdDefault = cartUsualBoolean(cart,varName, FALSE);
     // TODO: var name (hgt prefix) needs changing before ClosesToHome can be used
     printf("<p><b>Filter out NMD targets.</b>");
     cgiMakeCheckBox(varName, nmdDefault);
     }
 
 if (!sameString(tdb->track, "tigrGeneIndex")
 &&  !sameString(tdb->track, "ensGeneNonCoding")
 &&  !sameString(tdb->track, "encodeGencodeRaceFrags"))
     baseColorDropLists(cart, tdb, name);
 
 filterBy_t *filterBySet = filterBySetGet(tdb,cart,name);
 if (filterBySet != NULL)
     {
     printf("<BR>");
-    filterBySetCfgUi(cart,tdb,filterBySet,FALSE);
+    filterBySetCfgUi(cart,tdb,filterBySet,FALSE, name);
     filterBySetFree(&filterBySet);
     }
 filterBy_t *highlightBySet = highlightBySetGet(tdb,cart,name);
 if (highlightBySet != NULL)
     {
     printf("<BR>");
-    highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE);
+    highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE, name);
     filterBySetFree(&highlightBySet);
     }
 
 wigOption(cart, name, title, tdb);
 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) */
 {
 boolean parentLevel = isNameAtParentLevel(tdb,option);
 if (*option == '\0')
     safef(option, optionSize, "%s.%s", tdb->track, species);
 else
     {