86a4a9bd5fd7e7be7bb912172b862485c0c7bb4e
galt
  Thu Apr 12 11:03:57 2018 -0700
Need to set limitedVis consistently across all windows after loading tracks. fixes #21267

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 6aa2777..89474c8 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -1458,31 +1458,30 @@
 static void doLabelNextItemButtons(struct track *track, struct track *parentTrack,
                                    struct hvGfx *hvg, MgFont *font, int y,
                                    int trackPastTabX, int trackPastTabWidth, int fontHeight,
                                    int insideHeight, Color labelColor)
 /* If the track allows label next-item buttons (next gene), draw them. */
 /* The button will cause hgTracks to run again with the additional CGI */
 /* vars nextItem=trackName or prevItem=trackName, which will then  */
 /* signal the browser to find the next thing on the track before it */
 /* does anything else. */
 {
 int portWidth = fullInsideWidth;
 int portX = fullInsideX;
 // If a portal was established, then set the portal dimensions
 long portalStart,chromStart;
 double basesPerPixel;
-// TODO GALT need to tweak it still for virtchrom stuff, e.g. maybe change some var names or types to long
 if (theImgBox
 && imgBoxPortalDimensions(theImgBox,&chromStart,NULL,NULL,NULL,&portalStart,NULL,
                           &portWidth,&basesPerPixel))
     {
     portX = (int)((portalStart - chromStart) / basesPerPixel);
     portX += gfxBorder;
     if (withLeftLabels)
         portX += tl.leftLabelWidth + gfxBorder;
     portWidth = portWidth-gfxBorder-insideX;
     }
 int arrowWidth = insideHeight;
 int arrowButtonWidth = arrowWidth + 2 * NEXT_ITEM_ARROW_BUFFER;
 int rightButtonX = portX + portWidth - arrowButtonWidth - 1;
 char buttonText[256];
 Color fillColor = lightGrayIndex();
@@ -4722,31 +4721,33 @@
 // but before things are checked for overflow or limitedVis?
 // The fixed non-overflow tracks like knownGene used to initialize 
 // ss and track height during loadItems(). That was delayed
 // because we now need all windows to be fully loaded before
 // calculating their joint ss layout and height.
 
 // set heights and visibilities.
 struct window *window = NULL;
 for(window=windows;window;window=window->next)
     {
     setGlobalsFromWindow(window);
     trackList = window->trackList;
     for (track = trackList; track != NULL; track = track->next)
 	{
 	if (tdbIsCompositeChild(track->tdb)) // When single track is requested via AJAX,
+	    {
 	    limitedVisFromComposite(track);  // it could be a subtrack
+	    }
 	else
 	    {
 	    limitVisibility(track);
 	    }
 
 	if (tdbIsComposite(track->tdb))
 	    {
 	    struct track *subtrack;
 	    for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
 		{
 		if (!isSubtrackVisible(subtrack))
 		    continue;
 
 		// subtrack vis can be explicit or inherited from composite/view.
 		// Then it could be limited because of pixel height
@@ -5350,32 +5351,30 @@
             (void) sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot
             if (isCenterLabelConditional(track)) // sometimes calls track height, especially when no data there
 		{
                 imgTrackUpdateCenterLabelSeen(curImgTrack,isCenterLabelConditionallySeen(track) ?
                                                                             clNowSeen : clNotSeen);
 		}
             }
         if (trackShouldUseAjaxRetrieval(track))
 	    {
             y += REMOTE_TRACK_HEIGHT;
 	    }
         else
 	    { 
 	    int savey = y; // GALT
             y = doCenterLabels(track, track, hvg, font, y, fullInsideWidth); // calls track height
-	    // TODO GALT why do I just pass track here instead of parentTrack? Did I lose something?
-	    // have to look at old code to see.
 	    y = savey + flatTrack->maxHeight;
 	    }
         }
     hvGfxUnclip(hvg);
 
     setGlobalsFromWindow(windows); // first window
     }
 
 
 
 /* Draw tracks. */
 
     { // brace allows local vars
     
     long lastTime = 0;
@@ -5469,95 +5468,89 @@
 
         if (yEnd != y)
             warn("Slice height for track %s does not add up.  Expecting %d != %d actual",
                  track->shortLabel, yEnd - yStart - 1, y - yStart);
         }
 
     calcWiggleOrdering(cart, flatTracks);
     y++;
     }
 
 /* post draw tracks leftLabels */
 
 /* if a track can draw its left labels, now is the time since it
  *  knows what exactly happened during drawItems
  */
-// TODO GALT Parellelize or not?
 if (withLeftLabels)
     {
     y = yAfterRuler;
     for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
         {
         track = flatTrack->track;
         if (track->limitedVis == tvHide)
             continue;
         if (theImgBox)
             {
             // side label slice of tracks
-            // ORIG sliceHeight      = trackPlusLabelHeight(track, fontHeight);
 	    sliceHeight      = flatTrack->maxHeight;
             sliceOffsetY     = y;
             curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
             curSlice    = imgTrackSliceUpdateOrAdd(curImgTrack,stSide,theSideImg,NULL,
                                                    sliceWidth[stSide],sliceHeight,
                                                    sliceOffsetX[stSide],sliceOffsetY);
             (void) sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot
             }
 
         if (trackShouldUseAjaxRetrieval(track))
             y += REMOTE_TRACK_HEIGHT;
     #ifdef IMAGEv2_NO_LEFTLABEL_ON_FULL
         else if (track->drawLeftLabels != NULL
              &&  (theImgBox == NULL || track->limitedVis == tvDense))
     #else ///ndef IMAGEv2_NO_LEFTLABEL_ON_FULL
         else if (track->drawLeftLabels != NULL)
     #endif ///ndef IMAGEv2_NO_LEFTLABEL_ON_FULL
-	    {  // TODO parallelize?
+	    {
 	    setGlobalsFromWindow(windows);
             y = doOwnLeftLabels(track, hvgSide, font, y);
 	    setGlobalsFromWindow(windows); // first window
 	    }
         else
-            // ORIG y += trackPlusLabelHeight(track, fontHeight);
 	    y += flatTrack->maxHeight;
         }
     }
 
 
 
 /* Make map background. */
 
 y = yAfterRuler;
 for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
     {
     track = flatTrack->track;
     if (track->limitedVis != tvHide)
         {
         if (theImgBox)
             {
             // Set imgTrack in case any map items will be set
-            // ORIG sliceHeight      = trackPlusLabelHeight(track, fontHeight);
 	    sliceHeight      = flatTrack->maxHeight;
             sliceOffsetY     = y;
             curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
             }
 
-	// TODO Parallelize?
 	setGlobalsFromWindow(windows); // first window
         doTrackMap(track, hvg, y, fontHeight, trackPastTabX, trackPastTabWidth);
 
-        // ORIG y += trackPlusLabelHeight(track, fontHeight);
 	y += flatTrack->maxHeight;
         }
     }
 
 /* Finish map. */
 hPrintf("</MAP>\n");
 
 // turn off inPlaceUpdate when rows in imgTbl can arbitrarily reappear and disappear (see redmine #7306 and #6944)
 jsonObjectAdd(jsonForClient, "inPlaceUpdate", newJsonBoolean(withLeftLabels && withCenterLabels));
 jsonObjectAdd(jsonForClient, "rulerClickHeight", newJsonNumber(rulerClickHeight));
 if(newWinWidth)
     {
     jsonObjectAdd(jsonForClient, "newWinWidth", newJsonNumber(newWinWidth));
     }
 
@@ -6745,31 +6738,30 @@
     /* or maybe errabort ? */
     label[3] = 0;
     len = 4;
     }
 if (len % 2 != 0)
     paddedLabel[3] = 0;
 if (len == strlen(paddedLabel))
     strcpy(paddedLabel, label);
 else
     {
     int i;
     for (i=0; i<len; i++)
         paddedLabel[i+1] = label[i];
     }
 hButtonWithOnClick(var, paddedLabel, NULL, "return imageV2.navigateButtonClick(this);");
-// TODO GALT could consider trying to give these all the same class and then attach handlers at the class level.
 }
 
 void limitSuperTrackVis(struct track *track)
 /* Limit track visibility by supertrack parent */
 {
 if (tdbIsSuperTrackChild(track->tdb))
     {
     assert(track->tdb->parent != NULL);
     if (sameString("hide", cartUsualString(cart, track->tdb->parent->track,
                                            track->tdb->parent->isShow ? "show" : "hide")))
         track->visibility = tvHide;
     }
 }
 
 struct track *rFindTrackWithTable(char *tableName, struct track *trackList)
