b409b5a3c5aef834f7604d6da332a1fe864b4e86
kate
  Tue Nov 17 17:02:17 2020 -0800
Clean up code supporting barChartCategoryUrl. As bonus feature, dd support for r,g,b color spec. refs #22075

diff --git src/hg/lib/barChartUi.c src/hg/lib/barChartUi.c
index 3855f22..0244574 100644
--- src/hg/lib/barChartUi.c
+++ src/hg/lib/barChartUi.c
@@ -1,23 +1,24 @@
 /* Bar chart track controls */
 
 /* Copyright (C) 2015 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "cheapcgi.h"
 #include "cart.h"
 #include "net.h"
+#include "errCatch.h"
 #include "hui.h"
 #include "trackDb.h"
 #include "jsHelper.h"
 #include "hCommon.h"
 #include "rainbow.h"
 #include "htmlColor.h"
 #include "barChartCategory.h"
 #include "barChartUi.h"
 
 /* Restrict features on right-click (popup) version */
 static boolean isPopup = FALSE;
 
 /* Convenience functions for category filter controls */
 
 
@@ -148,169 +149,188 @@
 safef(cartVar, sizeof(cartVar), "%s.%s", track, BAR_CHART_LOG_TRANSFORM);
 boolean isLogTransform = cartCgiUsualBoolean(cart, cartVar, BAR_CHART_LOG_TRANSFORM_DEFAULT);
 safef(buf, sizeof buf, "%sViewLimitsMaxLabel %s", track, isLogTransform ? "disabled" : "");
 printf("<span class='%s'><b>View limits maximum:</b></span>\n", buf);
 safef(cartVar, sizeof(cartVar), "%s.%s", track, BAR_CHART_MAX_VIEW_LIMIT);
 int viewMax = cartCgiUsualInt(cart, cartVar, BAR_CHART_MAX_VIEW_LIMIT_DEFAULT);
 cgiMakeIntVarWithExtra(cartVar, viewMax, 4, isLogTransform ? "disabled" : "");
 char *unit = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_UNIT, "");
 printf("<span class='%s'> %s (range 0-%d)</span>\n", buf, unit, 
                                 round(barChartUiMaxMedianScore(tdb)));
 }
 
 // TODO: libify
 static boolean isUrl(char *url)
 {
-return ((startsWith("http://", url)
+return startsWith("http://", url)
    || startsWith("https://", url)
-   || startsWith("ftp://", url)));
+   || startsWith("ftp://", url);
 }
 
-struct barChartCategory *barChartUiGetCategories(char *database, struct trackDb *tdb)
-/* Get category colors and descriptions.  
-   Use labels in tab-sep file specified by barChartCategoryUrl setting, o/w in barChartBars setting.
-   If colors are not specified via barChartColors setting or second column in category file,
-   assign rainbow colors.
- */
+static void getCategsFromSettings(char *track, char *labelSetting, char *colorSetting, 
+                                        struct slName **labels, struct slName **colors)
+/* Get category labels and optionally colors, from track settings */
 {
-int count = 0;
-struct slName *labels = NULL, *colors = NULL;
-char *fileOrUrl = trackDbSetting(tdb, BAR_CHART_CATEGORY_URL);
-if (isEmpty(fileOrUrl))
-    {
-    // use trackDb settings for labels and perhaps colors
-    char *labelSetting = trackDbSetting(tdb, BAR_CHART_CATEGORY_LABELS);
+if (!labels || !colors)
+    return;
 if (isEmpty(labelSetting))
     {
     errAbort("barChart track %s missing required setting: %s or %s\n",
-                        tdb->track, BAR_CHART_CATEGORY_LABELS, BAR_CHART_CATEGORY_URL);
+                    track, BAR_CHART_CATEGORY_LABELS, BAR_CHART_CATEGORY_URL);
     }
 char *words[BAR_CHART_MAX_CATEGORIES];
-    count = chopLine(cloneString(labelSetting), words);
-    labels = slNameListFromStringArray(words, count);
-    char *colorSetting = trackDbSetting(tdb, BAR_CHART_CATEGORY_COLORS);
+int labelCount = chopLine(cloneString(labelSetting), words);
+*labels = slNameListFromStringArray(words, labelCount);
 if (isNotEmpty(colorSetting))
     {
-        int ct = chopLine(cloneString(colorSetting), words);
-        if (ct != count)
+    int colorCount = chopLine(cloneString(colorSetting), words);
+    if (colorCount != labelCount)
         errAbort("barChart track %s settings mismatch: %s (%d) and  %s (%d)\n",
-                        tdb->track, BAR_CHART_CATEGORY_LABELS, count, BAR_CHART_CATEGORY_COLORS, ct);
-        colors = slNameListFromStringArray(words, count);
+            track, BAR_CHART_CATEGORY_LABELS, labelCount, BAR_CHART_CATEGORY_COLORS, colorCount);
+    *colors = slNameListFromStringArray(words, labelCount);
     }
 }
