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/hgTracks/config.c src/hg/hgTracks/config.c
index 87d05ea..0d989cc 100644
--- src/hg/hgTracks/config.c
+++ src/hg/hgTracks/config.c
@@ -324,36 +324,39 @@
     for (track = *pTrackList; track != NULL; track = track->next)
         {
         struct trackRef *tr;
         AllocVar(tr);
         tr->track = track;
         slAddHead(&track->group->trackList, tr);
         }
 
     /* Straighten things out, clean up, and go home. */
     for (group = *pGroupList;group != NULL; group = group->next)
         slReverse(&group->trackList);
     }
 return count;
 }
 
-void configPageSetTrackVis(int vis)
-/* Do config page after setting track visibility. If vis is -2, then visibility
- * is unchanged.  If -1 then set visibility to default, otherwise it should
- * be tvHide, tvDense, etc. */
+
+void configInitTrackList(
+    int vis, 
+    char **pGroupTarget,
+    struct track **pTrackList,
+    struct track **pIdeoTrack,
+    struct group **pGroupList
+)
 {
-struct dyString *title = dyStringNew(0);
 char *groupTarget = NULL;
 struct track *trackList =  NULL;
 struct track *ideoTrack = NULL;
 struct group *groupList = NULL;
 
 withPriorityOverride = cartUsualBoolean(cart, configPriorityOverride, FALSE);
 
 /* Get track list and group them. */
 ctList = customTracksParseCart(database, cart, &browserLines, &ctFileName);
 trackList = getTrackList(&groupList, vis);
 
 if (trackHash == NULL)
     trackHash = makeGlobalTrackHash(trackList);
 // Subtrack settings must be removed when composite/view settings are updated
 parentChildCartCleanup(trackList,cart,oldVars);
@@ -362,30 +365,51 @@
 
 /* The ideogram for some reason is considered a track.
  * We don't really want to process it as one though, so
  * we see if it's there, and if necessary remove it. */
 ideoTrack = chromIdeoTrack(trackList);
 if (ideoTrack != NULL)
     removeTrackFromGroup(ideoTrack);
 
 /* Fetch group to change on if any from CGI,
  * and remove var so it doesn't get used again. */
 groupTarget = cloneString(cartUsualString(cart, configGroupTarget, ""));
 cartRemove(cart, configGroupTarget);
 if (sameString(groupTarget, "none"))
     freez(&groupTarget);
 
+*pGroupTarget = groupTarget;
+*pTrackList = trackList;
+*pIdeoTrack = ideoTrack;
+*pGroupList = groupList;
+
+}
+
+void configPageSetTrackVis(int vis)
+/* Do config page after setting track visibility. If vis is -2, then visibility
+ * is unchanged.  If -1 then set visibility to default, otherwise it should
+ * be tvHide, tvDense, etc. */
+{
+char *groupTarget;
+struct track *trackList;
+struct track *ideoTrack;
+struct group *groupList;
+
+configInitTrackList(vis, &groupTarget, &trackList, &ideoTrack, &groupList);
+
+struct dyString *title = dyStringNew(0);
+
 dyStringPrintf(title, "Configure Image");
 
 hPrintf("<FORM ACTION=\"%s\" NAME=\"mainForm\" METHOD=%s>\n", hgTracksName(),
 	cartUsualString(cart, "formMethod", "POST"));
 webStartWrapperDetailedNoArgs(cart, database, "", title->string, FALSE, FALSE, FALSE, FALSE);
 cartSaveSession(cart);
 
 hPrintf("<INPUT TYPE=HIDDEN NAME=\"hgTracksConfigPage\" VALUE=\"\">");
 /* do not want all the submit buttons named the same thing, this one is: */
 cgiMakeButton("topSubmit", "submit");
 
 // 3 column table
 hPrintf("<TABLE style=\"border:0px; \">\n");
 hPrintf("<TR><TD>image width:");
 hPrintf("<TD style=\"text-align: right\">");
@@ -418,31 +442,31 @@
 
 themeDropDown(cart);
 
 hTableStart();
 if (ideoTrack != NULL)
     {
     hPrintf("<TR><TD>");
     hCheckBox("ideogram", cartUsualBoolean(cart, "ideogram", TRUE));
     hPrintf("</TD><TD>");
     hPrintf("Display chromosome ideogram above main graphic");
     hPrintf("</TD></TR>\n");
     }
 hPrintf("<TR><TD>");
 hCheckBox("guidelines", cartUsualBoolean(cart, "guidelines", TRUE));
 hPrintf("</TD><TD>");
-hPrintf("Show light blue vertical guidelines");
+hPrintf("Show light blue vertical guidelines, or light red vertical  window separators in multi-region view");
 hPrintf("</TD></TR>\n");
 
 hPrintf("<TR><TD>");
 hCheckBox("leftLabels", cartUsualBoolean(cart, "leftLabels", TRUE));
 hPrintf("</TD><TD>");
 hPrintf("Display labels to the left of items in tracks");
 hPrintf("</TD></TR>\n");
 
 hPrintf("<TR><TD>");
 hCheckBox("centerLabels", cartUsualBoolean(cart, "centerLabels", TRUE));
 hPrintf("</TD><TD>");
 hPrintf("Display description above each track");
 hPrintf("</TD></TR>\n");
 
 hPrintf("<TR><TD>");
@@ -463,36 +487,37 @@
 hPrintf("Next/previous exon navigation");
 hPrintf("</TD></TR>\n");
 
 hPrintf("<TR><TD>");
 hCheckBox("exonNumbers", cartUsualBoolean(cart, "exonNumbers", TRUE));
 hPrintf("</TD><TD>");
 hPrintf("Show exon numbers");
 hPrintf("</TD></TR>\n");
 
 hPrintf("<TR><TD>");
 hCheckBox("enableHighlightingDialog", cartUsualBoolean(cart, "enableHighlightingDialog", TRUE));
 hPrintf("</TD><TD>");
 hPrintf("Enable highlight with drag-and-select "
         "(if unchecked, drag-and-select always zooms to selection)");
 hPrintf("</TD></TR>\n");
-
 hTableEnd();
+
+
 cgiDown(0.9);
 
-char *freeze = hFreezeFromDb(database);
 char buf[256];
+char *freeze = hFreezeFromDb(database);
 if (freeze == NULL)
     safef(buf, sizeof buf, "Configure Tracks on %s %s: %s",
 	  organization, browserName, trackHubSkipHubName(organism));
 else if (stringIn(database, freeze))
     safef(buf, sizeof buf, "Configure Tracks on %s %s: %s %s",
 	  organization, browserName, trackHubSkipHubName(organism), freeze);
 else
     safef(buf, sizeof buf, "Configure Tracks on %s %s: %s %s (%s)",
 	  organization, browserName, trackHubSkipHubName(organism), freeze, trackHubSkipHubName(database));
 webNewSection("%s",buf);
 hPrintf("Tracks: ");
 if (isSearchTracksSupported(database,cart))
     {
     cgiMakeButtonWithMsg(TRACK_SEARCH, TRACK_SEARCH_BUTTON,TRACK_SEARCH_HINT);
     hPrintf(" ");
@@ -512,15 +537,216 @@
         "more selectively below.</div>");
 trackConfig(trackList, groupList, groupTarget, vis);
 
 dyStringFree(&title);
 freez(&groupTarget);
 webEndSectionTables();
 hPrintf("</FORM>");
 }
 
 void configPage()
 /* Put up configuration page. */
 {
 configPageSetTrackVis(-2);
 }
 
