a08ccd7dd4930bff5bcf0a32bd35ec22db71215f
markd
  Sat Aug 22 06:14:41 2026 -0700
Fix the browser.theme drop down, which has never applied a theme, and retire browser.background and browser.bgcolor from the example configs. refs #38158

themeDropDown built the menu label from the hg.conf key and posted that label
back, while setThemeFromCart looks a theme up by key, so no entry carrying a sort
prefix or an underscore could ever resolve.  Broken since af6898753ba (2023)
introduced that key format and changed the menu side without the lookup side.
themeDropDown now passes the key suffix as the option value and the prettified
label as the display text, using cgiMakeDropListWithVals.

setThemeFromCart compared the generated link against "<>" instead of the config
value, so the "no theme file" marker never fired.  It also let an empty link from
a missing CSS file overwrite browser.style with nothing.  Both now test the right
string and leave browser.style alone when there is no theme to apply.

browser.background has not been read by any CGI since hBackgroundImage() and its
callers were removed in 2012 (8e6fcda9f58), and browser.bgcolor has never been
read at all, yet ex.hg.conf presented browser.background as a working setting.
Commented both out with a note, in ex.hg.conf and the three browserbox configs.

ex.hg.conf also documented two conflicting browser.theme formats in two places.
The comma form stopped working in 2023.  Consolidated to a single block that
describes what the code actually does.

diff --git src/hg/hgTracks/config.c src/hg/hgTracks/config.c
index 407cddd2ae0..3320a3d5549 100644
--- src/hg/hgTracks/config.c
+++ src/hg/hgTracks/config.c
@@ -11,62 +11,74 @@
 #include "hCommon.h"
 #include "cart.h"
 #include "web.h"
 #include "customTrack.h"
 #include "hgTracks.h"
 #include "trashDir.h"
 #include "hgConfig.h"
 #include "jsHelper.h"
 #include "imageV2.h"
 #include "search.h"
 #include "hubConnect.h"
 #include "fileUi.h"
 #include "trackHub.h"
 #include "versionInfo.h"
 
+static void themeMenuEntry(char *cfgName, char **retLabel, char **retValue)
+/* Split an hg.conf browser.theme.* key into the menu label and the option value.
+ * The value is everything after "browser.theme.", which is what setThemeFromCart
+ * looks the theme up by, so it must survive the round trip through the form
+ * untouched.  The label is only the part after the last '.', with underscores as
+ * spaces, so a sort prefix like browser.theme.2.Sans_Serif shows as "Sans Serif".
+ * The first letter is upper cased, which the plain hDropList used to do for us. */
+{
+char *value = cloneString(cfgName + strlen("browser.theme."));
+char *label = cloneString(findTail(value, '.'));
+replaceChar(label, '_', ' ');
+label[0] = toupper((unsigned char)label[0]);
+*retLabel = label;
+*retValue = value;
+}
+
 static void themeDropDown(struct cart* cart)
 /* Create drop down for UI themes.
- * specfied in hg.conf like this
- * browser.theme.modern=background.png,HGStyle
+ * specified in hg.conf like this
+ * browser.theme.modern=theme-modern.css
+ * optionally with a sort prefix
+ * browser.theme.3.Sans_Serif=theme-modern.css
  * */
 {
 struct slName* themes = cfgNamesWithPrefix("browser.theme.");
 if (themes==NULL)
     return;
 
 slNameSort(&themes);
 hPrintf("<TR><TD>website style:");
 hPrintf("<TD style=\"text-align: right\">");
 
-// create labels for drop down box by removing prefix from hg.conf keys
 char *labels[50];
+char *values[50];
 struct slName* el;
 int i = 0;
-el = themes;
 for (el = themes; el != NULL && i<50; el = el->next)
     {
-    char* name = el->name;
-    name = chopPrefix(name); // chop off first three words
-    name = chopPrefix(name);
-    name = chopPrefix(name);
-    replaceChar(name, '_', ' ');
-    labels[i] = name;
+    themeMenuEntry(el->name, &labels[i], &values[i]);
     i++;
     }
 
 char* currentTheme = cartOptionalString(cart, "theme"); 
-hDropList("theme", labels, i, currentTheme);
+cgiMakeDropListWithVals("theme", labels, values, i, currentTheme);
 slFreeList(themes);
 hPrintf("</TD>");
 }
 
 /* HOW TO ADD A NEW FONT
  *
  * The browser draws track text with either the old bitmap engine or, when
  * freeType is on (hg.conf "freeType=on", the default in a FreeType build), the
  * FreeType engine.  The fonts the FreeType engine offers are the freeTypeFonts[]
  * table below.  To add one:
  *
  *   1. Put the font file where the engine can find it.  At run time the file is
  *      looked up under freeTypeDir, which defaults to "../htdocs/urw-fonts".
  *      That path is relative to the CGI's working directory, so it resolves to
  *      the *shared* htdocs/urw-fonts even from a per-user sandbox -- you do not