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;