e2467a639cc1e98174ffbd9d0da399b3b75bc9ae
markd
  Thu Jul 26 21:33:38 2012 -0700
highlighting by attribute functionality for GENCODE
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 1750298..98ccd2f 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -3318,63 +3318,63 @@
         if (first)
             *first = strtod(a,NULL);
         freeMem(a);
         }
     if (b!=NULL)
         {
         if (second)
             *second = strtod(b,NULL);
         freeMem(b);
         }
     return TRUE;
     }
 return FALSE;
 }
 
-filterBy_t *filterBySetGet(struct trackDb *tdb, struct cart *cart, char *name)
+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
 {
 filterBy_t *filterBySet = NULL;
 
-char *setting = trackDbSettingClosestToHome(tdb, FILTER_BY);
+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++)
     {
     char *filter = cloneString(filters[ix]);
     filterBy_t *filterBy;
     AllocVar(filterBy);
     char *first = strchr(filter,':');
     if (first != NULL)
         *first = '\0';
     else
-        errAbort("filterBySetGet() expected ':' divider between table column and label.");
+        errAbort("filterBySetGet() expected ':' divider between table column and label: %s", filters[ix]);
     filterBy->column = filter;
     filter += strlen(filter) + 1;
     first = strchr(filter,'=');
     if (first != NULL)
         *first = '\0';
     else
-        errAbort("filterBySetGet() expected '=' divider between table column and options list.");
+        errAbort("filterBySetGet() expected '=' divider between table column and options list: %s", filters[ix]);
     filterBy->title = strSwapChar(filter,'_',' '); // Title does not have underscores
     filter += strlen(filter) + 1;
 
     // Are values indexes to the string titles?
     if (filter[0] == '+')
         {
         filter += 1;
         filterBy->useIndex = TRUE;
         }
 
     // Now set up each of the values which may have 1-3 parts (value|label{style})
     // the slName list will have the 3 parts delimited by null value\0label\0style\0
     stripString(filter, "\"");  // Remove any double quotes now and chop by commmas
     filterBy->slValues = slNameListFromComma(filter);
     struct slName *val = filterBy->slValues;
@@ -3417,49 +3417,61 @@
                              "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.");
             }
         }
 
     slAddTail(&filterBySet,filterBy); // Keep them in order (only a few)
 
     if (cart != NULL)
         {
         char suffix[256];
-        safef(suffix, sizeof(suffix), "filterBy.%s", filterBy->column);
+        safef(suffix, sizeof(suffix), "%s.%s", subName, filterBy->column);
         boolean parentLevel = isNameAtParentLevel(tdb,name);
         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(name) + strlen(filterBy->column) + 15;
     filterBy->htmlName = needMem(len);
-    safef(filterBy->htmlName, len, "%s.filterBy.%s", name,filterBy->column);
+    safef(filterBy->htmlName, len, "%s.%s.%s", name,subName,filterBy->column);
     }
 freeMem(setting);
 
 return filterBySet;
 }
 
+filterBy_t *filterBySetGet(struct trackDb *tdb, struct cart *cart, char *name)
+/* Gets one or more "filterBy" settings (ClosestToHome).  returns NULL if not found */
+{
+return filterBySetGetGuts(tdb, cart, name, "filterBy", FILTER_BY);
+}
+
+filterBy_t *highlightBySetGet(struct trackDb *tdb, struct cart *cart, char *name)
+/* Gets one or more "highlightBy" settings (ClosestToHome).  returns NULL if not found */
+{
+return filterBySetGetGuts(tdb, cart, name, "highlightBy", HIGHLIGHT_BY);
+}
+
 void filterBySetFree(filterBy_t **filterBySet)
 // Free a set of filterBy structs
 {
 if (filterBySet != NULL)
     {
     while (*filterBySet != NULL)
         {
         filterBy_t *filterBy = slPopHead(filterBySet);
         if (filterBy->slValues != NULL)
             slNameFreeList(filterBy->slValues);
         if (filterBy->slChoices != NULL)
             slNameFreeList(filterBy->slChoices);
         if (filterBy->htmlName != NULL)
             freeMem(filterBy->htmlName);
         freeMem(filterBy->column);
@@ -3493,31 +3505,31 @@
     }
 if (dyStringLen(dyClause) == 0)
     {
     dyStringFree(&dyClause);
     return NULL;
     }
 if (count > 1)
     dyStringPrintf(dyClause, ")");
 
 return dyStringCannibalize(&dyClause);
 }
 
 char *filterByClause(filterBy_t *filterBy)
 // returns the SQL where clause for a single filterBy struct
 {
-if ((filterBy->slChoices == NULL) || (slNameInList(filterBy->slChoices,"All")))
+if (filterByAllChosen(filterBy))
     return NULL;
 else
     return filterByClauseStd(filterBy);
 }
 
 struct dyString *dyAddFilterByClause(struct cart *cart, struct trackDb *tdb,
                                      struct dyString *extraWhere,char *column, boolean *and)
 // creates the where clause condition to support a filterBy setting.
 // Format: filterBy column:Title=value,value [column:Title=value|label,value|label,value|label])
 // filterBy filters are multiselect's so could have multiple values selected.
 // thus returns the "column1 in (...) and column2 in (...)" clause.
 // if 'column' is provided, and there are multiple filterBy columns, only the named column's
 // clause is returned.
 // The 'and' param and dyString in/out allows stringing multiple where clauses together
 {
@@ -3560,72 +3572,106 @@
         if (notFirst)
             dyStringPrintf(dyClause, " AND ");
         dyStringAppend(dyClause, clause);
         freeMem(clause);
         notFirst = TRUE;
         }
     }
 if (dyStringLen(dyClause) == 0)
     {
     dyStringFree(&dyClause);
     return NULL;
     }
 return dyStringCannibalize(&dyClause);
 }
 
