546e4b62f9c8abdda3576ce5b9bf8c4b82dc6b4e
braney
  Tue Mar 21 14:42:01 2023 -0700
make squishyPack work nicely with Javascript track reorder

diff --git src/hg/hgTracks/imageV2.c src/hg/hgTracks/imageV2.c
index 8fdbdb6..cdfd9f2 100644
--- src/hg/hgTracks/imageV2.c
+++ src/hg/hgTracks/imageV2.c
@@ -21,60 +21,67 @@
 struct imgBox   *theImgBox   = NULL; // Make this global for now to avoid huge rewrite
 struct imgTrack *curImgTrack = NULL; // Make this global for now to avoid huge rewrite
 
 /////////////////////////
 // FLAT TRACKS
 // A simplistic way of flattening the track list before building the image
 // NOTE: Strategy is NOT to use imgBox->imgTracks, since this should be independednt of imageV2
 /////////////////////////
 void flatTracksAdd(struct flatTracks **flatTracks,struct track *track,struct cart *cart, struct slName *orderedWiggles)
 // Adds one track into the flatTracks list
 {
 struct flatTracks *flatTrack;
 AllocVar(flatTrack);
 flatTrack->track = track;
 char var[256];  // The whole reason to do this is to reorder tracks/subtracks in the image!
+if (track->originalTrack != NULL)
+    safef(var,sizeof(var),"%s_%s",track->originalTrack,IMG_ORDER_VAR);
+else
     safef(var,sizeof(var),"%s_%s",track->tdb->track,IMG_ORDER_VAR);
 flatTrack->order = cartUsualInt(cart, var,IMG_ANYORDER);
 if (flatTrack->order >= IMG_ORDERTOP)
     {
     cartRemove(cart,var);
     flatTrack->order = IMG_ANYORDER;
     }
 static int topOrder  = IMG_ORDERTOP; // keep track of the order added to top of image
 static int lastOrder = IMG_ORDEREND; // keep track of the order added and beyond end
 if ( flatTrack->order == IMG_ANYORDER)
     {
     int index;
     if (track->customTrack)
         flatTrack->order = ++topOrder; // Custom tracks go to top
     else if ((orderedWiggles != NULL) && ((index = slNameFindIx(orderedWiggles, track->track)) != -1))
         flatTrack->order = topOrder + index + 1;
     else
         flatTrack->order = ++lastOrder;
     }
 
 slAddHead(flatTracks,flatTrack);
 }
 
 int flatTracksCmp(const void *va, const void *vb)
 // Compare to sort on flatTrack->order
 {
 const struct flatTracks *a = *((struct flatTracks **)va);
 const struct flatTracks *b = *((struct flatTracks **)vb);
 if (a->order == b->order)
+    {
+    if ((a->track->originalTrack != NULL) || (b->track->originalTrack != NULL))
+        return a->track->visibility - b->track->visibility;
     return tgCmpPriority(&(a->track),&(b->track));
+    }
 return (a->order - b->order);
 }
 
 void flatTracksSort(struct flatTracks **flatTracks)
 // This routine sorts the imgTracks then forces tight ordering, so new tracks wil go to the end
 {
 // flatTracks list has 2 sets of "order": those already dragReordered (below IMG_ORDERTOP)
 // and those not yet reordered (above).  Within those not yet dragReordered are 2 sets:
 // Those that begin numbering at IMG_ORDERTOP and those that begin at IMG_ORDEREND.
 // This routine must determine if there are any already dragOrdered, and if so, position the
 // newcomers in place.  Newly appearing customTracks will appear at top, while newly appearing
 // standard tracks appear at the end of the image.
 int haveBeenOrderd = 0, imgOrdHighest=0; // Keep track of reordered count and position
 int notYetOrdered = 0, toBeTopHighest=0; // Keep track of those to be reordered, and top ordered
 
@@ -985,30 +992,32 @@
     }
 return imgTrack;
 }
 
 void imgTrackMarkForAjaxRetrieval(struct imgTrack *imgTrack,boolean ajaxRetrieval)
 // Updates the imgTrack to trigger an ajax callback from the html client to get this track
 {
 imgTrack->ajaxRetrieval = ajaxRetrieval;
 }
 
 int imgTrackOrderCmp(const void *va, const void *vb)
 // Compare to sort on imgTrack->order
 {
 const struct imgTrack *a = *((struct imgTrack **)va);
 const struct imgTrack *b = *((struct imgTrack **)vb);
+if (a->order == b->order)
+    return a->vis - b->vis;
 return (a->order - b->order);
 }
 
 struct imgSlice *imgTrackSliceAdd(struct imgTrack *imgTrack,enum sliceType type, struct image *img,
                                   char *title,int width,int height,int offsetX,int offsetY)
 // Adds slices to an image track.  Expected are types: stData, stButton, stSide and stCenter
 {
 struct imgSlice *slice = sliceCreate(type,img,title,width,height,offsetX,offsetY);
 if (slice)
     slAddHead(&(imgTrack->slices),slice);
 return imgTrack->slices;
 //slAddTail(&(imgTrack->slices),slice);
 //return slice;
 }
 