+// TODO GALT there is duplication still between config and configMultiRegionPageSetTrackVis
+//  that could maybe be addressed by pulling the code that initializes the tracklist,
+//  and the code that draws the multi-region options, into 2 functions to be called by each.
+
+void configMultiRegionPage()
+/* Do multi-region config page after setting track visibility. If vis is -2, then visibility
+ * is unchanged.  If -1 then set visibility to default, otherwise it should
+ * be tvHide, tvDense, etc. */
+{
+char *groupTarget;
+struct track *trackList;
+struct track *ideoTrack;
+struct group *groupList;
+int vis = -2;
+
+configInitTrackList(vis, &groupTarget, &trackList, &ideoTrack, &groupList);
+
+hPrintf("<FORM ACTION=\"%s\" NAME=\"mainForm\" METHOD=%s>\n", hgTracksName(),
+	cartUsualString(cart, "formMethod", "POST"));
+
+webStartWrapperDetailedNoArgs(cart, database, "", "", FALSE, FALSE, FALSE, FALSE);
+
+cartSaveSession(cart);
+
+
+hPrintf("<BR>\n");
+
+hTableStart();
+
+virtModeType = cartUsualString(cart, "virtModeType", virtModeType);
+
+hPrintf("<TR><TD>");
+cgiMakeRadioButton("virtModeType", "default", sameWord("default", virtModeType));
+hPrintf("</TD><TD>");
+hPrintf("Show single-chromsome view (default)");
+hPrintf("</TD></TR>\n");
+
+struct sqlConnection *conn = NULL;
+if (!trackHubDatabase(database))  // no db conn for assembly hubs 
+    conn = hAllocConn(database);
+
+// Do we have a gene table for exonMostly?
+findBestEMGeneTable(trackList);
+if (emGeneTable)
+    {
+    hPrintf("<TR><TD>");
+    cgiMakeRadioButton("virtModeType", "exonMostly", sameWord("exonMostly", virtModeType));
+    hPrintf("</TD><TD>");
+    hPrintf("Show exons or ");
+    cgiMakeRadioButton("virtModeType", "geneMostly", sameWord("geneMostly", virtModeType));
+    hPrintf("genes using %s. &nbsp;&nbsp; Use padding of: ", emGeneTrack->shortLabel);
+    hIntVar("emPadding", cartUsualInt(cart, "emPadding", emPadding), 3);
+    hPrintf(" bases.");
+    hPrintf("</TD></TR>\n");
+    }
+
+/* obsolete    
+if (conn && sqlTableExists(conn,"knownCanonical"))
+    {
+    hPrintf("<TR><TD>");
+    cgiMakeRadioButton("virtModeType", "kcGenes", sameWord("kcGenes", virtModeType));
+    hPrintf("</TD><TD>");
+    hPrintf("Show gene regions genome-wide.");
+    hPrintf("</TD></TR>\n");
+    }
+*/
+
+hPrintf("<TR><TD>");
+cgiMakeRadioButton("virtModeType", "customUrl", sameWord("customUrl", virtModeType));
+hPrintf("</TD><TD>");
+hPrintf("Custom regions from BED URL: ");
+hTextVar("multiRegionsBedUrl", cartUsualString(cart, "multiRegionsBedUrl", multiRegionsBedUrl), 60);
+hPrintf("</TD></TR>\n");
+
+
+/* The AllChroms option will be released in future
+if (emGeneTable && sqlTableExists(conn, emGeneTable))
+    {
+    hPrintf("<TR><TD>");
+    cgiMakeRadioButton("virtModeType", "singleTrans", sameWord("singleTrans", virtModeType));
+    hPrintf("</TD><TD>");
+    hPrintf("Show only one transcript using an ID from %s : ", emGeneTrack->shortLabel);
+    char *trans = cartUsualString(cart, "singleTransId", singleTransId);
+    char sql[1024];
+    sqlSafef(sql, sizeof sql, "select name from %s where name='%s'", emGeneTable, trans);
+    char *result = sqlQuickString(conn, sql);
+    if (!result)
+	{
+	sqlSafef(sql, sizeof sql, "select name from %s limit 1", emGeneTable);
+	trans = sqlQuickString(conn, sql);
+	}
+    hTextVar("singleTransId", trans, 20);
+    hPrintf("</TD></TR>\n");
+    }
+*/
+
+if (conn && sqlTableExists(conn, "altLocations"))
+    {
+    hPrintf("<TR><TD>");
+    cgiMakeRadioButton("virtModeType", "singleAltHaplo", sameWord("singleAltHaplo", virtModeType));
+    hPrintf("</TD><TD>");
+    hPrintf("Show one alternate haplotype, placed on its chromosome, using ID: ");
+    char *haplo = cartUsualString(cart, "singleAltHaploId", singleAltHaploId);
+    char sql[1024];
+    sqlSafef(sql, sizeof sql, "select name from altLocations where name='%s'", haplo);
+    char *result = sqlQuickString(conn, sql);
+    if (!result)
+	{
+	sqlSafef(sql, sizeof sql, "select name from altLocations limit 1");
+	haplo = sqlQuickString(conn, sql);
+	}
+    hTextVar("singleAltHaploId", haplo, 20);
+    hPrintf("</TD></TR>\n");
+    }
+
+/* disable demo for now
+if (sameString(database,"hg19") || sameString(database, "hg38"))
+    {
+    hPrintf("<TR><TD>");
+    cgiMakeRadioButton("virtModeType", "demo1", sameWord("demo1", virtModeType));
+    hPrintf("</TD><TD>");
+    hPrintf("demo1 two windows on two chroms (default pos on chr21, and same loc on chr22)");
+    hPrintf("</TD></TR>\n");
+    }
+*/
+
+
+/* Disabled for now 
+hPrintf("<TR><TD>");
+cgiMakeRadioButton("virtModeType", "demo2", sameWord("demo2", virtModeType));
+hPrintf("</TD><TD>");
+hPrintf("demo2 multiple "); 
+hIntVar("demo2NumWindows", cartUsualInt(cart, "demo2NumWindows", demo2NumWindows), 3);
+hPrintf(" windows on one chrom chr21 def posn, window size ");
+hIntVar("demo2WindowSize", cartUsualInt(cart, "demo2WindowSize", demo2WindowSize), 3);
+hPrintf(" and step size ");
+hIntVar("demo2StepSize", cartUsualInt(cart, "demo2StepSize", demo2StepSize), 3);
+hPrintf(" exon-like");
+hPrintf("</TD></TR>\n");
+*/
+
+/* The AllChroms option will be released in future
+if (conn)  // requires chromInfo from database. 
+    { // TODO allow it to use assembly hubs via trackHubAllChromInfo() ?
+    hPrintf("<TR><TD>");
+    cgiMakeRadioButton("virtModeType", "allChroms", sameWord("allChroms", virtModeType));
+    hPrintf("</TD><TD>");
+    hPrintf("<br>Show all chromosomes.<br><span style='color:red'>Warning:</span> Turn off all tracks except bigBed, bigWig, and very sparse tracks.<br>Press Hide All to hide all tracks.");
+    hPrintf("</TD></TR>\n");
+    }
+*/
+
+
+/* Disabled for now 
+hPrintf("<TR><TD>");
+cgiMakeRadioButton("virtModeType", "demo4", sameWord("demo4", virtModeType));
+hPrintf("</TD><TD>");
+hPrintf("demo4 multiple (311) windows showing exons from TITIN gene uc031rqd.1.");
+hPrintf("</TD></TR>\n");
+*/
+
+/* Disabled for now 
+hPrintf("<TR><TD>");
+cgiMakeRadioButton("virtModeType", "demo5", sameWord("demo5", virtModeType));
+hPrintf("</TD><TD>");
+hPrintf("demo5 alt locus on hg38. Shows alt chrom surrounded by regions of same size from reference genome.");
+hPrintf("</TD></TR>\n");
+*/
+
+/* Disabled for now 
+hPrintf("<TR><TD>");
+cgiMakeRadioButton("virtModeType", "demo6", sameWord("demo6", virtModeType));
+hPrintf("</TD><TD>");
+hPrintf("demo6 shows zoomed in exon-exon junction from SOD1 gene, between exon1 and exon2.");
+hPrintf("</TD></TR>\n");
+*/
+
+
+hTableEnd();
+
+hPrintf("<BR>\n");
+hPrintf("<TABLE style=\"border:0px; \">\n");
+hPrintf("<TR><TD>");
+hCheckBox("emAltHighlight", cartUsualBoolean(cart, "emAltHighlight", FALSE));
+hPrintf("</TD><TD>");
+hPrintf("Highlight alternating regions in multi-region view");
+hPrintf("</TD></TR>\n");
+hPrintf("</TABLE>\n");
+
+hPrintf("<BR>\n");
+cgiMakeButton("topSubmit", "submit");
+
+hFreeConn(&conn);
+
+cgiDown(0.9);
+
+freez(&groupTarget);
+webEndSectionTables();
+hPrintf("</FORM>");
+}
+