-void filterBySetCfgUi(struct cart *cart, struct trackDb *tdb,
-                      filterBy_t *filterBySet, boolean onOneLine)
-// Does the UI for a list of filterBy structure
+void filterBySetCfgUiOption(filterBy_t *filterBy, struct slName *slValue, int ix)
+/* output one option for filterBy UI  */
+{
+char varName[32];
+char *label = NULL;
+char *name = NULL;
+if (filterBy->useIndex)
+    {
+    safef(varName, sizeof(varName), "%d",ix);
+    name = varName;
+    label = slValue->name;
+    }
+else
+    {
+    label = (filterBy->valueAndLabel? slValue->name + strlen(slValue->name)+1: slValue->name);
+    name = slValue->name;
+    }
+printf("<OPTION");
+if (filterBy->slChoices != NULL && slNameInList(filterBy->slChoices,name))
+    printf(" SELECTED");
+if (filterBy->useIndex || filterBy->valueAndLabel)
+    printf(" value='%s'",name);
+if (filterBy->styleFollows)
+    {
+    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)
+// 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>Filter items by:</B> (select multiple categories and items - %s)"
            "<TABLE cellpadding=3><TR valign='top'>\n",FILTERBY_HELP_LINK);
 
 filterBy_t *filterBy = NULL;
 if (cartOptionalString(cart, "ajax") == NULL)
     {
     webIncludeResourceFile("ui.dropdownchecklist.css");
     jsIncludeFile("ui.dropdownchecklist.js",NULL);
     jsIncludeFile("ddcl.js",NULL);
     }
 
 int ix=0;
-for (filterBy = filterBySet;filterBy != NULL; filterBy = filterBy->next)
+for(filterBy = filterBySet;filterBy != NULL; filterBy = filterBy->next, ix++)
     {
     puts("<TD>");
     if (count == 1)
-        printf("<B>Filter by %s</B> (select multiple items - %s)",
-               filterBy->title,FILTERBY_HELP_LINK);
+        printf("<B>%s by %s</B> (select multiple items - %s)",filterTypeTitle,filterBy->title,FILTERBY_HELP_LINK);
     else
         printf("<B>%s</B>",filterBy->title);
     printf("<BR>\n");
 
     // TODO: columnCount (Number of filterBoxes per row) should be configurable through tdb setting
-    #define FILTER_BY_FORMAT "<SELECT id='fbc%d' name='%s' multiple style='display: none; " \
-                             "font-size:.9em;' class='filterBy'><BR>\n"
-    printf(FILTER_BY_FORMAT,ix,filterBy->htmlName);
-    ix++;
-    printf("<OPTION%s>All</OPTION>\n",
-           (filterBy->slChoices == NULL || slNameInList(filterBy->slChoices,"All") ? " SELECTED"
-                                                                                   : ""));
+    #define FILTER_BY_FORMAT "<SELECT id='%s%d' name='%s' multiple style='display: none; font-size:.9em;' class='filterBy'><BR>\n"
+    printf(FILTER_BY_FORMAT,selectIdPrefix,ix,filterBy->htmlName);
+
+    // value is always "All", even if label is different, to simplify javascript code
+    printf("<OPTION%s value=\"All\">%s</OPTION>\n", (filterByAllChosen(filterBy)?" SELECTED":""), allLabel);
     struct slName *slValue;
 
     int ix=1;
     for (slValue=filterBy->slValues;slValue!=NULL;slValue=slValue->next,ix++)
         {
         char varName[32];
         char *label = NULL;
         char *name = NULL;
         if (filterBy->useIndex)
             {
             safef(varName, sizeof(varName), "%d",ix);
             name = varName;
             label = slValue->name;
             }
         else
@@ -3644,32 +3690,44 @@
             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);
         }
     }
 printf("</SELECT>\n");
 
 puts("</TR></TABLE>");
+}
 
-return;
+void filterBySetCfgUi(struct cart *cart, struct trackDb *tdb,
+                      filterBy_t *filterBySet, boolean onOneLine)
+/* Does the filter UI for a list of filterBy structure */
+{
+filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", "fbc", "All");
+}
+
+void highlightBySetCfgUi(struct cart *cart, struct trackDb *tdb,
+                         filterBy_t *filterBySet, boolean onOneLine)
+/* Does the highlight UI for a list of filterBy structure */
+{
+filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", "hbc", "None");
 }
 
 #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);
@@ -5710,30 +5768,37 @@
     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);
     filterBySetFree(&filterBySet);
     }
+filterBy_t *highlightBySet = highlightBySetGet(tdb,cart,name);
+if (highlightBySet != NULL)
+    {
+    printf("<BR>");
+    highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE);
+    filterBySetFree(&highlightBySet);
+    }
 
 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
     {
     char *suffix = option + strlen(option);
     int suffixSize = optionSize - strlen(option);
     safef(suffix,suffixSize,".%s",species);