733ba7b804e84d7b60ccad1f137398ecd52db983
chmalee
  Tue Apr 23 18:15:09 2024 -0700
Add a general highlight trackDb variable(s), working like trackDb filters, except put a color behind the item, refs #24507

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 588f050..4df1012 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -3964,71 +3964,72 @@
     {
     safef(labelSetting, sizeof labelSetting, "%s.%s", field, FILTER_LABEL_NAME_CAP);
     trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL);
     }
 if (trackDbLabel == NULL)
     {
     safef(labelSetting, sizeof labelSetting, "%s%s", field, FILTER_LABEL_NAME_CAP);
     trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL);
     }
 return trackDbLabel;
 }
 
 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. */
 {
+boolean isHighlight = startsWith("highlightValues.", tdbFilter->name);
 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, tdbFilter->name, tdbFilter->setting);
 
 filterBy_t *filterBy;
 AllocVar(filterBy);
 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);
+    safef(suffix, sizeof(suffix), "%s.%s", isHighlight ? "highlightBy" : "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);
         }
     }
 
 if (filterBy->slChoices == NULL)  // no settings in cart, initialize from trackDb
     {
     char *setting = getFilterValueDefaultsSetting(cart, tdb, field);
     filterBy->slChoices = slNameListFromCommaEscaped(setting);
     }
 
 struct dyString *dy = dyStringNew(128);
-dyStringPrintf(dy, "%s.%s.%s", name, "filterBy", filterBy->column);
+dyStringPrintf(dy, "%s.%s.%s", name, isHighlight ? "highlightBy": "filterBy", filterBy->column);
 filterBy->htmlName = dy->string;
 
 return filterBy;
 }
 
 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);
 if (as == NULL)
     errAbort("Unable to get autoSql for %s", name);
 filterBy_t *filterByList = NULL, *filter;
 struct trackDbFilter *fieldFilter;
 while ((fieldFilter = slPopHead(&trackDbFilters)) != NULL)
     {
@@ -4036,30 +4037,36 @@
         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
 if (differentString(subName, "highlightBy"))
     {
     struct trackDbFilter *trackDbFilters = tdbGetTrackFilterByFilters( tdb);
     if (trackDbFilters)
         return filterByValues(tdb, cart, trackDbFilters, name);
     }
+else
+    {
+    struct trackDbFilter *trackDbHighlights = tdbGetTrackHighlightByHighlights(tdb);
+    if (trackDbHighlights)
+        return filterByValues(tdb, cart, trackDbHighlights, 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++)
     {
@@ -4299,32 +4306,33 @@
         sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_OR) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_AND) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_AND));
 }
 
 static boolean advancedFilter(struct cart *cart, struct trackDb *tdb, char *setting)
 {
 if (!tdbIsBigBed(tdb))
     return FALSE;
 
 return  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)
+		      char *filterTypeTitle, char *selectIdPrefix, char *allLabel, char *prefix, boolean isHighlight)
 // Does the UI for a list of filterBy structure for either filterBy or highlightBy controls
