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

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index e883494..0c6e1e3 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -180,30 +180,31 @@
 struct track *track;
 for (track = tracks; track != NULL; track = track->next)
     {
     if (sameString(track->track, trackName))
         return track;
     else if (track->subtracks != NULL)
         {
         struct track *st = trackFindByName(track->subtracks, trackName);
         if (st != NULL)
             return st;
         }
     }
 return NULL;
 }
 
+
 int tgCmpPriority(const void *va, const void *vb)
 /* Compare to sort based on priority; use shortLabel as secondary sort key. */
 {
 const struct track *a = *((struct track **)va);
 const struct track *b = *((struct track **)vb);
 float dif = 0;
 if (a->group && b->group)
     dif = a->group->priority - b->group->priority;
 if (dif == 0)
     dif = a->priority - b->priority;
 if (dif < 0)
    return -1;
 else if (dif == 0.0)
     /* secondary sort on label */
     return strcasecmp(a->shortLabel, b->shortLabel);
@@ -4557,142 +4558,89 @@
 struct window *window;
 for (window=windows, winTrack=track; window; window=window->next, winTrack=winTrack->nextWindow)
     {
     setGlobalsFromWindow(window);
 
     int trackHeight =  trackPlusLabelHeight(winTrack, fontHeight);
 
     if (trackHeight > maxHeight)
 	maxHeight = trackHeight;
     }
 setGlobalsFromWindow(windows); // first window
 
 flatTrack->maxHeight = maxHeight;
 }
 
-char *hideEmptySubtracksSetting(struct track *track)
-/* Setting to suppress display of empty subtracks */
-{
-if (!tdbIsComposite(track->tdb))
-    return FALSE;
-char *collapse = trackDbSetting(track->tdb, "hideEmptySubtracks");
-if (!collapse)
-    // previous syntax (not documented, but used by Regeneron)
-    collapse = trackDbSetting(track->tdb, "collapseEmptySubtracks");
-return (collapse);
-}
-
-boolean doHideEmptySubtracksNoMultiBed(struct track *track)
-/* TRUE if hideEmptySubtracks setting is 'on'  or 'true' (no file specs for multiBed */
-{
-char *setting = hideEmptySubtracksSetting(track);
-if (setting && (sameString(setting, "on") || sameString(setting, "true")))
-    return TRUE;
-return FALSE;
-}
-
-boolean doHideEmptySubtracks(struct track *track, char **multiBedFile, char **subtrackIdFile)
-{
-/* Support setting to suppress display of empty subtracks. 
- * (Initial support only for bed's).
- * 
- * Format:  hideEmptySubtracks on
- *              or
- *          hideEmptySubtracks 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
- */
-char *collapse = hideEmptySubtracksSetting(track);
-if (!collapse)
-    return FALSE;
-if (doHideEmptySubtracksNoMultiBed(track))
-    return TRUE;
-char *orig = cloneString(collapse);
-char *words[2];
-int wordCount = chopByWhite(collapse, words, ArraySize(words));
-if (wordCount == 2)
+boolean doHideEmptySubtracksNoMultiBed(struct cart *cart, struct track *track)
+/* TRUE if hideEmptySubtracks is enabled, but there is no multiBed */
 {
-    if (multiBedFile)
-        *multiBedFile = cloneString(hReplaceGbdb(words[0]));
-    if (subtrackIdFile)
-        *subtrackIdFile = cloneString(words[1]);
+char *multiBedFile = NULL;
+char *subtrackIdFile = NULL;
+boolean hideEmpties = compositeHideEmptySubtracks(cart, track->tdb, &multiBedFile, &subtrackIdFile);
+if (hideEmpties && (multiBedFile == NULL || subtrackIdFile == NULL))
         return TRUE;
-    }
-warn("Track %s hideEmptySubtracks setting invalid: %s",
-                track->track, orig);
 return FALSE;
 }
 
 struct hash *getNonEmptySubtracks(struct track *track)
 {
 /* Support setting to suppress display of empty subtracks. 
- * (Initial support only for bed's).
- * 
- * Format:  hideEmptySubtracks on
- *              or
- *          hideEmptySubtracks 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 hash with subtrack names as keys
+ * If multiBed is available, return hash with subtrack names as keys
  */
 
 char *multiBedFile = NULL;
-char *subtracksIdFile = NULL;
-if (!doHideEmptySubtracks(track, &multiBedFile, &subtracksIdFile))
+char *subtrackIdFile = NULL;
+if (!compositeHideEmptySubtracks(cart, track->tdb, &multiBedFile, &subtrackIdFile))
     return NULL;
 if (!multiBedFile)
     return NULL;
 
 // load multiBed items in window
 // TODO: filters here ?
 // TODO:  protect against temporary network error ? */
 struct lm *lm = lmInit(0);
 struct bbiFile *bbi = bigBedFileOpen(multiBedFile);
 struct bigBedInterval *bb, *bbList =  bigBedIntervalQuery(bbi, chromName, winStart, winEnd, 0, lm);
 char *row[bbi->fieldCount];
 char startBuf[16], endBuf[16];
 struct hash *nonEmptySubtracksHash = hashNew(0);
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bigBedIntervalToRow(bb, chromName, startBuf, endBuf, row, ArraySize(row));
     // TODO: do this in bed3Sources.c
     char *idList = row[4];
     struct slName *ids = slNameListFromComma(idList);
     struct slName *id = NULL;
     for (id = ids; id != NULL; id = id->next)
         hashStore(nonEmptySubtracksHash, id->name);
     // TODO: free some stuff ?
     }
 
 // read file containing ids of subtracks 