-else
+
+static void getCategsFromFile(char *track, char *categUrl,
+                                        struct slName **labels, struct slName **colors)
+/* Get category labels and optionally colors, from category file.
+ * This is tab-sep file, column 1 is category label, optional column 2 is a color spec */
 {
-    // use file for labels and perhaps colors
+if (!labels || !colors) return;
 struct lineFile *lf = NULL;
 
-#ifdef TODO
-// TODO: protect against network error
+// protect against network error
 struct errCatch *errCatch = errCatchNew();
 if (errCatchStart(errCatch))
     {
-    if (isUrl(fileOrUrl))
-        {
-        lf = netLineFileOpen(fileOrUrl);
+    if (isUrl(categUrl))
+        lf = netLineFileOpen(categUrl);
     else
-        lf = lineFileMayOpen(fileOrUrl, TRUE);
-        }
+        lf = lineFileMayOpen(categUrl, TRUE);
     }
 errCatchEnd(errCatch); 
 if (errCatch->gotError)
     {
     if (isNotEmpty(errCatch->message->string))
-        warn("unable to open %s: %s", fileOrUrl, errCatch->message->string);
+        warn("unable to open %s track file %s: %s", 
+                    track, categUrl, errCatch->message->string);
     }
 errCatchFree(&errCatch);
-#endif
-
-    if (isUrl(fileOrUrl))
-        lf = netLineFileOpen(fileOrUrl);
-    else
-        lf = lineFileMayOpen(fileOrUrl, TRUE);
 char *line = NULL;
 int cols = 0;
 while (lineFileNextReal(lf, &line))
     {
     char *words[2];
     int wordCount = chopTabs(line, words);
     if (cols)
         {
         if (wordCount != cols)
             errAbort("barChart track %s category file %s expecting %d words, got %d",
-                            tdb->track, fileOrUrl, cols, wordCount);
+                        track, categUrl, cols, wordCount);
         }
     else
         {
         cols = wordCount;
         }
     slAddHead(&labels, slNameNew(words[0]));
     if (wordCount == 2)
         slAddHead(&colors, slNameNew(words[1]));
-        count++;
     }
 slReverse(&labels);
 slReverse(&colors);
 }
 
-// populate categories
+static struct barChartCategory *createCategs(char *track, 
+                                                struct slName *labels, struct slName *colors)
+/* Populate category structs from label and color lists.  Assign rainbow if no color list */
+{
 struct barChartCategory *categs = NULL, *categ = NULL;
+int count = slCount(labels);
 struct rgbColor *rainbow = NULL;
 if (!colors)
     {
     rainbow = getRainbow(&saturatedRainbowAtPos, count);
     }
 int i;
 char buf[6];
 for (i=0 ; i<count && labels; i++)
     {
     AllocVar(categ);
     categ->id = i;
     safef(buf, sizeof buf, "%d", i);
     categ->name = cloneString(buf);
     categ->label = labels->name;
-    if (colors)
+    if (!colors)
+        {
+        // rainbow
+        categ->color = ((rainbow[i].r & 0xff)<<16) + 
+                    ((rainbow[i].g & 0xff)<<8) + 
+                    ((rainbow[i].b & 0xff));
+        }
+    else
         {
-        unsigned rgb;
+        // colors from user
+        unsigned rgb = 0;
         char *color = colors->name;
-        if (htmlColorForCode(color, &rgb))
+        if (!htmlColorForCode(color, &rgb))
             {
-            categ->color = rgb;
-            }
-        else if (htmlColorForName(color, &rgb))
+            if (!htmlColorForName(color, &rgb))
                 {
-            categ->color = rgb;
-            }
-        else
-            warn("barChart track %s unknown color %s. Must be one of %s\n",
-                    tdb->track, color, slNameListToString(htmlColorNames(),','));
+                /* try r,g,b */
+                if (index(color, ','))
+                    {
+                    unsigned char r, g, b;
+                    parseColor(color, &r, &g, &b);
+                    htmlColorFromRGB(&rgb, r, g, b);
                     }
                 else
                     {
-        categ->color = ((rainbow[i].r & 0xff)<<16) + 
-                    ((rainbow[i].g & 0xff)<<8) + 
-                    ((rainbow[i].b & 0xff));
+                    warn("barChart track %s unknown color %s. Must r,g,b or #ffffff or one of %s\n",
+                            track, color, slNameListToString(htmlColorNames(),','));
+                    }
+                }
+            }
+        categ->color = rgb;
         }
     slAddHead(&categs, categ);
     labels = labels->next;
     if (colors)
         colors = colors->next;
     }
 slReverse(&categs);
 return categs;
 }
 
+struct barChartCategory *barChartUiGetCategories(char *database, struct trackDb *tdb)
+/* Get category colors and descriptive labels.
+   Use labels in tab-sep file specified by barChartCategoryUrl setting, o/w in barChartBars setting.
+   If colors are not specified via barChartColors setting or second column in category file,
+   assign rainbow colors.  Colors are specified as #fffff or r,g,b  or html color name) */
+{
+struct slName *labels = NULL, *colors = NULL;
+char *categUrl = trackDbSetting(tdb, BAR_CHART_CATEGORY_URL);
+if (isNotEmpty(categUrl))
+    getCategsFromFile(tdb->track, categUrl, &labels, &colors);
+else
+    {
+    char *labelSetting = trackDbSetting(tdb, BAR_CHART_CATEGORY_LABELS);
+    char *colorSetting = trackDbSetting(tdb, BAR_CHART_CATEGORY_COLORS);
+    getCategsFromSettings(tdb->track, labelSetting, colorSetting, &labels, &colors);
+    }
+return createCategs(tdb->track, labels, colors);
+}
+
 struct barChartCategory *barChartUiGetCategoryById(int id, char *database, 
                                                         struct trackDb *tdb)
 /* Get category info by id */
 {
 struct barChartCategory *categ;
 struct barChartCategory *categs = barChartUiGetCategories(database, tdb);
 for (categ = categs; categ != NULL; categ = categ->next)
     if (categ->id == id)
         return categ;
 return NULL;
 }
 
 char *barChartUiGetCategoryLabelById(int id, char *database, struct trackDb *tdb)
 /* Get label for a category id */
 {