+// isHighlight controls the variable name for the cart
 {
 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 class='trackUiFilterTable'><TR valign='top'>");
 else
     printf("<B>%s items by:</B> (select multiple categories and items - %s)"
 	   "<TABLE class='trackUiFilterTable'><TR valign='bottom'>\n",filterTypeTitle,FILTERBY_HELP_LINK);
 
 #ifdef ADVANCED_BUTTON
 if (tdbIsBigBed(tdb))
     {
@@ -4359,38 +4367,51 @@
         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>");
     char *setting =  getFilterType(cart, tdb, filterBy->column, FILTERBY_DEFAULT);
     if (advancedFilter(cart, tdb, setting))
         {
         char cartSettingString[4096];
+        if (isHighlight)
+            {
+            safef(cartSettingString, sizeof cartSettingString, "%s.%s.%s", prefix,HIGHLIGHT_TYPE_NAME_LOW, filterBy->column);
+            printf("<div ><b>Highlight if  ");
+            // ADVANCED BUTTON printf("<div class='advanced' style='display:none'><b>Match if  ");
+            cgiMakeRadioButton(cartSettingString, HIGHLIGHTBY_MULTIPLE_LIST_AND, sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_AND));
+            printf(" all ");
+            cgiMakeRadioButton(cartSettingString, HIGHLIGHTBY_MULTIPLE_LIST_OR, sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_OR));
+            printf(" one or more match</b></div> ");
+            }
+        else
+            {
             safef(cartSettingString, sizeof cartSettingString, "%s.%s.%s", prefix,FILTER_TYPE_NAME_LOW, filterBy->column);
             printf("<div ><b>Match if  ");
             // ADVANCED BUTTON 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++)
     {
     char *setting =  getFilterType(cart, tdb, filterBy->column, FILTERBY_DEFAULT);
     puts("<td>");
     // value is always "All", even if label is different, to simplify javascript code
     int valIx = 1;
     if (filterByColumnIsMultiple(cart, tdb, setting))
         printf( "<SELECT id='%s%d' name='%s' multiple style='display: none; font-size:.9em;' class='filterBy'><BR>\n", selectIdPrefix,ix,filterBy->htmlName);
     else
         printf( "<SELECT id='%s%d' name='%s' style='font-size:.9em;'<BR>\n", selectIdPrefix,ix,filterBy->htmlName);
 
@@ -4431,38 +4452,38 @@
 		}
 	    }
 	printf(">%s</OPTION>\n",label);
 	}
     printf("</SELECT>\n");
     puts("</td>");
     }
 
 puts("</TR></TABLE>");
 }
 
 void filterBySetCfgUi(struct cart *cart, struct trackDb *tdb,
 		  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", prefix);
+filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", "fbc", "All", prefix, FALSE);
 }
 
 void highlightBySetCfgUi(struct cart *cart, struct trackDb *tdb,
-		     filterBy_t *filterBySet, boolean onOneLine, char *prefix)
+		     filterBy_t *filterBySet, boolean onOneLine, char *prefix, boolean isHighlight)
 /* Does the highlight UI for a list of filterBy structure */
 {
-filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", "hbc", "None", prefix);
+filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", "hbc", "None", prefix, TRUE);
 }
 
 #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 boolean divisionIfNeeded(char **lastDivide,dividers_t *dividers,membership_t *membership)
 // Keeps track of location within subtracks in order to provide requested division lines
 {
 boolean division = FALSE;
 if (dividers)
     {
     if (lastDivide != NULL)
@@ -6578,31 +6599,31 @@
     }
 // 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 parentLevel,char *name, char *title,
-                               char *label, char *scoreName)
+                               char *label, char *scoreName, boolean isHighlight)
 // 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];
     char *filterName = getScoreNameAdd(tdb, scoreName, _BY_RANGE);
@@ -6692,53 +6713,93 @@
 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);
 }
 
+struct trackDbFilter *tdbGetTrackNumHighlights( struct trackDb *tdb)
+// get the number filters out of trackDb
+{
+return tdbGetTrackFilters( tdb, HIGHLIGHT_NUMBER_WILDCARD_LOW, HIGHLIGHT_NUMBER_NAME_LOW, HIGHLIGHT_NUMBER_WILDCARD_CAP, HIGHLIGHT_NUMBER_NAME_CAP);
+}
+
+struct trackDbFilter *tdbGetTrackTextHighlights( struct trackDb *tdb)
+// get the text filters out of trackDb
+{
+return tdbGetTrackFilters( tdb, HIGHLIGHT_TEXT_WILDCARD_LOW, HIGHLIGHT_TEXT_NAME_LOW, HIGHLIGHT_TEXT_WILDCARD_CAP, HIGHLIGHT_TEXT_NAME_CAP);
+}
+
+struct trackDbFilter *tdbGetTrackHighlightByHighlights( struct trackDb *tdb)
+// get the values filters out of trackDb
+{
+return tdbGetTrackFilters( tdb, HIGHLIGHT_VALUES_WILDCARD_LOW, HIGHLIGHT_VALUES_NAME_LOW, HIGHLIGHT_VALUES_WILDCARD_CAP, HIGHLIGHT_VALUES_NAME_CAP);
+}
+
+char *prevHighlightColor(struct cart *cart, struct trackDb *tdb)
+/* Return the cart string for the highlight color if it has been changed else the default */
+{
+return cartOrTdbString(cart, tdb, HIGHLIGHT_COLOR_CART_VAR, HIGHLIGHT_COLOR_DEFAULT);
+}
+
+void printHighlightColorPicker(struct cart *cart, struct trackDb *tdb)
+{
+jsIncludeFile("ajax.js", NULL);
+jsIncludeFile("hui.js", NULL);
+puts("<br>");
+puts("Choose highlight color:");
+puts("<div id='hgTrackUiColorPicker'></div>");
+jsInlineF("var cartHighlightColor = \"%s\"\n;", prevHighlightColor(cart, tdb));
+jsInlineF("makeHighlightPicker(\"hgTrackUiHighlight\", document.getElementById(\"hgTrackUiColorPicker\"), \"%s\");\n", tdb->track);
+}
+
 int defaultFieldLocation(char *field)
 /* Sometimes we get bigBed filters with field names that are not in the AS file.  
  * Try to guess what the user means. */
 {
 if (sameString("score", field))
     return 4;
 if (sameString("signal", field))
     return 6;
 if (sameString("signalValue", field))
     return 6;
 if (sameString("pValue", field))
     return 7;
 if (sameString("qValue", field))
     return 8;
 return -1;
 }
 
 static int numericFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean *opened,
