cfeb4d454d22011d7f637d060e7a51057d168504
braney
  Mon Mar 30 12:01:16 2026 -0700
Add color picker support for most track types, refs #20460

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index a20b7954ae4..baa53636309 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -6030,35 +6030,41 @@
 safef(varName, sizeof(varName), "%s.nameFilter", name);
 
 char *onlyTransStr = cartUsualString(cart, varName, "");
 
 cgiMakeTextVar(varName, onlyTransStr, 60);
 printInfoIcon("Enter the primary accession of the track, so RefSeq IDs for the RefSeq track, Gencode IDs for the Gencode track, etc. Separate multiple accessions with commas.");
 puts("</DIV>\n\n");
 }
 
 void colorTrackOption(struct cart *cart, char *name, struct trackDb *tdb)
 /* color picker for overriding track color */
 {
 char varName[1024];
 safef(varName, sizeof(varName), "%s.colorOverride", name);
 
-char *colorValue = cartUsualString(cart, varName, "");
+char defaultColor[16];
+safef(defaultColor, sizeof(defaultColor), "#%02x%02x%02x", tdb->colorR, tdb->colorG, tdb->colorB);
+
+char *rawCartValue = cartOptionalString(cart, varName);
+boolean hasOverride = (rawCartValue != NULL && rawCartValue[0] != '\0');
+char *colorValue = hasOverride ? rawCartValue : defaultColor;
+boolean hasItemRgb = !trackDbSettingOff(tdb, "itemRgb");
 
 printf("&nbsp;<div id='colorPicker_%s'>", name);
-jsInlineF("makeHighlightPicker('%s', document.getElementById('colorPicker_%s'), '%s', '<b>Change track color: </b>&nbsp;', '%s');",
-        varName, name, name, colorValue); // id="xx" is necessary as id contains a dot
+jsInlineF("makeHighlightPicker('%s', document.getElementById('colorPicker_%s'), '%s', '<b>Change track color: </b>&nbsp;', '%s', '%s', %s, %s);",
+        varName, name, name, colorValue, defaultColor, hasItemRgb ? "true" : "false", hasOverride ? "true" : "false");
 puts("</div>\n\n");
 }
 
 void wiggleScaleDropDownJavascript(char *name)
 /* print some js that deactivates the min/max range if autoscaling is activated */
 {
 struct dyString *dy = dyStringNew(1024);
 dyStringPrintf(dy, "  $(\"[name='%s.autoScale']\").change(function()\n", name);
 dyStringPrintf(dy, "  {\n");
 dyStringPrintf(dy, "  val= $(this).find(':selected').val(); \n");
 dyStringPrintf(dy, "  if (val!=\"use vertical viewing range setting\")\n");
 dyStringPrintf(dy, "     {\n");
 dyStringPrintf(dy, "     $(\"[name='%s.minY']\")[0].disabled=true;\n", name);
 dyStringPrintf(dy, "     $(\"[name='%s.maxY']\")[0].disabled=true;\n", name);
 dyStringPrintf(dy, "     $(\".%sAutoScaleDesc\").attr('style', 'color:grey;');\n", name);
@@ -7882,31 +7888,30 @@
     {
     printf("<BR>");
     filterBySetCfgUi(cart,tdb,filterBySet,FALSE, name);
     filterBySetFree(&filterBySet);
     }
 filterBy_t *highlightBySet = highlightBySetGet(tdb,cart,name);
 if (highlightBySet != NULL)
     {
     printf("<BR>");
     highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE, name, TRUE);
     filterBySetFree(&highlightBySet);
     }
 
 squishyPackOption(cart, name, title, tdb);
 filterNameOption(cart, name, tdb);
-colorTrackOption(cart, name, tdb);
 wigOption(cart, name, title, tdb);
 cfgEndBox(boxed);
 // N.B. scoreCfgUi maybe creates a box, so this is called after cfgEndBox
 // unclear what the logic is with box creation here
 if (startsWith("bigGenePred", tdb->type))
     {
     char *scoreMax = trackDbSettingClosestToHome(tdb, SCORE_FILTER _MAX);
     int maxScore = (scoreMax ? sqlUnsigned(scoreMax):1000);
     scoreCfgUi(db, cart,tdb,name,title,maxScore,boxed);
     }
 }
 
 static boolean isSpeciesOn(struct cart *cart, struct trackDb *tdb, char *species, char *option, int optionSize, boolean defaultState)
 /* check the cart to see if species is turned off or on (default is defaultState) */
 {