8c908f948b09826c6cb4452ee5b282aca41be85e
galt
  Tue Dec 8 21:52:59 2015 -0800
Multi-region (exonMostly). This work allows people to look at virtual chromosomes from a list of regions and then navigate and perform all of the usual functions on it.

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 146e638..d5856c1 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -134,31 +134,31 @@
 if (!sqlTableExists(conn, userDbTable()))
     {
     fprintf(stderr, "ASH: cartTablesOk failed on %s.%s!  pid=%ld\n",
 	    sqlGetDatabase(conn), userDbTable(),  (long)getpid());
     return FALSE;
     }
 if (!sqlTableExists(conn, sessionDbTable()))
     {
     fprintf(stderr, "ASH: cartTablesOk failed on %s.%s!  pid=%ld\n",
 	    sqlGetDatabase(conn), sessionDbTable(), (long)getpid());
     return FALSE;
     }
 return TRUE;
 }
 
-static void cartParseOverHash(struct cart *cart, char *contents)
+void cartParseOverHash(struct cart *cart, char *contents)
 /* Parse cgi-style contents into a hash table.  This will *not*
  * replace existing members of hash that have same name, so we can
  * support multi-select form inputs (same var name can have multiple
  * values which will be in separate hashEl's). */
 {
 struct hash *hash = cart->hash;
 char *namePt, *dataPt, *nextNamePt;
 namePt = contents;
 while (namePt != NULL && namePt[0] != 0)
     {
     dataPt = strchr(namePt, '=');
     if (dataPt == NULL)
 	errAbort("Mangled input string %s", namePt);
     *dataPt++ = 0;
     nextNamePt = strchr(dataPt, '&');
@@ -1761,31 +1761,31 @@
 void cartHtmlShellWithHead(char *head, char *title, void (*doMiddle)(struct cart *cart),
 	char *cookieName, char **exclude, struct hash *oldVars)
 /* Load cart from cookie and session cgi variable.  Write web-page
  * preamble including head and title, call doMiddle with cart, and write end of web-page.
  * Exclude may be NULL.  If it exists it's a comma-separated list of
  * variables that you don't want to save in the cart between
  * invocations of the cgi-script. */
 {
 struct cart *cart;
 char *db, *org, *pos;
 char titlePlus[2048];
 char extra[2048];
 pushWarnHandler(cartEarlyWarningHandler);
 cart = cartAndCookie(cookieName, exclude, oldVars);
 getDbAndGenome(cart, &db, &org, oldVars);
-pos = cartGetPosition(cart, db);
+pos = cartGetPosition(cart, db, NULL);
 pos = addCommasToPos(db, stripCommas(pos));
 if(pos != NULL && oldVars != NULL)
     {
     struct hashEl *oldpos = hashLookup(oldVars, positionCgiName);
     if(oldpos != NULL && differentString(pos,oldpos->val))
         cartSetString(cart,"lastPosition",oldpos->val);
     }
 *extra = 0;
 if (pos == NULL && org != NULL)
     safef(titlePlus,sizeof(titlePlus), "%s%s - %s",trackHubSkipHubName(org), extra, title );
 else if (pos != NULL && org == NULL)
     safef(titlePlus,sizeof(titlePlus), "%s - %s",pos, title );
 else if (pos == NULL && org == NULL)
     safef(titlePlus,sizeof(titlePlus), "%s", title );
 else
@@ -2801,56 +2801,98 @@
     if (exists)
 	dyStringPrintf(dyMessage,
 		       "<P>Note: the session contains BLAT results.  ");
     else
 	dyStringPrintf(dyMessage,
 		"<P>Note: the session contains an expired reference to "
 		"previously saved BLAT results, so it may not appear as "
 		"originally intended.  ");
     dyStringPrintf(dyMessage,
 		   "BLAT results are subject to an "
 		   "<A HREF=\"../goldenPath/help/hgSessionHelp.html#CTs\" TARGET=_BLANK>"
 		   "expiration policy</A>.");
     }
 }
 
-char *cartGetPosition(struct cart *cart, char *database)
+char *cartGetPosition(struct cart *cart, char *database, struct cart **pLastDbPosCart)
 /* get the current position in cart as a string chr:start-end.
  * This can handle the special CGI params 'default' and 'lastDbPos'
  * Returned value has to be freed. Returns 
  * default position of assembly is no position set in cart nor as CGI var.
  * Returns NULL if no position set anywhere and no default position.
+ * For virtual modes, returns the type and extraState. 
 */
 {
 // position=lastDbPos in URL? -> go back to the last browsed position for this db
 char *position = NULL;
-char dbPosKey[256];
 char *defaultPosition = hDefaultPos(database);
+struct cart *lastDbPosCart = cartOfNothing();
+boolean gotCart = FALSE;
+char dbPosKey[256];
 safef(dbPosKey, sizeof(dbPosKey), "position.%s", database);
 if (sameOk(cgiOptionalString("position"), "lastDbPos"))
     {
-    position = cartUsualString(cart, dbPosKey, defaultPosition);
-    cartSetString(cart, "position", position);
+    char *dbLocalPosContent = cartUsualString(cart, dbPosKey, NULL);
+    if (dbLocalPosContent)
+	{
+	//warn("dbLocalPosContent=%s",dbLocalPosContent); // DEBUG REMOVE
+	if (strchr(dbLocalPosContent, '='))
+	    {
+	    gotCart = TRUE;
+	    cartParseOverHash(lastDbPosCart, cloneString(dbLocalPosContent)); // this function chews up input
+	    position = cloneString(cartUsualString(lastDbPosCart, "position", NULL));
+	    //warn("gotCart position=%s",position); // DEBUG REMOVE
+
+	    // DEBUG REMOVE:
+	    //struct dyString *dbPosValue = newDyString(4096);
+	    //cartEncodeState(lastDbPosCart, dbPosValue);
+	    //warn("gotCart dbPosValue->string=[%s]",dbPosValue->string);
+
+	    }
+	else
+	    {
+	    position = dbLocalPosContent;  // old style value
+	    }
+	}
+    else
+	{
+	position = defaultPosition; // no value was set
+	}
     }
     
 if (position == NULL)
     {
     position = cloneString(cartUsualString(cart, "position", NULL));
     }
 
 /* default if not set at all, as would happen if it came from a URL with no
  * position. Otherwise tell them to go back to the gateway. Also recognize
  * "default" as specifying the default position. */
 if (((position == NULL) || sameString(position, "default"))
     && (defaultPosition != NULL))
     position = cloneString(defaultPosition);
 
+if (!gotCart)
+    {
+    cartSetBoolean(lastDbPosCart, "virtMode", FALSE);
+    cartSetString(lastDbPosCart, "virtModeType", "default");
+    cartSetString(lastDbPosCart, "lastVirtModeType", "default");
+    cartSetString(lastDbPosCart, "position", position);
+    cartSetString(lastDbPosCart, "nonVirtPosition", position);
+    cartSetString(lastDbPosCart, "lastVirtModeExtra", "");
+    }
+
+if (pLastDbPosCart)
+    *pLastDbPosCart = lastDbPosCart;
+
 return position;
 }
 
-void cartSetDbPosition(struct cart *cart, char *database, char *position)
-/* set the 'position.db' variable in the cart */
+void cartSetDbPosition(struct cart *cart, char *database, struct cart *lastDbPosCart)
+/* Set the 'position.db' variable in the cart.*/
 {
 char dbPosKey[256];
-safef(dbPosKey, sizeof(dbPosKey), "position.%s", database);
-cartSetString(cart, dbPosKey, cloneString(position)); // XX need to clone here?
+safef(dbPosKey, sizeof dbPosKey, "position.%s", database);
+struct dyString *dbPosValue = newDyString(4096);
+cartEncodeState(lastDbPosCart, dbPosValue);
+cartSetString(cart, dbPosKey, dbPosValue->string);
 }