@@ -7477,30 +7469,54 @@
     }
 }
 
 boolean windowsHaveMultipleChroms()
 /* Are there multiple different chromosomes in the windows list? */
 {
 struct window *window;
 for (window=windows->next; window; window=window->next)
     {
     if (!sameString(window->chromName,windows->chromName))
 	return TRUE;
     }
 return FALSE;
 }
 
+static void setSharedLimitedVisAcrossWindows(struct track *track)
+/* Look for lowest limitedVis across all windows
+ * if found, set all windows to same lowest limited vis. */
+{
+enum trackVisibility sharedVis = 99;
+struct track *tg;
+for (tg=track; tg; tg=tg->nextWindow)
+    {
+    if (tg->limitedVisSet)
+	{
+	if (tg->limitedVis < sharedVis)
+	    sharedVis = tg->limitedVis;
+	}
+    }
+if (sharedVis != 99)
+    {
+    for (tg=track; tg; tg=tg->nextWindow)
+	{
+	tg->limitedVis = sharedVis;
+	tg->limitedVisSet = TRUE;
+	}
+    }
+}
+
 static void setSharedErrorsAcrossWindows(struct track *track)
 /* Look for network errors across all windows
  * if found, set all windows to same errMsg and set bigWarn track handlers. */
 {
 char *sharedErrMsg = NULL;
 struct track *tg;
 for (tg=track; tg; tg=tg->nextWindow)
     {
     if (!sharedErrMsg && tg->networkErrMsg)
 	{
 	sharedErrMsg = tg->networkErrMsg;
 	break;
 	}
     }
 if (sharedErrMsg)
@@ -7837,43 +7853,54 @@
 			// TODO does this work for subtracks or parents/children?
 			}
 		    }
 
 		if (measureTiming)
 		    {
 		    thisTime = clock1000();
 		    track->loadTime = thisTime - lastTime;
 		    }
 		}
 	    }
 	}
 
     if (ptMax > 0)
 	{
-	// TODO GALT parallel actually not sure if anything to worry about here
 	/* wait for remote parallel load to finish */
 	remoteParallelLoadWait(atoi(cfgOptionDefault("parallelFetch.timeout", "90")));  // wait up to default 90 seconds.
 	if (measureTiming)
 	    measureTime("Waiting for parallel (%d threads for %d tracks) remote data fetch", ptMax, pfdListCount);
 	}
 
     }
 trackLoadingInProgress = FALSE;
 
 setGlobalsFromWindow(windows); // first window // restore globals
 trackList = windows->trackList;  // restore track list
 
