8dc2f2a3bfe8d47e02a94ca4d9f62063df86b15c
braney
  Thu Oct 5 14:56:25 2017 -0700
ongoing work on hgCollection.  Add the ability to add tracks in hgTracks
to a collection.   Turn "overlay method" back on for custom collections.
Fix panning bug in hgTracks with custom collections

diff --git src/hg/hgCollection/hgCollection.c src/hg/hgCollection/hgCollection.c
index d0f48b3..ee2f903 100644
--- src/hg/hgCollection/hgCollection.c
+++ src/hg/hgCollection/hgCollection.c
@@ -11,31 +11,31 @@
 #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"
 
 /* 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", "hgva_startQuery", "jsonp", NULL,};
+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;
 char *viewFunc;
 };
 
 struct trackDbRef 
@@ -118,57 +118,57 @@
 jsInlineF(" (%s)", tdb->longLabel);
 
 
 if (tdb->subtracks)
     {
     struct trackDb *subTdb;
 
     jsInlineF("<ul>");
     for(subTdb = tdb->subtracks; subTdb; subTdb = subTdb->next)
         printGroup(trackHubSkipHubName(tdb->track), subTdb, user && (subTdb->subtracks != NULL), user);
     jsInlineF("</ul>");
     }
 jsInlineF("</li>");
 }
 
-static void outHubHeader(FILE *f, char *db, char *hubName)
+static void outHubHeader(FILE *f, char *db)
 // output a track hub header
 {
 fprintf(f,"hub hub1\n\
 shortLabel User Composite\n\
 longLabel User Composite\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)
 // get the name of the hub to use for user collections
 {
 struct tempName hubTn;
 char buffer[4096];
 safef(buffer, sizeof buffer, "%s-%s", customCompositeCartName, db);
 char *hubName = cartOptionalString(cart, buffer);
 
 if (hubName == NULL)
     {
     trashDirDateFile(&hubTn, "hgComposite", "hub", ".txt");
     hubName = cloneString(hubTn.forCgi);
     cartSetString(cart, buffer, hubName);
     FILE *f = mustOpen(hubName, "a");
-    outHubHeader(f, db, hubName);
+    outHubHeader(f, db);
     fclose(f);
     cartSetString(cart, "hubUrl", hubName);
     cartSetString(cart, hgHubConnectRemakeTrackHub, hubName);
     }
 return hubName;
 }
 
 static bool subtrackEnabledInTdb(struct trackDb *subTdb)
 /* Return TRUE unless the subtrack was declared with "subTrack ... off". */
 {
 bool enabled = TRUE;
 char *words[2];
 char *setting;
 if ((setting = trackDbLocalSetting(subTdb, "parent")) != NULL)
     {
@@ -454,30 +454,31 @@
 fprintf(f, "%svisibility %s\n",tabs,visibility);
 fprintf(f, "%spriority %d\n",tabs,priority);
 fprintf(f, "\n");
 }
 
 static void outComposite(FILE *f, struct track *collection)
 // output a composite header for user composite
 {
 char *parent = collection->name;
 char *shortLabel = collection->shortLabel;
 char *longLabel = collection->longLabel;
 fprintf(f,"track %s\n\
 shortLabel %s\n\
 compositeTrack on\n\
 autoScale on\n\
+aggregate  none\n\
 longLabel %s\n\
 %s on\n\
 color %ld,%ld,%ld \n\
 type wig \n\
 visibility full\n\n", parent, shortLabel, longLabel, CUSTOM_COMPOSITE_SETTING,
  0xff& (collection->color >> 16),0xff& (collection->color >> 8),0xff& (collection->color));
 
 }
 
 static char *skipColl(char *str)
 {
 if (startsWith("coll_", str))
     return &str[5];
 return str;
 }
@@ -509,31 +510,31 @@
     //useColor++;
     }
 
 return priority;
 }
 
 static void updateHub(struct cart *cart, char *db, struct track *collectionList, struct hash *nameHash)
 // save our state to the track hub
 {
 char *hubName = getHubName(cart, db);
 
 chmod(hubName, 0666);
 FILE *f = mustOpen(hubName, "w");
 struct hash *collectionNameHash = newHash(6);
 
-outHubHeader(f, db, hubName);
+outHubHeader(f, db);
 //int useColor = 0;
 struct track *collection;
 struct sqlConnection *conn = hAllocConn(db);
 for(collection = collectionList; collection; collection = collection->next)
     {
     outComposite(f, collection);
     struct trackDb *tdb;
     struct track *track;
     int priority = 1;
     for (track = collection->trackList; track; track = track->next)
         {
         if (track->trackList != NULL)
             {
             priority = outView(f, conn, db, track, collection->name,  nameHash, collectionNameHash, priority);
             }
@@ -726,60 +727,175 @@
             hashStore(superHash, tdb->parent->track);
 
             slAddHead(&newList, tdb->parent);
             }
         slAddHead(&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)
+/* 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));
+
+char *dataUrl = NULL;
+char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
+if (bigDataUrl == NULL)
+    {
+    if (startsWith("bigWig", tdb->type))
+        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)))
+        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)
+/* 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);
+
+    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);
+
+    outTrackDbList(db, conn, f, hubName,  tdb->subtracks, collectionName, newTdb, numTabs + 1);
+    }
+}
+
+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, trackName);
+hashReplace(newTdb->settingsHash, "track", makeUnique(nameHash, trackName));
+hashReplace(newTdb->settingsHash, "parent", trackHubSkipHubName(collectionName));
+
+outHubHeader(f, db);
+struct sqlConnection *conn = hAllocConn(db);
+outTrackDbList(db, conn, f, hubName, trackList, collectionName, newTdb,  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);
 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 trackDb *superList = addSupers(trackList);
 struct hash *nameHash = newHash(5);
 buildNameHash(nameHash, superList);
 
-char *jsonIn = cgiUsualString("jsonp", NULL);
-fprintf(stderr, "BRANEY %s\n", jsonIn);
-if (jsonIn != NULL)
+char *cmd = cartOptionalString(cart, "cmd");
+if (cmd == NULL)
     {
-    doAjax(cart, db, jsonIn, nameHash);
-    apiOut("{\"serverSays\": \"Collections saved successfully.\"}", NULL);
+    doMainPage(cart, db, groupList, superList);
     }
-else
+else if (sameString("addTrack", cmd))
     {
-    doMainPage(cart, db, groupList, superList);
+    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;
+
+    AllocVar(tdb);
+    slAddHead(&superList, tdb);
+    tdb->settingsHash = newHash(5);
+    tdb->type = cloneString("wig");
+
+    hashAdd(tdb->settingsHash, "track", collectionName);
+    hashAdd(tdb->settingsHash, "shortLabel", shortLabel);
+    hashAdd(tdb->settingsHash, "longLabel", longLabel);
+    hashAdd(tdb->settingsHash, "autoScale", "on");
+    hashAdd(tdb->settingsHash, "compositeTrack", "on");
+    hashAdd(tdb->settingsHash, "aggregate", "none");
+    hashAdd(tdb->settingsHash, "type", "wig");
+    hashAdd(tdb->settingsHash, "visibility", "full");
+    hashAdd(tdb->settingsHash, "color", "0,0,0");
+    hashAdd(tdb->settingsHash, CUSTOM_COMPOSITE_SETTING, "on");
+
+    doAddTrack(cart, db, superList, trackName, collectionName, nameHash);
+    apiOut("{\"serverSays\": \"new %s to collection\"}", NULL);
+    }
+else if (sameString("saveCollection", cmd))
+    {
+    char *jsonIn = cgiUsualString("jsonp", NULL);
+    doAjax(cart, db, jsonIn, nameHash);
+    apiOut("{\"serverSays\": \"Collections gaved successfully.\"}", NULL);
     }
 }
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 long enteredMainTime = clock1000();
 cgiSpoof(&argc, argv);
 
 boolean isCommandLine = (cgiOptionalString("cgiSpoof") != NULL);
 if (!isCommandLine)
     htmlPushEarlyHandlers(); /* Make errors legible during initialization. */
 oldVars = hashNew(10);
 
 cartEmptyShellNoContent(doMiddle, hUserCookie(), excludeVars, oldVars);