2dff2aae844982e070bdcb424b672db2aa203339
braney
  Wed Feb 28 08:30:59 2018 -0800
fix sorting in visible tracks

diff --git src/hg/hgCollection/hgCollection.c src/hg/hgCollection/hgCollection.c
index d659b7b..98cc2ab 100644
--- src/hg/hgCollection/hgCollection.c
+++ src/hg/hgCollection/hgCollection.c
@@ -31,30 +31,31 @@
 struct track *trackList;
 struct trackDb *tdb;
 char *name;
 char *shortLabel;
 char *longLabel;
 char *visibility;
 unsigned long color;
 char *viewFunc;        // The method by which calculated tracks should be calculated
 char *missingMethod;   // How should missing data be treated in calculated tracks
 };
 
 struct trackDbRef 
 {
 struct trackDbRef *next;
 struct trackDb *tdb;
+struct grp *grp;
 int order;
 };
 
 static char *makeUnique(struct hash *nameHash, char *name)
 // Make the name of this track unique.
 {
 char *skipHub = trackHubSkipHubName(name);
 if (hashLookup(nameHash, skipHub) == NULL)
     {
     hashStore(nameHash, name);
     return skipHub;
     }
 
 unsigned count = 0;
 char buffer[4096];
@@ -173,84 +174,90 @@
     return FALSE;
 
 char *cartVis = cartOptionalString(cart, tdb->parent->track);
 boolean vis;
 if (cartVis != NULL) 
     vis =  differentString(cartVis, "hide");
 else if (tdbIsSuperTrack(tdb->parent))
     vis = tdb->parent->isShow;
 else
     vis = tdb->parent->visibility != tvHide;
 
 return vis;
 }
 
 
-static void checkForVisible(struct cart *cart, struct trackDbRef **list, struct trackDb *tdb)
+static void checkForVisible(struct cart *cart, struct hash *groupHash,  struct trackDbRef **list, struct trackDb *tdb)
 /* Walk the trackDb hierarchy looking for visible leaf tracks. */
 {
 struct trackDb *subTdb;
 char buffer[4096];
 
 if (tdb->subtracks)
     {
     for(subTdb = tdb->subtracks; subTdb; subTdb = subTdb->next)
-        checkForVisible(cart, list, subTdb);
+        checkForVisible(cart, groupHash,  list, subTdb);
     }
 else
     {
     boolean isVisible = FALSE;
     if (tdb->parent == NULL) 
         {
         char *cartVis = cartOptionalString(cart, tdb->track);
         if (cartVis == NULL)
             isVisible =  tdb->visibility != tvHide;
         else
             isVisible =  differentString(cartVis, "hide");
         }
     else if (isParentVisible(cart, tdb) &&  isSubtrackVisible(cart, tdb))
         isVisible = TRUE;
 
     if (isVisible)
         {
         struct trackDbRef *tdbRef;
         AllocVar(tdbRef);
         tdbRef->tdb = tdb;
+        tdbRef->grp = hashMustFindVal(groupHash, tdb->grp);
         slAddHead(list, tdbRef);
         safef(buffer, sizeof buffer, "%s_imgOrd", tdb->track);
 
         tdbRef->order = cartUsualInt(cart, buffer, tdb->priority);
         }
     }
 }
 
 static int tdbRefCompare (const void *va, const void *vb)
 // Compare to sort on imgTrack->order.
 {
 const struct trackDbRef *a = *((struct trackDbRef **)va);
 const struct trackDbRef *b = *((struct trackDbRef **)vb);
+
+int dif = a->grp->priority - b->grp->priority;
+if (dif == 0)
     return (a->order - b->order);
+
+return dif;
 }       
 
