eb78be0ea632442a7ff6c55321f5bb6cafb2c7d2
braney
  Tue Mar 6 09:36:21 2018 -0800
add tool tips and the ability to put multiwigs into collections from
hgTracks

diff --git src/hg/hgCollection/hgCollection.c src/hg/hgCollection/hgCollection.c
index 99c2e80..792317c 100644
--- src/hg/hgCollection/hgCollection.c
+++ src/hg/hgCollection/hgCollection.c
@@ -8,30 +8,35 @@
 #include "trashDir.h"
 #include "hubConnect.h"
 #include "hui.h"
 #include "grp.h"
 #include "cheapcgi.h"
 #include "jsHelper.h"
 #include "web.h"
 #include "knetUdc.h"
 #include "api.h"
 #include "genbank.h"
 #include "htmshell.h"
 #include "jsonParse.h"
 #include "customComposite.h"
 #include "stdlib.h"
 
+/* Tool tips */
+#define COLLECTIONTITLE  "Double-click to edit name and color"
+#define FOLDERTITLE      "Click to open node"
+#define TRACKTITLE       "Press Green Plus to add track to collection"
+
 /* Global Variables */
 struct hash *oldVars = NULL;	/* The cart before new cgi stuff added. */
 // Null terminated list of CGI Variables we don't want to save permanently:
 char *excludeVars[] = {"Submit", "submit", "cmd", "track", "collection", "jsonp", NULL,};
 
 struct track
 {
 struct track *next;
 struct track *trackList;
 struct trackDb *tdb;
 char *name;
 char *shortLabel;
 char *longLabel;
 char *visibility;
 unsigned long color;
@@ -72,41 +77,49 @@
     }
 
 return NULL;
 }
 
 static boolean trackCanBeAdded(struct trackDb *tdb)
 // are we allowing this track into a custom composite
 {
 return  (tdb->subtracks == NULL) && !startsWith("wigMaf",tdb->type) &&  (startsWith("wig",tdb->type) || startsWith("bigWig",tdb->type) || startsWith("bedGraph",tdb->type)) ;
 }
 
 static void printTrack(char *parent, struct trackDb *tdb,  boolean user)
 // output list elements for a group
 {
 char *userString = "";
+char *title;
+
+if (user)
+    title = COLLECTIONTITLE;
+else if (tdb->subtracks)
+    title = FOLDERTITLE;
+else
+    title = TRACKTITLE;
 
 if (tdb->subtracks)
     userString = "icon:'../images/folderC.png',children:true,";
 else if (user)
     userString = "icon:'fa fa-minus-square',";
 else
     userString = "icon:'fa fa-plus',";
     
 #define IMAKECOLOR_32(r,g,b) ( ((unsigned int)b<<0) | ((unsigned int)g << 8) | ((unsigned int)r << 16))
 
-jsInlineF("{%s id:'%s',li_attr:{shortlabel:'%s', longlabel:'%s',color:'#%06x',name:'%s'},text:'%s (%s)',parent:'%s'}",userString, trackHubSkipHubName(tdb->track), tdb->shortLabel, tdb->longLabel, IMAKECOLOR_32(tdb->colorR,tdb->colorG,tdb->colorB),trackHubSkipHubName(tdb->track),tdb->shortLabel,tdb->longLabel,parent);
+jsInlineF("{%s id:'%s',li_attr:{title:'%s',shortlabel:'%s', longlabel:'%s',color:'#%06x',name:'%s'},text:'%s (%s)',parent:'%s'}",userString, trackHubSkipHubName(tdb->track),title, tdb->shortLabel, tdb->longLabel, IMAKECOLOR_32(tdb->colorR,tdb->colorG,tdb->colorB),trackHubSkipHubName(tdb->track),tdb->shortLabel,tdb->longLabel,parent);
 }
 
 static void outHubHeader(FILE *f, char *db)
 // output a track hub header
 {
 fprintf(f,"hub hub1\n\
 shortLabel Track Collections\n\
 longLabel Track Collections\n\
 useOneFile on\n\
 email genome-www@soe.ucsc.edu\n\n");
 fprintf(f,"genome %s\n\n", db);  
 }
 
 
 static char *getHubName(struct cart *cart, char *db)
@@ -254,31 +267,31 @@
 {
 struct trackDb *tdb;
 struct trackDbRef *tdbRefList = NULL, *tdbRef;
 
 for(tdb = trackList; tdb; tdb = tdb->next)
     {
     struct grp *grp = hashMustFindVal(groupHash, tdb->grp);
     double priority =  grp->priority + tdb->priority/100.0;
 
     checkForVisible(cart, groupHash, &tdbRefList, tdb,  priority, 1.0/100.0);
     }
 
 slSort(&tdbRefList, tdbRefCompare);
 if (!isEmpty(rootChildren->string))
     dyStringPrintf(rootChildren, ",");
-dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'visible', text:'Visible Tracks', parent:'#'");
+dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'visible', text:'Visible Tracks', parent:'#', li_attr:{title:'%s'} ", FOLDERTITLE);
 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)
         jsInlineF(",");
     }
 jsInlineF("];");
 }
 
 void printSubtracks(char *arrayName, struct trackDb *parentTdb, boolean user)
