3162d1fec3dffece6f608f15eb6d46f6c46f7436 chmalee Fri Dec 5 16:13:05 2025 -0800 Working version of custom track group 'visible' that is at the top of the group list and has all the currently visible tracks, refs #36609 diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index 942c1730fef..2415e6f55e4 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -255,30 +255,36 @@ return 0; else return 1; } int gCmpPriority(const void *va, const void *vb) /* Compare groups based on priority, paying attention to hub groups */ { const struct group *a = *((struct group **)va); const struct group *b = *((struct group **)vb); float dif = a->priority - b->priority; int iDif = makeInt(dif); boolean aIsHub = startsWith("hub_", a->name); boolean bIsHub = startsWith("hub_", b->name); +// visible tracks sorts first always +if (sameString(a->name, "visible")) + return -1; +else if (sameString(b->name, "visible")) + return 1; + if (aIsHub) { if (bIsHub) { char *aNullPos = strchr(&a->name[4], '_'); char *bNullPos = strchr(&b->name[4], '_'); if ((aNullPos != NULL) && (bNullPos != NULL) ) *aNullPos = *bNullPos = 0; int strDiff = strcmp(a->name, b->name); int ret = 0; if (strDiff == 0) // same hub ret = iDif; else ret = strDiff; @@ -2179,30 +2185,65 @@ /* create list of codons in the specified coding frame */ sfList = baseColorCodonsFromDna(refFrame, winStart, winEnd, extraSeq, complementRulerBases); /* draw the codons in the list, with alternating colors */ baseColorDrawRulerCodons(hvg, sfList, scale, insideX, y, codonHeight, font, winStart, MAXPIXELS, zoomedToCodonLevel); } } } hvGfxUnclip(hvg); return y; } +static struct track *getVisibleTracks(struct track *trackList) +/* Return just the tracks that are currently on */ +{ +struct track *retList = NULL; +if (trackList == NULL) + return NULL; + +struct track *track; +for (track = trackList; track != NULL; track = track->next) + { + if (sameString(trackHubSkipHubName(track->track), "cytoBandIdeo")) + continue; + int vis = track->limitedVisSet ? track->limitedVis : track->visibility; + if (vis) + { + int subtrackVis = 0; + if (track->subtracks) + { + struct track *subtrack; + for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) + subtrackVis |= subtrack->limitedVisSet ? subtrack->limitedVis : subtrack->visibility; + vis &= subtrackVis; + } + if (vis) + { + struct track *clone = CloneVar(track); + clone->next = NULL; + slAddHead(&retList, clone); + } + } + } +// this will be sorted later, no need to reverse or sort now +return retList; +} + static void logTrackList(struct dyString *dy, struct track *trackList) /* add visibile tracks to dyString, recursively called */ { if (trackList == NULL) return; struct track *track; for (track = trackList; track != NULL; track = track->next) { int vis = track->limitedVisSet ? track->limitedVis : track->visibility; if (vis) { logTrackList(dy, track->subtracks); if (dy->stringSize) dyStringAppendC(dy, ','); @@ -7053,30 +7094,41 @@ // // If there isn't a map group, make one and set the priority so it will be right after custom tracks // and hub groups. if (!foundMap) { AllocVar(group); group->name = cloneString("map"); group->label = cloneString("Mapping and Sequencing"); group->defaultPriority = priority; group->priority = priority; group->defaultIsClosed = FALSE; slAddHead(&list, group); hashAdd(hash, "map", group); } +// The "Visible Tracks" group is now the default top group +struct group *visible = NULL; +AllocVar(visible); +visible->name = "visible"; +visible->label = "Visible Tracks"; +visible->defaultPriority = priority; +visible->priority = -1; +visible->defaultIsClosed = FALSE; +slAddHead(&list, visible); +hashAdd(hash, "visible", visible); + /* Loop through tracks and fill in their groups. * If necessary make up an unknown group. */ for (track = *pTrackList; track != NULL; track = track->next) { /* handle track reordering feature -- change group assigned to track */ if (withPriorityOverride) { char *groupName = NULL; char cartVar[256]; /* belt and suspenders -- accomodate inconsistent track/trackDb * creation. Note -- with code cleanup, these default variables * could be retired, and the tdb versions used as defaults */ if (!track->defaultGroupName) @@ -7154,67 +7206,72 @@ for (track = *pTrackList; track != NULL; track = track->next) { AllocVar(tr); tr->track = track; slAddHead(&track->group->trackList, tr); } /* Straighten things out, clean up, and go home. */ for (group = list; group != NULL; group = group->next) slReverse(&group->trackList); slSort(&list, gCmpPriority); hashFree(&hash); *pGroupList = list; } -void groupTrackListAddSuper(struct cart *cart, struct group *group, struct hash *superHash) +} +void groupTrackListAddSuper(struct cart *cart, struct group *group, struct hash *superHash, struct hash *trackHashRef) /* Construct a new track list that includes supertracks, sort by priority, * and determine if supertracks have visible members. * Replace the group track list with this new list. * Shared by hgTracks and configure page to expand track list, * in contexts where no track display functions (which don't understand - * supertracks) are invoked. */ + * supertracks) are invoked. + * In general, trackHashRef is just a pointer to the global trackHash, + * except in the case of building the Visible Tracks group, in which + * case it is a new hash, because we want super tracks duplicated into + * the visible tracks list and their normal group list */ { struct trackRef *newList = NULL, *tr, *ref; if (!group || !group->trackList) return; for (tr = group->trackList; tr != NULL; tr = tr->next) { struct track *track = tr->track; AllocVar(ref); ref->track = track; slAddHead(&newList, ref); if (tdbIsSuperTrackChild(track->tdb)) { assert(track->tdb->parentName != NULL); if (hTvFromString(cartUsualString(cart, track->track, hStringFromTv(track->tdb->visibility))) != tvHide) setSuperTrackHasVisibleMembers(track->tdb->parent); assert(track->parent == NULL); track->parent = hashFindVal(superHash, track->tdb->parentName); if (track->parent) continue; /* create track and reference for the supertrack */ struct track *superTrack = track->parent = trackFromTrackDb(track->tdb->parent); track->parent = superTrack; - if (trackHash != NULL) - hashAddUnique(trackHash,superTrack->track,superTrack); + if (trackHashRef != NULL) + hashAddUnique(trackHashRef,superTrack->track,superTrack); superTrack->hasUi = TRUE; - superTrack->group = group; - superTrack->groupName = cloneString(group->name); - superTrack->defaultGroupName = cloneString(group->name); + superTrack->group = track->group; + superTrack->groupName = cloneString(track->group->name); + superTrack->defaultGroupName = cloneString(track->group->name); /* handle track reordering */ char cartVar[256]; safef(cartVar, sizeof(cartVar), "%s.priority",track->tdb->parentName); float priority = (float)cartUsualDouble(cart, cartVar, track->tdb->parent->priority); /* remove cart variables that are the same as the trackDb settings */ if (priority == track->tdb->parent->priority) cartRemove(cart, cartVar); superTrack->priority = priority; AllocVar(ref); ref->track = superTrack; slAddHead(&newList, ref); hashAdd(superHash, track->tdb->parentName, superTrack); @@ -8821,30 +8878,53 @@ } if (cartUsualBoolean(cart, "dumpTracks", FALSE)) { struct dyString *dy = dyStringNew(1024); logTrackList(dy, trackList); printf("Content-type: text/html\n\n"); printf("%s\n", dy->string); exit(0); } if (sameString(cfgOptionDefault("trackLog", "off"), "on")) logTrackVisibilities(cartSessionId(cart), trackList, position); +struct track *visibleTracks = getVisibleTracks(trackList); +if (visibleTracks) + { + // add these tracks to the special 'visible' group + struct group *group, *visibleGroup = NULL; + for (group = groupList; group != NULL; group = group->next) + { + if (sameString(group->name, "visible")) + { + visibleGroup = group; + break; + } + } + struct track *t; + for (t = visibleTracks; t != NULL; t=t->next) + { + struct trackRef *tr; + AllocVar(tr); + tr->track = t; + slAddHead(&visibleGroup->trackList, tr); + } + slReverse(&visibleGroup->trackList); + } ///////////////// // NEED TO LOAD ALL WINDOWS NOW // // Need to load one window at a time! // // The use of the global values for a window // means that differerent threads cannot use different global window values. // Threads must run on just one window value at a time. // // Begin by making a copy of the track structure for visible tracks // for all windows. @@ -9079,30 +9159,41 @@ struct dyString *trackGroupsHidden2 = dyStringNew(1000); for (group = groupList; group != NULL; group = group->next) { if (group->trackList != NULL) { int looper; for (looper=1;looper<=2;looper++) { boolean isOpen = !isCollapsedGroup(group); char buf[1000]; safef(buf, sizeof(buf), "<input type='hidden' name=\"%s\" id=\"%s_%d\" value=\"%s\">\n", collapseGroupVar(group->name), collapseGroupVar(group->name), looper, isOpen ? "0" : "1"); dyStringAppend(looper == 1 ? trackGroupsHidden1 : trackGroupsHidden2, buf); } } + else if (sameString(group->name, "visible")) + { + boolean isOpen = !isCollapsedGroup(group); + char buf[1000]; + safef(buf, sizeof(buf), "<input type='hidden' name=\"%s\" id=\"%s_1\" value=\"%s\">\n", + collapseGroupVar(group->name), collapseGroupVar(group->name), isOpen ? "0" : "1"); + dyStringAppend(trackGroupsHidden1, buf); + safef(buf, sizeof(buf), "<input type='hidden' name=\"%s\" id=\"%s_2\" value=\"%s\">\n", + collapseGroupVar(group->name), collapseGroupVar(group->name), isOpen ? "0" : "1"); + dyStringAppend(trackGroupsHidden2, buf); + } } if (theImgBox) { // If a portal was established, then set the global dimensions back to the portal size if (imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL)) { virtWinBaseCount = virtWinEnd - virtWinStart; fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX; } } /* Center everything from now on. */ hPrintf("<CENTER>\n"); outCollectionsToJson(); @@ -9653,30 +9744,32 @@ /* check if group section should be displayed */ char *otherState; char *indicator; char *indicatorImg; boolean isOpen = !isCollapsedGroup(group); collapseGroupGoodies(isOpen, TRUE, &indicatorImg, &indicator, &otherState); hPrintf("<TR>"); cg->rowOpen = TRUE; if (group->errMessage) hPrintf("<th align=\"left\" colspan=%d class='errorToggleBar'>",MAX_CONTROL_COLUMNS); else if (startsWith("Hub", group->label)) hPrintf("<th align=\"left\" colspan=%d class='hubToggleBar'>",MAX_CONTROL_COLUMNS); else if (startsWith("QuickLift", group->label)) hPrintf("<th align=\"left\" colspan=%d class='quickToggleBar'>",MAX_CONTROL_COLUMNS); + else if (sameString("Visible Tracks", group->label)) + hPrintf("<th align=\"left\" colspan=%d class='visibleTracksToggleBar'>",MAX_CONTROL_COLUMNS); else hPrintf("<th align=\"left\" colspan=%d class='nativeToggleBar'>",MAX_CONTROL_COLUMNS); hPrintf("<table style='width:100%%;'><tr><td style='text-align:left;'>"); hPrintf("\n<A NAME=\"%sGroup\"></A>",group->name); char idText[256]; safef(idText, sizeof idText, "%s_button", group->name); hPrintf("<IMG class='toggleButton'" " id='%s' src=\"%s\" alt=\"%s\" title='%s this group'> ", idText, indicatorImg, indicator,isOpen?"Collapse":"Expand"); jsOnEventByIdF("click", idText, "return vis.toggleForGroup(this, '%s');", group->name); if (isHubTrack(group->name)) { @@ -9787,31 +9880,39 @@ showedRuler = TRUE; myControlGridStartCell(cg, isOpen, group->name, FALSE); hPrintf("<A HREF=\"%s\">", url); hPrintf(" %s<BR> ", RULER_TRACK_LABEL); hPrintf("</A>"); hDropListClassWithStyle("ruler", rulerMenu, sizeof(rulerMenu)/sizeof(char *), rulerMenu[rulerMode], rulerMode == tvHide ? "hiddenText" : "normalText", TV_DROPDOWN_STYLE); controlGridEndCell(cg); freeMem(url); } /* Add supertracks to track list, sort by priority and * determine if they have visible member tracks */ - groupTrackListAddSuper(cart, group, superHash); + if (sameString(group->name, "visible")) + { + // we want tracks in the visible list to also be visible + // in the normal group list, so use a separate hash for the + // visible tracks grouping + groupTrackListAddSuper(cart, group, hashNew(8), hashNew(8)); + } + else + groupTrackListAddSuper(cart, group, superHash, trackHash); /* Display track controls */ if (group->errMessage) { myControlGridStartCell(cg, isOpen, group->name, shouldBreakAll(group->errMessage)); hPrintf("%s", group->errMessage); controlGridEndCell(cg); } for (tr = group->trackList; tr != NULL; tr = tr->next) { struct track *track = tr->track; if (tdbIsSuperTrackChild(track->tdb)) /* don't display supertrack members */