-                                 boolean boxed, boolean parentLevel,char *name, char *title)
+                                 boolean boxed, boolean parentLevel,char *name, char *title,
+                                 boolean isHighlight)
 // Shows all *Filter style filters.  Note that these are in random order and have no graceful title
 {
 int count = 0;
-struct trackDbFilter *trackDbFilters = tdbGetTrackNumFilters(tdb);
+struct trackDbFilter *trackDbFilters = NULL;
+if (isHighlight)
+    trackDbFilters = tdbGetTrackNumHighlights(tdb);
+else
+    trackDbFilters = tdbGetTrackNumFilters(tdb);
 if (trackDbFilters)
     {
     puts("<BR>");
     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(&trackDbFilters)) != NULL)
         {
         char *field = filter->fieldName;
         char *scoreName = cloneString(filter->name);
         char *trackDbLabel = getLabelSetting(cart, tdb, field);
@@ -6751,31 +6812,33 @@
                 field = asCol->comment;
                 }
             else if (defaultFieldLocation(field) < 0)
                 errAbort("Building filter on field %s which is not in AS file.", field);
             }
         char labelBuf[1024];
         char *label = labelBuf;
         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);
+        if (isHighlight && count == 0)
+            printHighlightColorPicker(cart, tdb);
+        showScoreFilter(cart,tdb,opened,boxed,parentLevel,name,title,label,scoreName,isHighlight);
         count++;
         }
     if (as != NULL)
         asObjectFree(&as);
     }
 if (count > 0)
     puts("</TABLE>");
 return count;
 }
 
 
 boolean bedHasFilters(struct trackDb *tdb)
 // Does track have filters
 {
 if (trackDbSettingClosestToHome(tdb, FILTER_BY))
@@ -6840,64 +6903,70 @@
 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)
+static int textFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean isHighlight)
 /* Show all the text filters for this track. */
 {
 int count = 0;
-struct trackDbFilter *trackDbFilters = tdbGetTrackTextFilters(tdb);
+struct trackDbFilter *trackDbFilters = NULL;
+if (isHighlight)
+    trackDbFilters = tdbGetTrackTextHighlights(tdb);
+else
+    trackDbFilters = tdbGetTrackTextFilters(tdb);
 if (trackDbFilters)
     {
     puts("<BR>");
     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(&trackDbFilters)) != NULL)
         {
         char *trackDbLabel = getLabelSetting(cart, tdb, filter->fieldName);
         char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, filter->setting);
         if (as != NULL)
             {
             struct asColumn *asCol = asColumnFind(as, filter->fieldName);
             if (asCol != NULL)
                 {
                 if (trackDbLabel == NULL)
                     trackDbLabel = asCol->comment;
                 }
             else if (defaultFieldLocation(filter->fieldName) < 0)
                 errAbort("Building filter on field %s which is not in AS file.", filter->fieldName);
             }
         if (trackDbLabel == NULL)
             trackDbLabel = filter->fieldName;
 
+        if (isHighlight && count == 0)
+            printHighlightColorPicker(cart, tdb);
         count++;
-        printf("<P><B>Filter items in '%s' field:</B> ", trackDbLabel);
+        printf("<P><B>%s items in '%s' field:</B> ", isHighlight ? "Highlight": "Filter", trackDbLabel);
 
         char cgiVar[128];
         safef(cgiVar,sizeof(cgiVar),"%s.%s",tdb->track,filter->name);
         cgiMakeTextVar(cgiVar, value, 45);
 
         char *setting = getFilterType(cart, tdb, filter->fieldName, FILTERTEXT_WILDCARD);
         safef(cgiVar,sizeof(cgiVar),"%s.%s.%s",tdb->track,FILTER_TYPE_NAME_LOW, filter->fieldName);
         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("&nbsp;&nbsp;<button class='buttonClear-%s'>Clear</button>\n", tdb->track);
         printf("</P>");
         }
