0d1ffc7e1c162f115ffa1622a70535b99c465ee0
kate
  Tue Jan 20 18:12:40 2015 -0800
Add inputFieldMetaTables setting to trackDb.  It provides a way to add meta tables to non-composites (same functionality as subGroupMetaTables setting).  Used in details of DNase clusters, to replace cv.ra and metaDb.  refs #14353
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index e4f8e89..2fc793e 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -4174,76 +4174,97 @@
     else
         printf("<TD colspan=%d>&nbsp;&nbsp;&nbsp;&nbsp;",columnCount);
 
     // Count of subtracks is filled in by javascript.
     if (subCount > 5)
         printf("<span class='subCBcount'></span>\n");
 
     // Restriction policy needs a link
     if (restrictions && sortOrder != NULL)
         printf("</TD><TH><A HREF='%s' TARGET=BLANK style='font-size:.9em;'>Restriction Policy</A>",
                ENCODE_DATA_RELEASE_POLICY);
 
     printf("</TD></TR></TFOOT>\n");
     }
 }
+
 /********************/
-/* Metadata for subgroups */
+/* Basic metadata for subgroups and input fields */
 
-struct meta {
+struct metaBasic {
+    struct metaBasic *next;
     char *term;
     char *description;
     char *url;
 };
 
-static struct hash *metaForAllSubGroupsGet(struct trackDb *parentTdb, struct cart *cart)
-/* Get description and URL for all subgroupMetaTables. Returns a hash of hashes */
+char *metaVocabLink(struct hash *metaFieldHash, char *term, char *title)
+/* Make an anchor with mouseover containing description and link if present */
+{   
+struct metaBasic *meta = hashFindVal(metaFieldHash, term);
+if (meta == NULL)
+    return NULL;
+struct dyString *ds = dyStringNew(0);
+if (meta->url == NULL || strlen(meta->url) == 0)
+    dyStringPrintf(ds, "<A title='%s' style='cursor: pointer;'>%s</A>",
+                        meta->description, term);
+else
+    dyStringPrintf(ds, "<A target='_blank' class='cv' title='%s' href='%s'>%s</A>\n",
+                        meta->description, meta->url, term);
+return dyStringCannibalize(&ds);
+}
+
+struct hash *metaBasicFromSetting(struct trackDb *parentTdb, struct cart *cart, char *setting)
+/* Get description and URL for all metaTables. Returns a hash of hashes */
 {
-char *setting = trackDbSetting(parentTdb, "subGroupMetaTables");
-if (!setting)
+if (differentString(setting, "subGroupMetaTables") &&
+    differentString(setting, "inputFieldMetaTables"))
         return NULL;
-struct slPair *metaTables = slPairFromString(setting);
+char *spec = trackDbSetting(parentTdb, setting);
+if (!spec)
+    return NULL;
+struct slPair *metaTables = slPairFromString(spec);
 struct hash *tableHash = hashNew(0);
 struct slPair *metaTable;
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *database = cartString(cart, "db");
 char *db = database;
 for (metaTable = metaTables; metaTable != NULL; metaTable = metaTables->next)
     {
     char *tableName = chopPrefix(cloneString(metaTable->val));
     if (differentString(tableName, metaTable->val))
         {
         chopSuffix(metaTable->val);
         db = metaTable->val;
         }
     struct sqlConnection *conn = hAllocConn(db);
     boolean hasUrl = FALSE;
     struct hash *subgroupHash = hashNew(0);
     hashAdd(tableHash, metaTable->name, subgroupHash);
     if (hHasField(db, tableName, "url"))
         {
         sqlSafef(query, sizeof(query), "select term, description, url from %s", tableName);
         hasUrl = TRUE;
         }
     else
         sqlSafef(query, sizeof(query), "select term, description from %s", tableName);
     sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
         {
-        struct meta *meta = NULL;
+        struct metaBasic *meta = NULL;
         AllocVar(meta);
         meta->term = cloneString(row[0]);
         meta->description = cloneString(row[1]);
         if (hasUrl)
             meta->url = cloneString(row[2]);
         hashAdd(subgroupHash, meta->term, meta);
         }
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     }
 return tableHash;
 }
 
 static void printSubtrackTableBody(struct trackDb *parentTdb, struct slRef *subtrackRefList,
                                     struct subtrackConfigSettings *settings, struct cart *cart)
@@ -4263,31 +4284,31 @@
     {
     // preserves user's prev sort/drags
     preSorted = tdbRefSortPrioritiesFromCart(cart, &subtrackRefList);
     printf("<TBODY class='%saltColors'>\n", (sortOrder != NULL ? "sortable " : "") );
     }
 else
     {
     slSort(&subtrackRefList, trackDbRefCmp);  // straight from trackDb.ra
     preSorted = TRUE;
     puts("<TBODY>");
     }
 
 // Finally the big "for loop" to list each subtrack as a table row.
 printf("\n<!-- ----- subtracks list ----- -->\n");
 membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,NULL);