-static void addVisibleTracks(struct dyString *rootChildren, struct cart *cart, struct trackDb *trackList)
+static void addVisibleTracks(struct hash *groupHash, struct dyString *rootChildren, struct cart *cart, struct trackDb *trackList)
 // add the visible tracks table rows.
 {
 struct trackDb *tdb;
 struct trackDbRef *tdbRefList = NULL, *tdbRef;
 for(tdb = trackList; tdb; tdb = tdb->next)
     {
-    checkForVisible(cart, &tdbRefList, tdb);
+    checkForVisible(cart, groupHash,  &tdbRefList, tdb);
     }
 
 slSort(&tdbRefList, tdbRefCompare);
 if (!isEmpty(rootChildren->string))
     dyStringPrintf(rootChildren, ",");
 dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'visible', text:'Visible Tracks', parent:'#'");
 if (tdbRefList != NULL)
     dyStringPrintf(rootChildren, ",children:true");
 dyStringPrintf(rootChildren, "}");
 
 jsInlineF("trackData['visible'] = [");
 for(tdbRef = tdbRefList; tdbRef; tdbRef = tdbRef->next)
     {
     printTrack("visible", tdbRef->tdb,  FALSE);
     if (tdbRef->next != NULL)
@@ -279,40 +286,37 @@
 }
 
 void addSubtrackNames(struct dyString *dy, struct trackDb *parentTdb)
 {
 if (parentTdb->subtracks == NULL)
     return;
 
 struct trackDb *tdb;
 for(tdb = parentTdb->subtracks; tdb;  tdb = tdb->next)
     {
     dyStringPrintf(dy, ",'%s'", trackHubSkipHubName(tdb->track));
     addSubtrackNames(dy, tdb);
     }
 }
 
-static void doTable(struct cart *cart, char *db, struct grp *groupList, struct trackDb *trackList)
+static void doTable(struct cart *cart, char *db, struct hash *groupHash, struct trackDb *trackList)
 // output the tree table
 {
 char *hubName = hubNameFromUrl(getHubName(cart, db));
-struct grp *curGroup;
-for(curGroup = groupList; curGroup;  curGroup = curGroup->next)
-    {
-    if ((hubName != NULL) && sameString(curGroup->name, hubName))
-        break;
-    }
+struct grp *curGroup = NULL;
+if (hubName != NULL)
+    curGroup = hashFindVal(groupHash, hubName);
 
 jsInlineF("var collectionData = []; ");
 struct dyString *dy = newDyString(100);
 if (curGroup != NULL)
     {
     // print out all the tracks in all the collections
     struct trackDb *tdb;
     jsInlineF("collectionData['#'] = [");
     boolean first = TRUE;
     for(tdb = trackList; tdb;  tdb = tdb->next)
         {
         if (sameString(tdb->grp, hubName))
             {
             if (!first)
                 {
@@ -329,33 +333,37 @@
         {
         if ( sameString(tdb->grp, curGroup->name))
             {
             printSubtracks("collectionData", tdb, TRUE);
             addSubtrackNames(dy, tdb);
             }
         }
     }
 else
     jsInlineF("collectionData['#'] = [];");
 
 jsInlineF("var collectionNames = new Set([%s]);", dy->string);
 
 jsInlineF("var trackData = []; ");
 struct dyString *rootChildren = newDyString(512);
-addVisibleTracks(rootChildren, cart, trackList);
-for(curGroup = groupList; curGroup;  curGroup = curGroup->next)
+addVisibleTracks(groupHash, rootChildren, cart, trackList);
+
+struct hashCookie cookie = hashFirst(groupHash);
+struct hashEl *hel;
+while ((hel = hashNext(&cookie)) != NULL)
     {
+    curGroup = (struct grp *)hel->val;
     if ((hubName != NULL) && sameString(curGroup->name, hubName))
         continue;
     if (!isEmpty(rootChildren->string))
         dyStringPrintf(rootChildren, ",");
     dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'%s', text:'%s', parent:'#', children:true}", curGroup->name, curGroup->label);
     struct trackDb *tdb;
     jsInlineF("trackData['%s'] = [", curGroup->name);
     boolean first = TRUE;
     for(tdb = trackList; tdb;  tdb = tdb->next)
         {
         if ( sameString(tdb->grp, curGroup->name))
             {
             if (!first)
                 jsInlineF(",");
             printTrack(curGroup->name, tdb, FALSE);
@@ -385,50 +393,50 @@
 puts(
 "       </div>\n"
 "    </div>\n"
 );
 puts(
 "    <div class='container-fluid'>\n"
 "       <div class='gbsPage'>\n");
 
 webIncludeFile("inc/hgCollectionHelpInclude.html");
 puts(
 "       </div>"
 "    </div>\n"
 );
 }
 
-static void doMainPage(struct cart *cart, char *db, struct grp *groupList, struct trackDb *trackList)
+static void doMainPage(struct cart *cart, char *db, struct hash *groupHash, struct trackDb *trackList)
 /* Print out initial HTML of control page. */
 {
 webStartGbNoBanner(cart, db, "Collections");
 webIncludeResourceFile("gb.css");
 //webIncludeResourceFile("../staticStyle/gbStatic.css");
 webIncludeResourceFile("gbStatic.css");
 webIncludeResourceFile("spectrum.min.css");
 webIncludeResourceFile("hgGtexTrackSettings.css");
 
 jsReloadOnBackButton(cart);
 
 // Write the page HTML: the application, followed by its help doc
 webIncludeFile("inc/hgCollection.html");
 char *assembly = stringBetween("(", ")", hFreezeFromDb(db));
 if (assembly != NULL)
     jsInlineF("$('#assembly').text('%s');\n",assembly);
 printHelp();
 
-doTable(cart, db, groupList, trackList);
+doTable(cart, db, groupHash, trackList);
 
 puts("<link rel='stylesheet' href='https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css'>");
 puts("<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css' />");
 puts("<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js'></script>");
 puts("<script src=\"//code.jquery.com/ui/1.10.3/jquery-ui.min.js\"></script>");
 puts("<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.4/jstree.min.js\"></script>\n");
 jsIncludeFile("utils.js", NULL);
 jsIncludeFile("ajax.js", NULL);
 jsIncludeFile("spectrum.min.js", NULL);
 jsIncludeFile("hgCollection.js", NULL);
 webEndGb();
 }
 
 static char *getSqlBigWig(struct sqlConnection *conn, char *db, struct trackDb *tdb)
 // figure out the bigWig for native tables
@@ -714,79 +722,90 @@
 }
 
 static void buildNameHash(struct hash *nameHash, struct trackDb *list)
 {
 if (list == NULL)
     return;
 
 struct trackDb *tdb = list;
 for(tdb = list; tdb;  tdb = tdb->next)
     {
     hashAdd(nameHash, trackHubSkipHubName(tdb->track), tdb);
     buildNameHash(nameHash, tdb->subtracks);
     }
 }
 
-static struct trackDb *traverseTree(struct trackDb *oldList, struct hash *groupHash)
+static struct trackDb *traverseTree(struct trackDb *oldList, struct hash *allGroupHash, struct hash *groupHash)
 // add acceptable tracks to our tree
 {
 struct trackDb *newList = NULL, *tdb, *tdbNext;
 
 for(tdb = oldList;  tdb ; tdb = tdbNext)
     {
     tdbNext = tdb->next;
     if (tdb->subtracks)
         {
-        tdb->subtracks = traverseTree(tdb->subtracks, groupHash);
+        tdb->subtracks = traverseTree(tdb->subtracks, allGroupHash, groupHash);
 
         if (tdb->subtracks)
             {
-            hashStore(groupHash, tdb->grp);
+            if (hashLookup(groupHash, tdb->grp) == NULL)
+                hashAdd(groupHash, tdb->grp, hashMustFindVal(allGroupHash, tdb->grp));
             slAddHead(&newList, tdb);
             }
         }
     else
         {
         if (trackCanBeAdded(tdb))
             {
-            hashStore(groupHash, tdb->grp);
+            if (hashLookup(groupHash, tdb->grp) == NULL)
+                hashAdd(groupHash, tdb->grp, hashMustFindVal(allGroupHash, tdb->grp));
             slAddHead(&newList, tdb);
             }
         }
     }
 slReverse(&newList);
 
 return newList;
 }
 
-static void pruneTrackList(struct trackDb **fullTrackList, struct grp **fullGroupList)
+static struct hash *pruneTrackList(struct trackDb **fullTrackList, struct grp **fullGroupList)
 // drop track types we don't grok yet
 {
+struct hash *allGroupHash = newHash(5);
 struct hash *groupHash = newHash(5);
-
-*fullTrackList = traverseTree(*fullTrackList, groupHash);
 struct grp *newGroupList = NULL, *grp, *nextGrp;
 
 for (grp = *fullGroupList; grp; grp = nextGrp)
     {
     nextGrp = grp->next;
+    hashAdd(allGroupHash, grp->name, grp);
+    }
+
+*fullTrackList = traverseTree(*fullTrackList, allGroupHash, groupHash);
+
+for (grp = *fullGroupList; grp; grp = nextGrp)
+    {
+    nextGrp = grp->next;
 
     if (hashLookup(groupHash, grp->name))
         slAddHead(&newGroupList, grp);
     }
 slReverse(&newGroupList);
 *fullGroupList = newGroupList;
+
+return groupHash;
 }
 
 static struct trackDb *addSupers(struct trackDb *trackList)
 /* Insert supertracks into the hierarchy. */
 {
 struct trackDb *newList = NULL;
 struct trackDb *tdb, *nextTdb;
 struct hash *superHash = newHash(5);
 
 for(tdb = trackList; tdb;  tdb = nextTdb)
     {
     nextTdb = tdb->next;
 
     if (tdb->parent)
         {
@@ -907,40 +926,40 @@
 static void doMiddle(struct cart *cart)
 /* Set up globals and make web page */
 {
 char *db;
 char *genome;
 getDbAndGenome(cart, &db, &genome, oldVars);
 initGenbankTableNames(db);
 int timeout = cartUsualInt(cart, "udcTimeout", 300);
 if (udcCacheTimeout() < timeout)
     udcSetCacheTimeout(timeout);
 knetUdcInstall();
 
 struct trackDb *trackList;
 struct grp *groupList;
 cartTrackDbInit(cart, &trackList, &groupList, TRUE);
-pruneTrackList(&trackList, &groupList);
+struct hash *groupHash = pruneTrackList(&trackList, &groupList);
 
 struct trackDb *superList = addSupers(trackList);
 struct hash *nameHash = newHash(5);
 buildNameHash(nameHash, superList);
 
 char *cmd = cartOptionalString(cart, "cmd");
 if (cmd == NULL)
     {
-    doMainPage(cart, db, groupList, superList);
+    doMainPage(cart, db, groupHash, superList);
     }
 else if (sameString("addTrack", cmd))
     {
     char *trackName = cgiString("track");
     char *collectionName = cgiString("collection");
     doAddTrack(cart, db, superList, trackName, collectionName, nameHash);
     apiOut("{\"serverSays\": \"added %s to collection\"}", NULL);
     }
 else if (sameString("newCollection", cmd))
     {
     char *trackName = cgiString("track");
     char *collectionName = makeUnique(nameHash, "coll");
     char *shortLabel = "New Collection";
     char *longLabel = "Description of New Collection";
     struct trackDb *tdb;