-struct lineFile *lf = udcWrapShortLineFile(subtracksIdFile, NULL, 0); 
+struct lineFile *lf = udcWrapShortLineFile(subtrackIdFile, NULL, 0); 
 char *words[2];
 while (lineFileChopNext(lf, words, sizeof words))
     {
     char *id = words[0];
     char *name = words[1];
     if (hashLookup(nonEmptySubtracksHash, id))
         {
         hashStore(nonEmptySubtracksHash, cloneString(name));
         }
     }
 lineFileClose(&lf);
 return nonEmptySubtracksHash;
-
 }
 
 void makeActiveImage(struct track *trackList, char *psOutput)
 /* Make image and image map. */
 {
 struct track *track;
 MgFont *font = tl.font;
 struct hvGfx *hvg;
 struct tempName pngTn;
 char *mapName = "map";
 int fontHeight = mgFontLineHeight(font);
 int trackPastTabX = (withLeftLabels ? trackTabWidth : 0);
 int trackTabX = gfxBorder;
 int trackPastTabWidth = tl.picWidth - trackPastTabX;
 int pixWidth, pixHeight;
@@ -4897,31 +4845,31 @@
         safef(buffer, sizeof buffer,  "%s_imgOrd", name->name);
         cartRemove(cart, buffer);
         }
     }
 
 // Construct flatTracks
 for (track = trackList; track != NULL; track = track->next)
     {
     if (tdbIsComposite(track->tdb))
         {
         struct track *subtrack;
         if (isCompositeInAggregate(track))
             flatTracksAdd(&flatTracks, track, cart, orderedWiggles);
         else
             {
-            boolean doHideEmpties = doHideEmptySubtracksNoMultiBed(track);
+            boolean doHideEmpties = doHideEmptySubtracksNoMultiBed(cart, track);
                 // If multibed was found, it has been used to suppress loading,
                 // and subtracks lacking items in window are already set hidden
             for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
                 {
                 if (!isSubtrackVisible(subtrack))
                     continue;
 
                 if (!isLimitedVisHiddenForAllWindows(subtrack) && 
                         !(doHideEmpties && slCount(subtrack->items) == 0))
                         // Ignore subtracks with no items in window
                     {
                     flatTracksAdd(&flatTracks,subtrack,cart, orderedWiggles);
                     }
                 }
             }
@@ -7216,52 +7164,30 @@
 	    subtrack->drawItems = drawMaxWindowWarning;
 	    subtrack->limitedVis = tvDense;
 	    subtrack->limitedVisSet = TRUE;
 	    }
 	}
     }
 else if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw)
     {
     tg->loadItems = dontLoadItems;
     tg->drawItems = drawMaxWindowWarning;
     tg->limitedVis = tvDense;
     tg->limitedVisSet = TRUE;
     }
 }
 