@@ -1966,30 +1975,62 @@
             }
         else
             hPrintf(" TITLE='Click for: 
%s'", attributeEncode(slice->title) );
         }
     hPrintf(">\n" );
     }
 
 imageDraw(imgBox,imgTrack,slice,name,offsetX,offsetY,useMap);
 if (slice->link != NULL)
     hPrintf("</A>");
 
 if (slice->parentImg)
     hPrintf("</div>");
 }
 
+struct imgTrack *smashSquish(struct imgTrack *imgTrackList)
+/* Javascript doesn't know about our trick to do squishyPack so we need to pass it a single div instead of two. 
+ * We assume that the linked track immediately follows the original track in the sorted list. */
+{
+struct imgTrack *nextImg, *imgTrack;
+
+for (imgTrack = imgTrackList; imgTrack!=NULL;imgTrack = nextImg)
+    {
+    nextImg = imgTrack->next;
+    boolean joinNext =  ((nextImg != NULL)  && nextImg->linked);
+
+    if (joinNext)  // Smash these together
+        {
+        struct imgSlice *packedSlices = imgTrack->slices;
+        struct imgSlice *squishSlices = imgTrack->next->slices;
+        for(; packedSlices; packedSlices = packedSlices->next, squishSlices = squishSlices->next)
+            {
+            if (packedSlices->type != stCenter)
+                {
+                if ((packedSlices->map != NULL) && (squishSlices->map != NULL))
+                    packedSlices->map->items = slCat(packedSlices->map->items, squishSlices->map->items);
+                packedSlices->height += squishSlices->height;
+                }
+            }
+        imgTrack->next = nextImg->next;
+        nextImg = nextImg->next;
+        }
+    }
+
+return imgTrackList;
+}
+
 void imageBoxDraw(struct imgBox *imgBox)
 // writes a entire imgBox including all tracksas HTML
 {
 if (imgBox->imgTracks == NULL) // Not an error to have an empty image
     return;
 imgBoxDropEmpties(imgBox);
 boolean verbose = (hIsPrivateHost());   // Warnings for hgwdev only
 if (!imgBoxIsComplete(imgBox,verbose)) // dorps empties as okay
     return;
 char name[256];
 
 imgBoxTracksNormalizeOrder(imgBox);
 //if (verbose)
 //    imgBoxShow(NULL,imgBox,0);
 
@@ -2032,31 +2073,31 @@
     jsonObjectAdd(jsonForClient,"imgBoxPortalOffsetX", newJsonNumber(
                   (long)((imgBox->portalStart - imgBox->chromStart) / imgBox->basesPerPixel)));
     jsonObjectAdd(jsonForClient,"imgBoxBasesPerPixel", newJsonDouble(imgBox->basesPerPixel));
     }
 else
     jsonObjectAdd(jsonForClient,"imgBoxPortal", newJsonBoolean(FALSE));
 
 hPrintf("<TABLE id='imgTbl' cellspacing='0' cellpadding='0'");
 hPrintf(" width='%d'",imgBox->showPortal?(imgBox->portalWidth+imgBox->sideLabelWidth):imgBox->width);
 hPrintf(" class='tableWithDragAndDrop'>\n");
 
 struct jsonElement *jsonTdbVars = newJsonObject(newHash(8));
 jsonTdbSettingsInit(jsonTdbVars);
 
 char *newLine = NEWLINE_TO_USE(cgiClientBrowser(NULL,NULL,NULL));
-struct imgTrack *imgTrack = imgBox->imgTracks;
+struct imgTrack *imgTrack = smashSquish(imgBox->imgTracks);
 for (;imgTrack!=NULL;imgTrack=imgTrack->next)
     {
     char *trackName = (imgTrack->name != NULL ? imgTrack->name : imgTrack->tdb->track );
     struct track *track = hashFindVal(trackHash, trackName);
     if (track)
         jsonTdbSettingsBuild(jsonTdbVars, track, TRUE);
     hPrintf("<TR id='tr_%s' abbr='%d' class='imgOrd%s%s%s'>\n",trackName,imgTrack->order,
             (imgTrack->reorderable ? " trDraggable" : " nodrop nodrag"),
             (imgTrack->centerLabelSeen != clAlways ? " clOpt" : ""),
             (imgTrack->ajaxRetrieval ? " mustRetrieve" : ""));
 
     if (imgBox->showSideLabel && imgBox->plusStrand)
         {
         // button
         safef(name, sizeof(name), "btn_%s", trackName);