9174a163b341bfd2dd5c8fe84952265e74bef32a
angie
  Sun Sep 27 23:50:14 2020 -0700
Added setting sampleColorFile so one or more sample coloring schemes can be offered for the tree in VCF+tree tracks.  refs #26177
TODO: document the new setting

diff --git src/hg/lib/vcfUi.c src/hg/lib/vcfUi.c
index 83066d9..53600c9 100644
--- src/hg/lib/vcfUi.c
+++ src/hg/lib/vcfUi.c
@@ -170,31 +170,36 @@
         printf("</TD><TD>using the tree specified in file associated with track</TD></TR>");
         }
     printf("<TR><TD>");
     cgiMakeRadioButton(varName, VCF_HAP_METHOD_CENTER_WEIGHTED,
                        sameString(hapMethod, VCF_HAP_METHOD_CENTER_WEIGHTED));
     printf("</TD><TD>");
     vcfCfgHaplotypeCenter(cart, tdb, track, parentLevel, vcff, NULL, NULL, 0, "mainForm");
     puts("<TR><TD>");
     cgiMakeRadioButton(varName, VCF_HAP_METHOD_FILE_ORDER,
                        sameString(hapMethod, VCF_HAP_METHOD_FILE_ORDER));
     puts("</TD><TD>using the order in which samples appear in the underlying VCF file</TD></TR>");
     puts("</TABLE>");
     jsInlineF("$('input[type=radio][name=\"%s\"]').change(function() { "
               "if (this.value == '"VCF_HAP_METHOD_CENTER_WEIGHTED"') {"
               "  $('#leafShapeContainer').show();"
+              "  $('#sampleColorContainer').hide();"
+              "} else if (this.value == '"VCF_HAP_METHOD_TREE_FILE"') {"
+              "  $('#sampleColorContainer').show();"
+              "  $('#leafShapeContainer').hide();"
               "} else {"
+              "  $('#sampleColorContainer').hide();"
               "  $('#leafShapeContainer').hide();"
               "}});\n",
               varName);
     }
 }
 
 //TODO: share this code w/hgTracks, hgc in hg/lib/vcfFile.c
 static struct vcfFile *vcfHopefullyOpenHeader(struct cart *cart, struct trackDb *tdb)
 /* Defend against network errors and return the vcfFile object with header data, or NULL. */
 {
 knetUdcInstall();
 if (udcCacheTimeout() < 300)
     udcSetCacheTimeout(300);
 char *fileOrUrl = trackDbSetting(tdb, "bigDataUrl");
 if (isEmpty(fileOrUrl))
@@ -266,30 +271,71 @@
     {
     cgiMakeRadioButton(varName, VCF_HAP_COLORBY_FUNCTION,
                        sameString(colorBy, VCF_HAP_COLORBY_FUNCTION));
     printf("reference alleles invisible, alternate alleles in "
            "<span style='color:red'>red</span> for non-synonymous, "
            "<span style='color:green'>green</span> for synonymous, "
            "<span style='color:blue'>blue</span> for UTR/noncoding, "
            "black otherwise<BR>\n");
     }
 cgiMakeRadioButton(varName, VCF_HAP_COLORBY_REFALT, sameString(colorBy, VCF_HAP_COLORBY_REFALT));
 printf("reference alleles in blue, alternate alleles in red<BR>\n");
 cgiMakeRadioButton(varName, VCF_HAP_COLORBY_BASE, sameString(colorBy, VCF_HAP_COLORBY_BASE));
 printf("first base of allele (A = red, C = blue, G = green, T = magenta)<BR>\n");
 }
 