@@ -366,52 +379,55 @@
     }
 else
     jsInlineF("collectionData['#'] = [];");
 
 jsInlineF("var collectionNames = new Set([%s]);", dy->string);
 
 jsInlineF("var trackData = []; ");
 struct dyString *rootChildren = newDyString(512);
 addVisibleTracks(groupHash, rootChildren, cart, trackList);
 for(curGroup = groupList; curGroup;  curGroup = curGroup->next)
     {
     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);
+    dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'%s', text:'%s', parent:'#', children:true,li_attr:{title:'%s'}}", curGroup->name, curGroup->label, FOLDERTITLE);
     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);
             first = FALSE;
             }
         }
     jsInlineF("];");
     for(tdb = trackList; tdb;  tdb = tdb->next)
         {
         if ( sameString(tdb->grp, curGroup->name))
             printSubtracks("trackData", tdb, FALSE);
         }
     }
 jsInlineF("trackData['#'] = [%s];", rootChildren->string);
+jsInlineF("var collectionTitle='%s';\n", COLLECTIONTITLE);
+jsInlineF("var folderTitle='%s';\n",  FOLDERTITLE);
+jsInlineF("var trackTitle='%s';\n", TRACKTITLE);
 jsInlineF("hgCollection.init();\n");
 }
 
 static void printHelp()
 // print out the help page
 {
 puts(
 "<br><a name='INFO_SECTION'></a>\n"
 "    <div class='row gbsPage'>\n"
 "        <div ><h1>Track Collection Builder Help</h1></div>\n"
 "        <div >\n"
 );
 puts(
 "       </div>\n"
 "    </div>\n"
@@ -564,30 +580,40 @@
 longLabel %s\n\
 %s on\n\
 color %ld,%ld,%ld \n\
 type mathWig\n\
 priority %d\n\
 visibility full\n\n", parent, shortLabel, longLabel, CUSTOM_COMPOSITE_SETTING,
  0xff& (collection->color >> 16),0xff& (collection->color >> 8),0xff& (collection->color),  priority);
 
 }
 
 static void modifyName(struct trackDb *tdb, char *hubName, struct hash  *collectionNameHash)
 /* If this is a new track in the collection we want to make sure
  * it gets a different name than the track in trackDb.
  * If it's a native track, we want to squirrel away the original track name. */
 {
+if (tdb->subtracks)
+    {
+    struct trackDb *subTdb;
+    for (subTdb = tdb->subtracks; subTdb; subTdb = subTdb->next)
+        {
+        modifyName(subTdb, hubName, collectionNameHash);
+        }
+    return;
+    }
+
 if ((tdb->grp == NULL) || (hubName == NULL) || differentString(tdb->grp, hubName))
     {
     if (collectionNameHash)
         hashStore(collectionNameHash,  tdb->track);
 
     char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
     if (bigDataUrl == NULL)
         {
         char *table = trackDbSetting(tdb, "table");
         if (table == NULL)
             hashAdd(tdb->settingsHash, "table", tdb->track);
         }
     }
 }
 
@@ -826,121 +852,146 @@
             hashStore(superHash, tdb->parent->track);
 
             slAddHead(&newList, tdb->parent);
             }
         slAddTail(&tdb->parent->subtracks, tdb);
         }
     else
         slAddHead(&newList, tdb);
     }
 
 slReverse(&newList);
 
 return newList;
 }
 
-static void outOneTdb(char *db, struct sqlConnection *conn, FILE *f, struct trackDb *tdb, int numTabs)
+static void outOneTdb(char *db, struct sqlConnection *conn, FILE *f, struct trackDb *tdb, int numTabs, int priority)
 /* Put out a single trackDb entry to our collections hub. */
 {
 char *tabs = "";
 if (numTabs == 1)
     tabs = "\t";
 else if (numTabs == 2)
     tabs = "\t\t";
 else if (numTabs == 3)
     tabs = "\t\t\t";
 
 struct hashEl *hel = hashLookup(tdb->settingsHash, "track");
 fprintf(f, "%s%s %s\n", tabs,hel->name, trackHubSkipHubName((char *)hel->val));
+fprintf(f, "%s%s %d\n", tabs, "priority", priority);
 
 char *dataUrl = NULL;
 char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
 if (bigDataUrl == NULL)
     {
     if (startsWith("bigWig", tdb->type))
         {
         if (conn == NULL)
             errAbort("track hub has bigWig without bigDataUrl");
         dataUrl = getSqlBigWig(conn, db, tdb);
         }
     }
 
 struct hashCookie cookie = hashFirst(tdb->settingsHash);
 while ((hel = hashNext(&cookie)) != NULL)
     {
     if (sameString("parent", hel->name))
         fprintf(f, "%s%s %s\n", tabs,hel->name, trackHubSkipHubName((char *)hel->val));
-    else if (!(sameString("track", hel->name) || sameString("polished", hel->name) || sameString("group", hel->name)))
+    else if (!(sameString("track", hel->name) || sameString("polished", hel->name)|| sameString("group", hel->name) || sameString("priority", hel->name)))
         fprintf(f, "%s%s %s\n", tabs,hel->name, (char *)hel->val);
     }
 
 if (bigDataUrl == NULL)
     {
     if (dataUrl != NULL)
         fprintf(f, "%sbigDataUrl %s\n", tabs,dataUrl);
     }
 
 fprintf(f, "\n");
 }
 