+// Some loadItems() calls will have already set limitedVis.
+// Look for lowest limitedVis across all windows
+// if found, set all windows to same lowest limitedVis 
+for (track = trackList; track != NULL; track = track->next)
+    {
+    setSharedLimitedVisAcrossWindows(track);
+    struct track *sub;
+    for (sub=track->subtracks; sub; sub=sub->next)
+	{
+	setSharedLimitedVisAcrossWindows(sub);
+	}
+    }
 
 // Look for network errors across all windows
 // if found, set all windows to same errMsg and set bigWarn track handlers.
 for (track = trackList; track != NULL; track = track->next)
     {
     setSharedErrorsAcrossWindows(track);
     struct track *sub;
     for (sub=track->subtracks; sub; sub=sub->next)
 	{
 	setSharedErrorsAcrossWindows(sub);
 	}
     }
 
 
 
@@ -8450,31 +8477,30 @@
 		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);
 
-	    // TODO GALT probably nothing to do here
 	    /* Display track controls */
 	    for (tr = group->trackList; tr != NULL; tr = tr->next)
 		{
 		struct track *track = tr->track;
 		if (tdbIsSuperTrackChild(track->tdb))
 		    /* don't display supertrack members */
 		    continue;
 		myControlGridStartCell(cg, isOpen, group->name);
 		if (track->hasUi)
 		    {
 		    char *url = trackUrl(track->track, chromName);
 		    char *longLabel = replaceChars(track->longLabel, "\"", "&quot;");
                     hPrintPennantIcon(track->tdb);
 
                     // Print an icon before the title when one is defined
@@ -9373,31 +9399,30 @@
 cartSetString(cart, "org", organism);
 cartSetString(cart, "db", database);
 
 char newPos[256];
 
 // disguise the cart pos var
 if (virtualSingleChrom()) // DISGUISE VMODE
     safef(newPos, sizeof newPos, "%s", windowsSpanPosition());
 else // usual
     safef(newPos, sizeof newPos, "%s:%ld-%ld", virtChromName, virtWinStart+1, virtWinEnd);
 
 position = cloneString(newPos);
 cartSetString(cart, "position", position);
 cartSetString(cart, "oldPosition", position);
 //cartSetString(cart, "lastPosition", position);  // this is set in cart.c
-// TODO GALT is it possible and worthwhile to just use lastPosition instead of oldPosition?
 
 cartSetBoolean(cart, "virtMode", virtMode);
 cartSetString(cart, "virtModeType", virtModeType); 
 virtModeType = cartString(cart, "virtModeType"); // refresh the pointer after changing hash
 
 
 lastVirtModeType=virtModeType;
 cartSetString(cart, "lastVirtModeType", lastVirtModeType);
 lastVirtModeType = cartString(cart, "lastVirtModeType"); // refresh 
 
 lastVirtModeExtraState=virtModeExtraState;
 cartSetString(cart, "lastVirtModeExtraState", lastVirtModeExtraState);
 lastVirtModeExtraState = cartString(cart, "lastVirtModeExtraState"); // refresh