-struct hash *subgroupMetaHash = metaForAllSubGroupsGet(parentTdb, cart);
+struct hash *subgroupMetaHash = metaBasicFromSetting(parentTdb, cart, "subGroupMetaTables");
 struct slRef *subtrackRef;
 
 /* Color handling ?? */
 //char *colors[2]   = { COLOR_BG_DEFAULT,
 //                      COLOR_BG_ALTDEFAULT };
 char *colors[2]   = { "bgLevel1",
                       "bgLevel1" };
 int colorIx = settings->bgColorIx;
 
 for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
     {
     struct trackDb *subtrack = subtrackRef->val;
     int ix;
 
     // Determine whether subtrack is checked, visible, configurable, has group membership, etc.
@@ -4444,52 +4465,38 @@
             ix = stringArrayIx(col, membership->subgroups, membership->count);
                                 // TODO: Sort needs to expand from subGroups to labels as well
             if (ix >= 0)
                 {
                 char *term = membership->membership[ix];
                 char *title = membership->titles[ix];
                 char *titleRoot=NULL;
                 if (cvTermIsEmpty(col, title))
                     titleRoot = cloneString(" &nbsp;");
                 else
                     titleRoot = labelRoot(title, NULL);
                 // Each sortable column requires hidden goop (in the "abbr" field currently)
                 // which is the actual sort on value
                 printf("<TD id='%s_%s' abbr='%s' align='left'>", subtrack->track, col, term);
                 printf("&nbsp");
-                boolean printed = FALSE;
+                char *link = NULL;
                 if (subgroupMetaHash)
                     {
                     struct hash *colHash = hashFindVal(subgroupMetaHash, col);
                     if (colHash)
-                        {
-                        struct meta *meta = hashFindVal(colHash, term);
-                        if (meta != NULL)
-                            {
-                            if (meta->url == NULL || strlen(meta->url) == 0)
-                                /* add pointer to suggest hover */
-                                printf("<A title='%s' style='cursor: pointer;'>%s</A>", 
-                                            meta->description, titleRoot);
-                            else
-                                printf("<A target='_blank' class='cv' title='%s' href='%s'>%s</A>\n", 
-                                        meta->description, meta->url, titleRoot);
-                            printed = TRUE;
-                            }
-                        }
+                        link = metaVocabLink(colHash, term, titleRoot);
                     }
-                if (!printed)
-                    printf("%s",titleRoot);
+                printf("%s", link ? link : titleRoot);
                 puts("</TD>");
                 freeMem(titleRoot);
                 }
             else if (sameString(col, SUBTRACK_COLOR_SUBGROUP))
                 {
                 char *hue = subtrackColorToCompare(subtrack);
                 printf("<TD id='%s_%s' abbr='%s' bgcolor='#%02X%02X%02X'>"
                         "&nbsp;&nbsp;&nbsp;&nbsp;</TD>",
                     subtrack->track, col, hue, 
                         subtrack->colorR, subtrack->colorG, subtrack->colorB);
                 }
             }
         }
     else  // Non-sortable tables do not have sort by columns but will display a short label
         { // (which may be a configurable link)