06d7be056190c14b85e71bc12523f18ea6815b5e
markd
  Mon Dec 7 00:50:29 2020 -0800
BLAT mmap index support merge with master

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index b47b461..1eea6ae 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -61,30 +61,31 @@
 #include "errCatch.h"
 #include "iupac.h"
 #include "botDelay.h"
 #include "chromInfo.h"
 #include "extTools.h"
 #include "basicBed.h"
 #include "customFactory.h"
 #include "genbank.h"
 #include "bigWarn.h"
 #include "wigCommon.h"
 #include "knetUdc.h"
 #include "hex.h"
 #include <openssl/sha.h>
 #include "customComposite.h"
 #include "chromAlias.h"
+#include "jsonWrite.h"
 
 //#include "bed3Sources.h"
 
 /* Other than submit and Submit all these vars should start with hgt.
  * to avoid weeding things out of other program's namespaces.
  * Because the browser is a central program, most of its cart
  * variables are not hgt. qualified.  It's a good idea if other
  * program's unique variables be qualified with a prefix though. */
 char *excludeVars[] = { "submit", "Submit", "dirty", "hgt.reset",
             "hgt.in1", "hgt.in2", "hgt.in3", "hgt.inBase",
             "hgt.out1", "hgt.out2", "hgt.out3", "hgt.out4",
             "hgt.left1", "hgt.left2", "hgt.left3",
             "hgt.right1", "hgt.right2", "hgt.right3",
             "hgt.dinkLL", "hgt.dinkLR", "hgt.dinkRL", "hgt.dinkRR",
             "hgt.tui", "hgt.hideAll", "hgt.visAllFromCt",
@@ -148,30 +149,40 @@
 boolean hideControls = FALSE;           /* Hide all controls? */
 boolean trackImgOnly = FALSE;           /* caller wants just the track image and track table html */
 boolean ideogramToo =  FALSE;           /* caller wants the ideoGram (when requesting just one track) */
 
 /* Structure returned from resolvePosition.
  * We use this to to expand any tracks to full
  * that were found to contain the searched-upon
  * position string */
 struct hgPositions *hgp = NULL;
 
 /* Other global variables. */
 struct group *groupList = NULL;    /* List of all tracks. */
 char *browserName;              /* Test, preview, or public browser */
 char *organization;             /* UCSC */
 
+/* mouseOver popUp global data, each track that wants to send json
+ * data will need to know the json file name from mouseOverJson
+ * and will write the track data to mouseOverJson.  The structure
+ * of the data in the json output will depend upon what the track needs
+ * to send.
+ */
+boolean enableMouseOver = FALSE;
+struct tempName *mouseOverJsonFile = NULL;
+struct jsonWrite *mouseOverJson = NULL;
+
 struct hash *trackHash = NULL; /* Hash of the tracks by their name. */
 
 #ifdef DEBUG
 void uglySnoopTrackList(int depth, struct track *trackList)
 /* Print out some info on track list. */
 {
 struct track *track;
 for (track = trackList; track != NULL; track = track->next)
     {
     if (stringIn("FaireH1h", track->track))
 	{
 	repeatCharOut(uglyOut, '+', depth);
 	}
     uglySnoopTrackList(depth+1, track->subtracks);
     }
@@ -620,30 +631,33 @@
        cytoband track where the starts and ends of two
        bands overlaps by one. */
     if (winEnd >  cb->chromStart
     &&  winEnd <= cb->chromEnd)
         {
         safef(endBand, buffSize, "%s", cb->name);
         }
     }
 }
 
 static void maybeNewFonts(struct hvGfx *hvg)
 /* Check to see if we want to use the alternate font engine (FreeType2). */
 {
 if (sameString(cfgOptionDefault("freeType", "off"), "on"))
     {
+    if (sameString(tl.textFont, "Bitmap"))
+        return;
+
     char *fontDir = cfgOptionDefault("freeTypeDir", "/usr/share/fonts/default/Type1");
     char buffer[4096];
 
     extern char *freeTypeFontNames[];
     extern char *freeTypeFontFiles[];
     int ii;
     for(ii=0; ii < 33; ii++)
         if (sameString(freeTypeFontNames[ii], tl.textFont))
             break;
     char *fontFile = freeTypeFontFiles[ii];
     char *fontName = freeTypeFontNames[ii];
     safef(buffer, sizeof buffer, "%s/%s", fontDir, fontFile);
     hvGfxSetFontMethod(hvg, FONT_METHOD_FREETYPE, fontName, buffer );
     }
 }