@@ -6920,50 +6989,80 @@
 if (cartOptionalString(cart, "ajax") == NULL)
     {
     webIncludeResourceFile("ui.dropdownchecklist.css");
     jsIncludeFile("ui.dropdownchecklist.js",NULL);
     jsIncludeFile("ddcl.js",NULL);
     }
 
 boolean parentLevel = isNameAtParentLevel(tdb,name);
 if (parentLevel)
     if (trackDbSettingOn(tdb->parent, "noParentConfig"))
         return;
 boolean skipScoreFilter = FALSE;
 
 // Numeric filters are first
 boolean isBoxOpened = FALSE;
-if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title) > 0)
+if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, FALSE) > 0)
     skipScoreFilter = TRUE;
 
-if (textFiltersShowAll(db, cart, tdb))
+if (textFiltersShowAll(db, cart, tdb, FALSE))
     skipScoreFilter = TRUE;
 
 // 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, name);
     filterBySetFree(&filterBySet);
     skipScoreFilter = TRUE;
     }
 
+// add any highlights:
+// Numeric highlights are first
+boolean didHighlightSelector = FALSE;
+if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, TRUE) > 0)
+    {
+    didHighlightSelector = TRUE;
+    skipScoreFilter = TRUE;
+    }
+
+if (textFiltersShowAll(db, cart, tdb, TRUE))
+    {
+    didHighlightSelector = TRUE;
+    skipScoreFilter = TRUE;
+    }
+
+filterBy_t *highlightBySet = highlightBySetGet(tdb,cart,name);
+if (highlightBySet != NULL)
+    {
+    if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL)
+        jsIncludeFile("hui.js",NULL);
+    if (!didHighlightSelector)
+        printHighlightColorPicker(cart, tdb);
+
+    if (!isBoxOpened)   // Note filterBy boxes are not double "boxed",
+        printf("<BR>"); // if there are no other filters
+    highlightBySetCfgUi(cart,tdb,highlightBySet,TRUE, name, TRUE);
+    filterBySetFree(&highlightBySet);
+    skipScoreFilter = TRUE;
+    }
+
 boolean scoreFilterOk = (trackDbSettingClosestToHome(tdb, NO_SCORE_FILTER) == NULL) && !skipScoreFilter;
 boolean glvlScoreMin = (trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN) != NULL);
 if (! (scoreFilterOk || glvlScoreMin))
     {
     cfgEndBox(boxed);
     return;
     }
 
 boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
 
 if (scoreFilterOk)
     {
     int minLimit=0,maxLimit=maxScore,minVal=0,maxVal=maxScore;
     getScoreIntRangeFromCart(cart,tdb,parentLevel,SCORE_FILTER,&minLimit,&maxLimit,
                                                                &minVal,  &maxVal);
@@ -7491,35 +7590,35 @@
         ||  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 parentLevel = isNameAtParentLevel(tdb,name);
 boolean opened = FALSE;
 showScoreFilter(cart,tdb,&opened,boxed,parentLevel,name,title,
-                "Minimum Signal value",     SIGNAL_FILTER);
+                "Minimum Signal value",     SIGNAL_FILTER, FALSE);
 showScoreFilter(cart,tdb,&opened,boxed,parentLevel,name,title,
-                "Minimum P-Value (<code>-log<sub>10</sub></code>)",PVALUE_FILTER);
+                "Minimum P-Value (<code>-log<sub>10</sub></code>)",PVALUE_FILTER, FALSE);
 showScoreFilter(cart,tdb,&opened,boxed,parentLevel,name,title,
-                "Minimum Q-Value (<code>-log<sub>10</sub></code>)",QVALUE_FILTER);
+                "Minimum Q-Value (<code>-log<sub>10</sub></code>)",QVALUE_FILTER, FALSE);
 
 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,parentLevel,SCORE_FILTER,&minLimit,&maxLimit,
                                                                &minVal,  &maxVal);
@@ -7690,31 +7789,31 @@
 &&  !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, name);
     filterBySetFree(&filterBySet);
     }
 filterBy_t *highlightBySet = highlightBySetGet(tdb,cart,name);
 if (highlightBySet != NULL)
     {
     printf("<BR>");
-    highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE, name);
+    highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE, name, TRUE);
     filterBySetFree(&highlightBySet);
     }
 
 squishyPackOption(cart, name, title, tdb);
 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