-static void checkHideEmptySubtracks(struct track *tg)
-/* Suppress queries on subtracks w/o data in window (identified from multiIntersect file) */
-{
-if (!tdbIsComposite(tg->tdb))
-    return;
-struct hash *nonEmptySubtracksHash = getNonEmptySubtracks(tg);
-if (!nonEmptySubtracksHash)
-    return;
-struct track *subtrack;
-for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next)
-    {
-    if (!isSubtrackVisible(subtrack))
-        continue;
-    if (!hashLookup(nonEmptySubtracksHash, subtrack->track))
-        {
-        subtrack->loadItems = dontLoadItems;
-        subtrack->limitedVis = tvHide;
-        subtrack->limitedVisSet = TRUE;
-        }
-    }
-}
-
 void printTrackInitJavascript(struct track *trackList)
 {
 hPrintf("<input type='hidden' id='%s' name='%s' value=''>\n", hgtJsCommand, hgtJsCommand);
 }
 
 void jsCommandDispatch(char *command, struct track *trackList)
 /* Dispatch a command sent to us from some javaScript event.
  * This gets executed after the track list is built, but before
  * the track->loadItems methods are called.  */
 {
 if (startsWithWord("makeItems", command))
     makeItemsJsCommand(command, trackList, trackHash);
 else
     warn("Unrecognized jsCommand %s", command);
 }
@@ -7366,30 +7292,52 @@
 		    struct paraFetchData *pfd;
 		    AllocVar(pfd);
 		    pfd->track = subtrack;  // need pointer to be stable
 		    slAddHead(ppfdList, pfd);
 		    subtrack->parallelLoading = TRUE;
 		    }
 		}
 	    }
 	}
     }
 }
 
 static pthread_mutex_t pfdMutex = PTHREAD_MUTEX_INITIALIZER;
 static struct paraFetchData *pfdList = NULL, *pfdRunning = NULL, *pfdDone = NULL, *pfdNeverStarted = NULL;
 
+static void checkHideEmptySubtracks(struct track *tg)
+/* Suppress queries on subtracks w/o data in window (identified from multiIntersect file) */
+{
+if (!tdbIsComposite(tg->tdb))
+    return;
+struct hash *nonEmptySubtracksHash = getNonEmptySubtracks(tg);
+if (!nonEmptySubtracksHash)
+    return;
+struct track *subtrack;
+for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next)
+    {
+    if (!isSubtrackVisible(subtrack))
+        continue;
+    if (!hashLookup(nonEmptySubtracksHash, subtrack->track))
+        {
+        subtrack->loadItems = dontLoadItems;
+        subtrack->limitedVis = tvHide;
+        subtrack->limitedVisSet = TRUE;
+        }
+    }
+}
+
 static void *remoteParallelLoad(void *threadParam)
 /* Each thread loads tracks in parallel until all work is done. */
 {
 pthread_t *pthread = threadParam;
 struct paraFetchData *pfd = NULL;
 pthread_detach(*pthread);  // this thread will never join back with it's progenitor
     // Canceled threads that might leave locks behind,
     // so the theads are detached and will be neither joined nor canceled.
 boolean allDone = FALSE;
 while(1)
     {
     pthread_mutex_lock( &pfdMutex );
     if (!pfdList)
 	{
 	allDone = TRUE;