@@ -5029,30 +5043,49 @@
 imagePixelHeight = pixHeight;
 if (psOutput)
     {
     hvg = hvGfxOpenPostScript(pixWidth, pixHeight, psOutput);
     hvgSide = hvg; // Always only one image
     }
 else
     {
     boolean transparentImage = FALSE;
     if (theImgBox!=NULL)
         transparentImage = TRUE;   // transparent because BG (blue ruler lines) is separate image
 
     if (measureTiming)
         measureTime("Time at start of obtaining trash hgt png image file");
     trashDirFile(&pngTn, "hgt", "hgt", ".png");
+    if (enableMouseOver)
+	{   /* created here at this time to get the same name as .png file
+	     * it is copied from pngTn since if we repeated trashFileDir()
+	     * to get the name, it could be different since there is a
+	     * timestamp involved in making the name.
+	     */
+        /* will open this file upon successful exit to write the data */
+	AllocVar(mouseOverJsonFile);
+	char *tmpStr = cloneString(pngTn.forCgi);
+	char *jsonStr = replaceChars(tmpStr, ".png", ".json");
+	safef(mouseOverJsonFile->forCgi, ArraySize(mouseOverJsonFile->forCgi), "%s", jsonStr);
+	freeMem(tmpStr);
+	freeMem(jsonStr);
+	tmpStr = cloneString(pngTn.forHtml);
+        jsonStr = replaceChars(tmpStr, ".png", ".json");
+	safef(mouseOverJsonFile->forHtml, ArraySize(mouseOverJsonFile->forHtml), "%s", jsonStr);
+	freeMem(tmpStr);
+	freeMem(jsonStr);
+	}
     hvg = hvGfxOpenPng(pixWidth, pixHeight, pngTn.forCgi, transparentImage);
 
     if (theImgBox)
         {
         // Adds one single image for all tracks (COULD: build the track by track images)
         theOneImg = imgBoxImageAdd(theImgBox,pngTn.forHtml,NULL,pixWidth, pixHeight,FALSE);
         theSideImg = theOneImg; // Unlkess this is overwritten below, there is a single image
         }
 
     hvgSide = hvg; // Unless this is overwritten below, there is a single image
 
     if (theImgBox && theImgBox->showPortal && withLeftLabels)
         {
         // TODO: It would be great to make the two images smaller,
         //       but keeping both the same full size for now
@@ -6950,30 +6983,44 @@
 if (cartOptionalString(cart, "hgt.trackNameFilter") == NULL)
     { // If a single track was asked for and it is from a hub, then it is already in trackList
     loadTrackHubs(&trackList, &grpList);
     }
 loadCustomTracks(&trackList);
 groupTracks( &trackList, pGroupList, grpList, vis);
 setSearchedTrackToPackOrFull(trackList);
 boolean hideTracks = cgiOptionalString( "hideTracks") != NULL;
 if (hideTracks)
     changeTrackVis(groupList, NULL, tvHide);    // set all top-level tracks to hide
 
 /* Get visibility values if any from ui. */
 struct hash *superTrackHash = newHash(5);  // cache whether supertrack is hiding tracks or not
 char buffer[4096];
 
+// Check to see if we have a versioned default gene track and let the knownGene 
+// cart variable determine its visibility
+char *defaultGeneTrack = NULL;
+char *knownDb = hdbDefaultKnownDb(database);
+if (differentString(knownDb, database))
+    defaultGeneTrack = hdbGetMasterGeneTrack(knownDb);
+
+if (defaultGeneTrack)
+    {
+    char *s = cartOptionalString(cart, "knownGene");
+    if ((s != NULL) && (differentString(s, "hide")))
+        cartSetString(cart, defaultGeneTrack, s);
+    }
+
 for (track = trackList; track != NULL; track = track->next)
     {
     // deal with any supertracks we're seeing for the first time
     if (tdbIsSuperTrackChild(track->tdb))
         {
         struct hashEl *hel = NULL;
 
         if ((hel = hashLookup(superTrackHash, track->tdb->parent->track)) == NULL)   // we haven't seen this guy
             {
             // first deal with visibility of super track
             char *s = hideTracks ? cgiOptionalString(track->tdb->parent->track) : cartOptionalString(cart, track->tdb->parent->track);
             if (s)
                 {
                 track->tdb->parent->visibility = hTvFromString(s) ;
                 cartSetString(cart, track->tdb->parent->track, s);
@@ -8664,30 +8711,34 @@
 	{
 	// TODO should be more than this. But at least this makes the same trackList mods to the other windows.
 	if (nukeIdeoFromList)
 	    {
 	    struct track *ideoTrack = chromIdeoTrack(window->trackList);
 	    if (ideoTrack)
 		{
 		slRemoveEl(&window->trackList, ideoTrack);
 		}
 	    }
 	}
 
     }
 setGlobalsFromWindow(windows); // first window // restore globals
 
+/* DBG - a message box to display information from the javascript
+hPrintf("<div id='mouseDbg'><span id='dbgMouseOver'><p>. . . dbgMouseOver</p></span></div>\n");
+ */
+
 #ifdef USE_NAVIGATION_LINKS
 hPrintf("<TABLE BORDER=0 CELLPADDING=0 width='%d'><tr style='font-size:small;'>\n",
         tl.picWidth);//min(tl.picWidth, 800));
 hPrintf("<td width='40' align='left'><a href='?hgt.left3=1' "
         "title='move 95&#37; to the left'>&lt;&lt;&lt;</a>\n");
 hPrintf("<td width='30' align='left'><a href='?hgt.left2=1' "
         "title='move 47.5&#37; to the left'>&lt;&lt;</a>\n");
 hPrintf("<td width='20' align='left'><a href='?hgt.left1=1' "
         "title='move 10&#37; to the left'>&lt;</a>\n");
 
 hPrintf("<td>&nbsp;</td>\n"); // Without width cell expands table with, forcing others to sides
 hPrintf("<td width='40' align='left'><a href='?hgt.in1=1' "
         "title='zoom in 1.5x'>&gt;&nbsp;&lt;</a>\n");
 hPrintf("<td width='60' align='left'><a href='?hgt.in2=1' "
         "title='zoom in 3x'>&gt;&gt;&nbsp;&lt;&lt;</a>\n");
@@ -8740,31 +8791,31 @@
     hPrintf("<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=1 WIDTH=%d COLS=%d><TR>\n",
             tl.picWidth, 27);
 #ifndef USE_NAVIGATION_LINKS
     hPrintf("<TD COLSPAN=6 ALIGN=left NOWRAP>");
     hPrintf("move start<BR>");
     hButtonWithOnClick("hgt.dinkLL", " < ", "move start position to the left",
                        "return imageV2.navigateButtonClick(this);");
     hTextVar("dinkL", cartUsualString(cart, "dinkL", "2.0"), 3);
     hButtonWithOnClick("hgt.dinkLR", " > ", "move start position to the right",
                        "return imageV2.navigateButtonClick(this);");
     hPrintf("</TD>");
     hPrintf("<td width='30'>&nbsp;</td>\n");
 #endif//ndef USE_NAVIGATION_LINKS
     hPrintf("<TD class='infoText' COLSPAN=15 style=\"white-space:normal\">"); // allow this text to wrap
     hWrites("Click on a feature for details. ");
-    hWrites("Click or drag in the base position track to zoom in. ");
+    hWrites("Click+shift+drag to zoom in. ");
     hWrites("Click side bars for track options. ");
     hWrites("Drag side bars or labels up or down to reorder tracks. ");
     hWrites("Drag tracks left or right to new position. ");
     hWrites("Press \"?\" for keyboard shortcuts. ");
     hPrintf("</TD>");
 #ifndef USE_NAVIGATION_LINKS
     hPrintf("<td width='30'>&nbsp;</td>\n");
     hPrintf("<TD COLSPAN=6 ALIGN=right NOWRAP>");
     hPrintf("move end<BR>");
     hButtonWithOnClick("hgt.dinkRL", " < ", "move end position to the left",
                        "return imageV2.navigateButtonClick(this);");
     hTextVar("dinkR", cartUsualString(cart, "dinkR", "2.0"), 3);
     hButtonWithOnClick("hgt.dinkRR", " > ", "move end position to the right",
                        "return imageV2.navigateButtonClick(this);");
     hPrintf("</TD>");
@@ -9794,30 +9845,31 @@
 
 if (virtWinEnd > virtSeqBaseCount)
     {
     virtWinEnd = virtSeqBaseCount;
     }
 
 if (virtWinStart > virtSeqBaseCount)
     {
     virtWinStart = virtSeqBaseCount - 1000;
     }
 
 virtWinBaseCount = virtWinEnd - virtWinStart;
 if (virtWinBaseCount <= 0)
     hUserAbort("Window out of range on %s", virtChromName);
 
+
 if (!cartUsualBoolean(cart, "hgt.psOutput", FALSE)
  && !cartUsualBoolean(cart, "hgt.imageV1" , FALSE))
     {
 
     // TODO GALT Guidelines broken on virtChrom for 3X.
     //  works in demo0 or real chrom. Only the guidelines seem to be messed up.
     //  Other stuff works. 1X works too.
     // Since we are not using 3X for now, I will leave this for a future fix.
     // To test 3X, do make clean; make CFLAGS=-DIMAGEv2_DRAG_SCROLL_SZ=3
 
     // Start an imagebox (global for now to avoid huge rewrite of hgTracks)
     // Set up imgBox dimensions
     int sideSliceWidth  = 0;   // Just being explicit
     if (withLeftLabels)
         sideSliceWidth   = (fullInsideX - gfxBorder*3) + 2;
@@ -10522,30 +10574,47 @@
         cartSetString(cart, "highlight", newHighlight);
     cartRemove(cart, "addHighlight");
     }
 }
 
 extern boolean issueBotWarning;
 
 void doMiddle(struct cart *theCart)
 /* Print the body of an html file.   */
 {
 cart = theCart;
 measureTiming = hPrintStatus() && isNotEmpty(cartOptionalString(cart, "measureTiming"));
 if (measureTiming)
     measureTime("Startup (bottleneck %d ms) ", botDelayMillis);
 
+char *mouseOverEnabled = cfgOption("mouseOverEnabled");
+if (sameWordOk(mouseOverEnabled, "on"))
+    {
+    enableMouseOver = TRUE;
+    /* mouseOverJsonFile will be initializes and created at the same
+     * time as the browser .png image file
+     */
+    mouseOverJson = jsonWriteNew();
+    jsonWriteObjectStart(mouseOverJson, NULL);
+    /* this jsonWrite structure will finish off upon successful exit.
+     * each track will start a list with the track name:
+     *   jsonWriteListStart(mouseOverJson, tg->track);
+     */
+    }
+else
+    enableMouseOver = FALSE;
+
 if (issueBotWarning)
     {
     char *ip = getenv("REMOTE_ADDR");
     botDelayMessage(ip, botDelayMillis);
     }
 
 char *debugTmp = NULL;
 /* Uncomment this to see parameters for debugging. */
 /* struct dyString *state = NULL; */
 /* Initialize layout and database. */
 if (measureTiming)
     measureTime("Get cart of %d for user:%s session:%s", theCart->hash->elCount,
 	    theCart->userId, theCart->sessionId);
 /* #if 1 this to see parameters for debugging. */
 /* Be careful though, it breaks if custom track
@@ -10612,55 +10681,57 @@
     jsIncludeFile("jquery-ui.js", NULL);
     jsIncludeFile("utils.js", NULL);
     jsIncludeFile("ajax.js", NULL);
     jsIncludeFile("jquery.watermarkinput.js", NULL);
     if(!searching)
         {
         jsIncludeFile("jquery.history.js", NULL);
         jsIncludeFile("jquery.imgareaselect.js", NULL);
         }
     jsIncludeFile("autocomplete.js", NULL);
     jsIncludeFile("es5-shim.4.0.3.min.js", NULL);
     jsIncludeFile("es5-sham.4.0.3.min.js", NULL);
     jsIncludeFile("lodash.3.10.0.compat.min.js", NULL);
     jsIncludeFile("autocompleteCat.js", NULL);
     jsIncludeFile("hgTracks.js", NULL);
-//    jsIncludeFile("mouseOver.js", NULL);
     jsIncludeFile("spectrum.min.js", NULL);
 
 #ifdef LOWELAB
     jsIncludeFile("lowetooltip.js", NULL);
 #endif///def LOWELAB
 
     webIncludeResourceFile("spectrum.min.css");
     webIncludeResourceFile("jquery-ui.css");
-//    webIncludeResourceFile("mouseOver.css");
+    if (enableMouseOver)
+      webIncludeResourceFile("mouseOver.css");
+
     if (!searching)     // NOT doing search
         {
         webIncludeResourceFile("jquery.contextmenu.css");
         jsIncludeFile("jquery.contextmenu.js", NULL);
         webIncludeResourceFile("ui.dropdownchecklist.css");
         jsIncludeFile("ui.dropdownchecklist.js", NULL);
         jsIncludeFile("ddcl.js", NULL);
         }
 
     hPrintf("<div id='hgTrackUiDialog' style='display: none'></div>\n");
     hPrintf("<div id='hgTracksDialog' style='display: none'></div>\n");
 
     cartFlushHubWarnings();
     }
 
+
 if (cartVarExists(cart, "chromInfoPage"))
     {
     cartRemove(cart, "chromInfoPage");
     chromInfoPage();
     }
 else if (differentString(cartUsualString(cart, TRACK_SEARCH,"0"),"0"))
     {
     doSearchTracks(groupList);
     }
 else if (sameWord(configPageCall, "configure") ||
          sameWord(configPageCall, "configure tracks and display"))
     {
     cartRemove(cart, "hgTracksConfigPage");
     configPage();
     }
@@ -10733,30 +10804,54 @@
 
 jsonObjectAdd(jsonForClient, "measureTiming", newJsonBoolean(measureTiming));
 // js code needs to know if a highlightRegion is defined for this db
 checkAddHighlight(); // call again in case tracksDisplay's call to resolvePosition changed vars
 char *highlightDef = cartOptionalString(cart, "highlight");
 if (highlightDef)
     jsonObjectAdd(jsonForClient, "highlight", newJsonString(highlightDef));
 jsonObjectAdd(jsonForClient, "enableHighlightingDialog",
 	      newJsonBoolean(cartUsualBoolean(cart, "enableHighlightingDialog", TRUE)));
 
 struct dyString *dy = dyStringNew(1024);
 jsonDyStringPrint(dy, (struct jsonElement *) jsonForClient, "hgTracks", 0);
 jsInline(dy->string);
 dyStringFree(&dy);
 
+dy = dyStringNew(1024);
+if (enableMouseOver)
+    {
+    jsonWriteObjectEnd(mouseOverJson);
+    /* if any data was written, it is longer than 4 bytes */
+    if (strlen(mouseOverJson->dy->string) > 4)
+	{
+	FILE *trashJson = mustOpen(mouseOverJsonFile->forCgi, "w");
+	fputs(mouseOverJson->dy->string,trashJson);
+	carefulClose(&trashJson);
+	}
+
+    hPrintf("<div id='mouseOverVerticalLine' class='mouseOverVerticalLine'></div>\n");
+    hPrintf("<div id='mouseOverText' class='mouseOverText'></div>\n");
+    dyStringPrintf(dy, "window.browserTextSize=%s;\n", tl.textSize);
+    dyStringPrintf(dy, "window.mouseOverEnabled=true;\n");
+    }
+else
+    {
+    dyStringPrintf(dy, "window.mouseOverEnabled=false;\n");
+    }
+jsInline(dy->string);
+dyStringFree(&dy);
+
 if (measureTiming)
     measureTime("Time at end of doMiddle, next up cart write");
 
 if (cartOptionalString(cart, "udcTimeout"))
     {
     warn("The Genome Browser cart currently includes the \"udcTimeout\" string. "
 	"While this is useful for debugging hubs, it may negatively impact "
 	"performance.   To clear this variable, click "
 	"<A HREF='hgTracks?hgsid=%s|url|&udcTimeout=[]'>here</A>.",cartSessionId(cart));
     }
 }
 
 void labelTrackAsFilteredNumber(struct track *tg, unsigned numOut)
 /* add text to track long label to indicate filter is active */
 {