e1988a798585c610e2399538a900fd57cfa83d63
galt
  Tue Apr 22 11:19:22 2025 -0700
Fixes VCF does not work on extended case-color options in Get DNA for vcf, vcfTabix, and vcfPhasedTrio. fixes #16386

diff --git src/hg/hgc/vcfClick.c src/hg/hgc/vcfClick.c
index 06849667560..aafd9acbfd6 100644
--- src/hg/hgc/vcfClick.c
+++ src/hg/hgc/vcfClick.c
@@ -7,30 +7,31 @@
 #include "dystring.h"
 #include "errCatch.h"
 #include "hCommon.h"
 #include "hdb.h"
 #include "hgc.h"
 #include "htmshell.h"
 #include "jsHelper.h"
 #include "pgSnp.h"
 #include "regexHelper.h"
 #include "trashDir.h"
 #include "knetUdc.h"
 #include "udc.h"
 #include "vcf.h"
 #include "vcfUi.h"
 #include "trackHub.h"
+#include "featureBits.h"
 
 #define NA "<em>n/a</em>"
 
 static void printKeysWithDescriptions(struct vcfFile *vcff, int wordCount, char **words,
 				      struct vcfInfoDef *infoDefs, boolean stripToSymbol)
 /* Given an array of keys, print out a list of values with descriptions if descriptions are
  * available.  If stripToSymbol, when searching infoDefs, pick the actual key out of
  * <>'s and other extraneous stuff (e.g. "(C)<DEL>" --> "DEL"). */
 {
 int i;
 for (i = 0;  i < wordCount; i++)
     {
     if (i > 0)
 	printf(", ");
     char *displayKey = words[i];
@@ -509,95 +510,142 @@
 boolean showLeftBase = (rec->chromStart == vcfStart+1);
 (void)vcfRecordTrimAllelesRight(rec);
 char *displayAls[rec->alleleCount];
 makeDisplayAlleles(rec, showLeftBase, leftBase, 20, TRUE, FALSE, displayAls);
 printPosOnChrom(seqName, rec->chromStart, rec->chromEnd, NULL, FALSE, rec->name);
 printf("<B>Reference allele:</B> %s<BR>\n", displayAls[0]);
 vcfAltAlleleDetails(rec, displayAls);
 vcfQualDetails(rec);
 vcfFilterDetails(rec);
 vcfInfoDetails(rec, tdb->track, recordCount);
 pgSnpCodingDetail(rec);
 makeDisplayAlleles(rec, showLeftBase, leftBase, 5, FALSE, TRUE, displayAls);
 vcfGenotypesDetails(rec, tdb, displayAls);
 }
 
-void doVcfDetailsCore(struct trackDb *tdb, char *fileOrUrl, boolean isTabix)
+void doVcfDetailsCore(struct trackDb *tdb, char *fileOrUrl, boolean isTabix, struct featureBits **pFbList, int rgnStart, int rgnEnd)
 /* Show item details using fileOrUrl. */
 {
+if (!pFbList)
     genericHeader(tdb, NULL);
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
+if (pFbList)
+    {
+    start = rgnStart;
+    end = rgnEnd;
+    }
+
 int vcfMaxErr = -1;
 struct vcfFile *vcff = NULL;
 /* protect against temporary network or parsing error */
 struct errCatch *errCatch = errCatchNew();
 if (errCatchStart(errCatch))
     {
     if (isTabix)
         {
         char *indexUrl = trackDbSetting(tdb, "bigDataIndex");
 	vcff = vcfTabixFileAndIndexMayOpen(fileOrUrl, indexUrl, seqName, start, end, vcfMaxErr, -1);
         }
     else
 	vcff = vcfFileMayOpen(fileOrUrl, seqName, start, end, vcfMaxErr, -1, TRUE);
     }
 errCatchEnd(errCatch);
 if (errCatch->gotError)
     {
     if (isNotEmpty(errCatch->message->string))
 	warn("%s", errCatch->message->string);
     }
 errCatchFree(&errCatch);
+
+struct featureBits *fbList = NULL, *fb;
 if (vcff != NULL)
     {
     struct vcfRecord *rec;
     // use the recordCount to make up unique id strings for html collapsible INFO section later
     int recordCount = 0;
     for (rec = vcff->records;  rec != NULL;  rec = rec->next)
+        {
+        if (pFbList)
+	    {
+            AllocVar(fb);
+            fb->chrom = rec->chrom;
+            fb->start = rec->chromStart;
+            fb->end = rec->chromEnd;
+            fb->strand = '+';
+            slAddHead(&fbList, fb);
+	    }
+	else
 	    {
 	    if (rec->chromStart == start && rec->chromEnd == end) // in pgSnp mode, don't get name
 		vcfRecordDetails(tdb, rec, recordCount);
+	    }
 	recordCount++;
         }
     }
 else
+    {
+    if (!pFbList)
         printf("Sorry, unable to open %s<BR>\n", fileOrUrl);
+    }
+if (pFbList)
+    {
+    slReverse(&fbList); 
+    *pFbList = fbList;
+    }
+else
+    {
     printTrackHtml(tdb);
     }
+}
 
 
 
-void doVcfTabixDetails(struct trackDb *tdb, char *item)
+void doVcfTabixDetailsExt(struct trackDb *tdb, char *item, struct featureBits **pFbList, int start, int end)
 /* Show details of an alignment from a VCF file compressed and indexed by tabix. */
 {
 knetUdcInstall();
 if (udcCacheTimeout() < 300)
     udcSetCacheTimeout(300);
 struct sqlConnection *conn = NULL;
+
 if (!trackHubDatabase(database))
     conn = hAllocConnTrack(database, tdb);
+
 char *fileOrUrl = bbiNameFromSettingOrTableChrom(tdb, conn, tdb->table, seqName);
 hFreeConn(&conn);
-doVcfDetailsCore(tdb, fileOrUrl, TRUE);
+doVcfDetailsCore(tdb, fileOrUrl, TRUE, pFbList, start, end);
 }
 
+void doVcfTabixDetails(struct trackDb *tdb, char *item)
+/* Show details of an alignment from a VCF file compressed and indexed by tabix. */
+{
+doVcfTabixDetailsExt(tdb, item, NULL, 0, 0);
+}
 
 
 
-void doVcfDetails(struct trackDb *tdb, char *item)
+
+void doVcfDetailsExt(struct trackDb *tdb, char *item, struct featureBits **pFbList, int start, int end)
 /* Show details of an alignment from an uncompressed VCF file. */
 {
 struct customTrack *ct = lookupCt(tdb->track);
 struct sqlConnection *conn = NULL;
 char *table = tdb->table;
 if (ct)
     {
     conn = hAllocConn(CUSTOM_TRASH);
     table = ct->dbTableName;
     }
 else
     conn = hAllocConnTrack(database, tdb);
 char *fileOrUrl = bbiNameFromSettingOrTableChrom(tdb, conn, table, seqName);
 hFreeConn(&conn);
-doVcfDetailsCore(tdb, fileOrUrl, FALSE);
+doVcfDetailsCore(tdb, fileOrUrl, FALSE, pFbList, start, end);
 }
+
+void doVcfDetails(struct trackDb *tdb, char *item)
+/* Show details of an alignment from an uncompressed VCF file. */
+{
+doVcfDetailsExt(tdb, item, NULL, 0, 0);
+}
+