64b2c5bd9ab0e2bd59360c39298ae54e97615407
kate
  Thu Sep 26 13:30:43 2019 -0700
Add UI control to hide empty subtracks. refs #23365

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index fe37255..c103ec1 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -4988,57 +4988,127 @@
     }
 else
     settings->bgColorIx = COLOR_BG_DEFAULT_IX; // Start with non-default allows alternation
 puts("'>");
 dyStringFree(&dyHtml)
 
 // save count of subtracks for use by footer code
 int subCount = slCount(subtrackRefList);
 
 printSubtrackTableHeader(parentTdb, subtrackRefList, settings);
 printSubtrackTableBody(parentTdb, subtrackRefList, settings, cart);
 printSubtrackTableFooter(subCount, settings);
 puts("</TABLE>");
 }
 
+boolean compositeHideEmptySubtracksSetting(struct trackDb *tdb, boolean *retDefault,
+                                        char **retMultiBedFile, char **retSubtrackIdFile)
+/* Parse hideEmptySubtracks setting
+ * Format:  hideEmptySubtracks on|default
+ *              or
+ *          hideEmptySubtracks on|default multiBed.bed subtrackIds.tab
+ * where multiBed.bed is a bed3Sources bigBed, generated with bedtools multiinter
+ *              post-processed by UCSC multiBed.pl tool
+ *      subtrackIds.tab is a tab-sep file: id subtrackName
+ *
+ * Return TRUE if set to true/on/default.  retDefault is TRUE if set default, o/w FALSE
+ */
+{
+if (!tdbIsComposite(tdb))
+    return FALSE;
+char *hideEmpties = trackDbSetting(tdb, SUBTRACK_HIDE_EMPTIES);
+if (!hideEmpties)
+    return FALSE;
+char *orig = cloneString(hideEmpties);
+char *words[3];
+int wordCount = chopByWhite(hideEmpties, words, ArraySize(words));
+char *mode = words[0];
+if (differentString(mode, "on") && differentString(mode, "true") &&
+    differentString(mode, "default"))
+        {
+        warn("Track %s %s setting invalid: %s", tdb->track, SUBTRACK_HIDE_EMPTIES, orig);
+        return FALSE;
+        }
+boolean deflt = sameString(mode, "default") ? TRUE : FALSE;
+if (retDefault)
+    *retDefault = deflt;
+
+if (wordCount == 1)
+    return TRUE;
+if (wordCount != 3)
+    {
+    warn("Track %s %s setting invalid: %s", tdb->track, SUBTRACK_HIDE_EMPTIES, orig);
+    return FALSE;
+    }
+// multi-bed specified (to speed display)
+if (retMultiBedFile)
+    *retMultiBedFile = cloneString(hReplaceGbdb(words[1]));
+if (retSubtrackIdFile)
+    *retSubtrackIdFile = cloneString(words[2]);
+return TRUE;
+}
+
+boolean compositeHideEmptySubtracks(struct cart *cart, struct trackDb *tdb,
+                                        char **retMutiBedFile, char **retSubtrackIdFile)
+/* Parse hideEmptySubtracks setting and check cart
+ * Return TRUE if we should hide empties
+ */
+{
+boolean deflt = FALSE;
+if (!compositeHideEmptySubtracksSetting(tdb, &deflt, retMutiBedFile, retSubtrackIdFile))
+    return FALSE;
+char buf[128];
+safef(buf, sizeof buf, "%s.%s", tdb->track, SUBTRACK_HIDE_EMPTIES);
+return cartUsualBoolean(cart, buf, deflt);
+}
+
 static void compositeUiSubtracks(char *db, struct cart *cart, struct trackDb *parentTdb)
 // Display list of subtracks and descriptions with checkboxes to control visibility and
 // possibly other nice things including links to schema and metadata and a release date.
 {
 char buffer[SMALLBUF];
 struct trackDb *subtrack;
 
 // Get list of leaf subtracks to work with
 struct slRef *subtrackRef, *subtrackRefList = trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
 
 membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,NULL);
 sortOrder_t* sortOrder = sortOrderGet(cart, parentTdb);
 char *displaySubs = NULL;
 int subCount = slCount(subtrackRefList);
 if (subCount > LARGE_COMPOSITE_CUTOFF && membersForAll->dimensions != NULL)
     {
     // ignore displaySubtracks setting for large composites with a matrix as
     // matrix effectively shows all
     safef(buffer, SMALLBUF,"%s.displaySubtracks", parentTdb->track);
     displaySubs = cartUsualString(cart, buffer,"some"); // track specific defaults to only selected
     }
 else
     {
     displaySubs = cartUsualString(cart, "displaySubtracks", "all"); // browser wide defaults to all
     }
 boolean displayAll = sameString(displaySubs, "all");
 
+boolean hideSubtracksDefault;
+if (compositeHideEmptySubtracksSetting(parentTdb, &hideSubtracksDefault, NULL, NULL))
+    {
+    printf("<BR><B>Hide empty subtracks:</B> &nbsp;");
+    char buf[128];
+    safef(buf, sizeof buf, "%s.%s", parentTdb->track, SUBTRACK_HIDE_EMPTIES);
+    cgiMakeCheckBox(buf, hideSubtracksDefault);
+    }
+
 // Table wraps around entire list so that "Top" link can float to the correct place.
 cgiDown(0.7);
 printf("<table><tr><td class='windowSize'>");
 printf("<A NAME='DISPLAY_SUBTRACKS'></A>");
 if (sortOrder != NULL)
     {
     // First table row contains the display "selected/visible" or "all" radio buttons
     // NOTE: list subtrack radio buttons are inside tracklist table header if
     //       there are no sort columns.  The reason is to ensure spacing of lines
     //       column headers when the only column header is "Restricted Until"
     printSubtrackListRadioButtons(parentTdb->track, subCount, displayAll);
     if (membersHaveMatrix(membersForAll))
 	makeTopLink(parentTdb);
     printf("</td></tr></table>");
     }