-static void outTrackDbList(char *db, struct sqlConnection *conn, FILE *f, char *hubName, struct trackDb *list, char *collectionName, struct trackDb *newTdb,  int numTabs)
+static void outTrackDbList(char *db, struct sqlConnection *conn, FILE *f, char *hubName, struct trackDb *list, char *collectionName, struct trackDb *newTdb,  int numTabs, int priority)
 /* Put a list of trackDb entries into a collection, adding a new track to the collection. */
 {
 if (list == NULL)
     return;
 
 struct trackDb *tdb;
 for(tdb = list; tdb; tdb = tdb->next)
     {
     if (tdb->grp != NULL)
         if ((hubName == NULL) || differentString(hubName, tdb->grp))
                 continue;
 
-    outOneTdb(db, conn, f, tdb, numTabs);
+    outOneTdb(db, conn, f, tdb, numTabs, priority++);
 
     struct hashEl *hel = hashLookup(tdb->settingsHash, "track");
     if ((hel != NULL) && (hel->val != NULL) &&  sameString((char *)hel->val, collectionName))
-        outOneTdb(db, conn, f, newTdb, numTabs + 1);
+        {
+        if (newTdb->subtracks)
+            {
+            struct trackDb *subTdb;
+            slReverse(&newTdb->subtracks);
+            for(subTdb = newTdb->subtracks; subTdb; subTdb = subTdb->next)
+                {
+                outOneTdb(db, conn, f, subTdb, numTabs + 1, priority++);
+                }
+            }
+        else
+            outOneTdb(db, conn, f, newTdb, numTabs + 1, priority++);
+        }
 
-    outTrackDbList(db, conn, f, hubName,  tdb->subtracks, collectionName, newTdb, numTabs + 1);
+    outTrackDbList(db, conn, f, hubName,  tdb->subtracks, collectionName, newTdb, numTabs + 1, priority);
     }
 }
 
 static void doAddTrack(struct cart *cart, char *db, struct trackDb *trackList,  char *trackName, char *collectionName, struct hash *nameHash)
 /* Add a track to a collection in a hub. */
 {
 char *fileName = getHubName(cart, db);
 char *hubName = hubNameFromUrl(fileName);
 FILE *f = fopen(fileName, "w");
 struct trackDb *newTdb = hashMustFindVal(nameHash, trackHubSkipHubName(trackName));
+if (newTdb->subtracks)
+    {
+    struct trackDb *subTdb;
+    for(subTdb = newTdb->subtracks; subTdb; subTdb = subTdb->next)
+        {
+        hashReplace(subTdb->settingsHash, "track", makeUnique(nameHash, subTdb->track));
+        hashReplace(subTdb->settingsHash, "parent", trackHubSkipHubName(collectionName));
+        }
+    }
+else
+    {
     hashReplace(newTdb->settingsHash, "track", makeUnique(nameHash, trackName));
     hashReplace(newTdb->settingsHash, "parent", trackHubSkipHubName(collectionName));
+    }
 char *tdbType = trackDbSetting(newTdb, "tdbType");
 if (tdbType != NULL)
     {
     hashReplace(newTdb->settingsHash, "type", tdbType);
     hashReplace(newTdb->settingsHash, "shortLabel", trackDbSetting(newTdb, "name"));
     hashReplace(newTdb->settingsHash, "longLabel", trackDbSetting(newTdb, "description"));
     }
 
 
 outHubHeader(f, db);
 struct sqlConnection *conn = NULL;
 if (!trackHubDatabase(db))
     conn = hAllocConn(db);
 modifyName(newTdb, hubName, NULL);
-outTrackDbList(db, conn, f, hubName, trackList, collectionName, newTdb,  0);
+outTrackDbList(db, conn, f, hubName, trackList, collectionName, newTdb,  0, 0);
 
 hFreeConn(&conn);
 fclose(f);
 }
 
 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);