530842acf922e67838eab9fa153b7b7c0b90e9c2
braney
  Thu Apr 18 12:31:31 2024 -0700
allow track hubs to make groups

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 2ffb83a..06ef375 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -232,45 +232,81 @@
 else if (dif == 0.0)
     /* secondary sort on label */
     return strcasecmp(a->shortLabel, b->shortLabel);
 else
    return 1;
 }
 
 int trackRefCmpPriority(const void *va, const void *vb)
 /* Compare based on priority. */
 {
 const struct trackRef *a = *((struct trackRef **)va);
 const struct trackRef *b = *((struct trackRef **)vb);
 return tgCmpPriority(&a->track, &b->track);
 }
 
+static int makeInt(double input)
+/* make an int out of an input.  Probably should use floor() and such. */
+{
+if (input == 0)
+    return 0;
+if (input < 0)
+   return -1;
+else if (input == 0.0)
+   return 0;
+else
+   return 1;
+}
+
 int gCmpPriority(const void *va, const void *vb)
-/* Compare groups based on priority. */
+/* 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);
 
-if (dif == 0)
-    return 0;
-if (dif < 0)
-   return -1;
-else if (dif == 0.0)
-   return 0;
+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;
+
+        if ((aNullPos != NULL) && (bNullPos != NULL) )
+            *aNullPos = *bNullPos = '_';
+
+        return ret;
+        }
     else
+        return -1;
+    }
+else if (bIsHub)
     return 1;
+
+return iDif;
 }
 
 void changeTrackVisExclude(struct group *groupList, char *groupTarget, int changeVis, struct hash *excludeHash)
 /* Change track visibilities. If groupTarget is
  * NULL then set visibility for tracks in all groups.  Otherwise,
  * just set it for the given group.  If vis is -2, then visibility is
  * unchanged.  If -1 then set visibility to default, otherwise it should
  * be tvHide, tvDense, etc.
  * If we are going back to default visibility, then reset the track
  * ordering also. 
  * If excludeHash is not NULL then don't change the visibility of the group names in that hash.
  */
 {
 struct group *group;
 if (changeVis == -2)
@@ -6861,34 +6897,37 @@
     rPropagateGroup(subtrack, group);
     }
 }
 
 static void groupTracks(struct track **pTrackList,
 	struct group **pGroupList, struct grp *grpList, int vis)
 /* Make up groups and assign tracks to groups.
  * If vis is -1, restore default groups to tracks. */
 {
 struct group *unknown = NULL;
 struct group *group, *list = NULL;
 struct hash *hash = newHash(8);
 struct track *track;
 struct trackRef *tr;
 struct grp* grps = hLoadGrps(database);
+struct grp* hubGrps = trackHubGetGrps();
 struct grp *grp;
 float minPriority = 100000; // something really large
 boolean foundMap = FALSE;
 
+grps = slCat(grps, hubGrps);
+
 /* build group objects from database. */
 for (grp = grps; grp != NULL; grp = grp->next)
     {
     if (sameString(grp->name, "map"))
         foundMap = TRUE;
     /* deal with group reordering */
     float priority = grp->priority;
     // we want to get the minimum priority over 1 (which is custom tracks)
     if ((priority > 1.0) && (priority < minPriority)) minPriority = priority;
     if (withPriorityOverride)
         {
         char cartVar[512];
         safef(cartVar, sizeof(cartVar), "%s.priority",grp->name);
         if (vis != -1)
             priority = (float)cartUsualDouble(cart, cartVar, grp->priority);
@@ -9428,30 +9467,32 @@
 		continue;
 
 	    struct trackRef *tr;
 
 	    /* 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='redToggleBar'>",MAX_CONTROL_COLUMNS);
+            else if (startsWith("Hub", group->label))
+                hPrintf("<th align=\"left\" colspan=%d class='mauveToggleBar'>",MAX_CONTROL_COLUMNS);
             else if (startsWith("Quicklift", group->label))
                 hPrintf("<th align=\"left\" colspan=%d class='greenToggleBar'>",MAX_CONTROL_COLUMNS);
             else
                 hPrintf("<th align=\"left\" colspan=%d class='blueToggleBar'>",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'>&nbsp;&nbsp;",
                     idText, indicatorImg, indicator,isOpen?"Collapse":"Expand");
 	    jsOnEventByIdF("click", idText, "return vis.toggleForGroup(this, '%s');", group->name);
 
             if (isHubTrack(group->name))