+static void vcfCfgHapClusterSampleColor(struct cart *cart, struct trackDb *tdb, char *name,
+                                        boolean parentLevel)
+/* If sampleColorFile specifies multiple files, when hapClusterMethod treeFile is selected,
+ * let the user choose sample-coloring scheme for the tree. */
+{
+char *tdbSetting = trackDbSetting(tdb, VCF_SAMPLE_COLOR_FILE);
+if (tdbSetting && strchr(tdbSetting, ' '))
+    {
+    char *hapMethod = cartOrTdbString(cart, tdb, VCF_HAP_METHOD_VAR, VCF_DEFAULT_HAP_METHOD);
+    printf("<div id='sampleColorContainer'%s>\n",
+           startsWithWord(VCF_HAP_METHOD_TREE_FILE, hapMethod) ? "" : " style='display: none;'");
+    printf("<b>Sample coloring scheme for tree:</b><br>\n");
+    char *setting = cartOrTdbString(cart, tdb, VCF_SAMPLE_COLOR_FILE, tdbSetting);
+    char *options[16];
+    int optionCount = chopLine(tdbSetting, options);
+    char *labels[optionCount];
+    char *values[optionCount];
+    int i;
+    for (i = 0;  i < optionCount;  i++)
+        {
+        char *eq = strchr(options[i], '=');
+        if (eq)
+            {
+            *eq = '\0';
+            labels[i] = options[i];
+            replaceChar(options[i], '_', ' ');
+            values[i] = eq+1;
+            }
+        else
+            {
+            labels[i] = values[i] = options[i];
+            }
+        }
+    char *selected = strchr(setting, ' ') ? values[0] : setting;
+    char varName[1024];
+    safef(varName, sizeof varName, "%s." VCF_SAMPLE_COLOR_FILE, name);
+    cgiMakeDropListWithVals(varName, labels, values, optionCount, selected);
+    puts("</div>");
+    }
+}
+
 static void vcfCfgHapClusterTreeAngle(struct cart *cart, struct trackDb *tdb, char *name,
 				   boolean parentLevel)
 /* Let the user choose branch shape. */
 {
 // This option applies only to center-weighted clustering; don't show option when some other
 // method is selected.
 char *hapMethod = cartOrTdbString(cart, tdb, VCF_HAP_METHOD_VAR, VCF_DEFAULT_HAP_METHOD);
 printf("<div id='leafShapeContainer'%s>\n",
        differentString(hapMethod, VCF_HAP_METHOD_CENTER_WEIGHTED) ? " style='display: none;'" : "");
 printf("<B>%s clustering tree leaf shape:</B><BR>\n", vcfHaplotypeOrSample(cart));
 char *treeAngle = cartOrTdbString(cart, tdb, VCF_HAP_TREEANGLE_VAR, VCF_DEFAULT_HAP_TREEANGLE);
 char varName[1024];
 safef(varName, sizeof(varName), "%s." VCF_HAP_TREEANGLE_VAR, name);
 cgiMakeRadioButton(varName, VCF_HAP_TREEANGLE_TRIANGLE,
 		   sameString(treeAngle, VCF_HAP_TREEANGLE_TRIANGLE));
@@ -313,30 +359,31 @@
     cgiMakeIntVarInRange(varName, cartHeight, "Height (in pixels) of track", 5, "4", "10000");
     puts("<BR>");
     }
 }
 
 static void vcfCfgHapCluster(struct cart *cart, struct trackDb *tdb, struct vcfFile *vcff,
 			     char *name, boolean parentLevel)
 /* Show controls for haplotype-sorting display, which only makes sense to do when
  * the VCF file describes multiple genotypes. */
 {
 char *hapOrSample = vcfHaplotypeOrSample(cart);
 printf("<H3>%s sorting display</H3>\n", hapOrSample);
 vcfCfgHapClusterEnable(cart, tdb, name, parentLevel);
 vcfCfgHaplotypeMethod(cart, tdb, name, parentLevel, vcff);
 vcfCfgHapClusterTreeAngle(cart, tdb, name, parentLevel);
+vcfCfgHapClusterSampleColor(cart, tdb, name, parentLevel);
 vcfCfgHapClusterColor(cart, tdb, name, parentLevel);
 vcfCfgHapClusterHeight(cart, tdb, vcff, name, parentLevel);
 }
 
 static void vcfCfgMinQual(struct cart *cart, struct trackDb *tdb, struct vcfFile *vcff,
 			  char *name, boolean parentLevel)
 /* If checkbox is checked, apply minimum value filter to QUAL column. */
 {
 char cartVar[1024];
 safef(cartVar, sizeof(cartVar), "%s." VCF_APPLY_MIN_QUAL_VAR, name);
 boolean applyFilter = cartOrTdbBoolean(cart, tdb, VCF_APPLY_MIN_QUAL_VAR,
 				       VCF_DEFAULT_APPLY_MIN_QUAL);
 cgiMakeCheckBox(cartVar, applyFilter);
 printf("<B>Exclude variants with Quality/confidence score (QUAL) score less than</B>\n");
 double minQual = cartOrTdbDouble(cart, tdb, VCF_MIN_QUAL_VAR, VCF_DEFAULT_MIN_QUAL);