58a3bf5fbc986db7c8b17589c9720b4bc6708133
tdreszer
  Tue Mar 8 16:44:22 2011 -0800
Moved a few functions common to track search and file search into library code.  Redesigned a few things to standardize on slPair lists of vals and labels.
diff --git src/hg/hgTracks/searchTracks.c src/hg/hgTracks/searchTracks.c
index 80a786a..b45bb32 100644
--- src/hg/hgTracks/searchTracks.c
+++ src/hg/hgTracks/searchTracks.c
@@ -10,58 +10,45 @@
 #include "cheapcgi.h"
 #include "hPrint.h"
 #include "htmshell.h"
 #include "cart.h"
 #include "hgTracks.h"
 #include "web.h"
 #include "jksql.h"
 #include "hdb.h"
 #include "mdb.h"
 #include "fileUi.h"
 #include "trix.h"
 #include "jsHelper.h"
 #include "imageV2.h"
 
 
-#define ANYLABEL                 "Any"
 #define TRACK_SEARCH_FORM        "trackSearch"
 #define SEARCH_RESULTS_FORM      "searchResults"
-#define METADATA_NAME_PREFIX     "hgt_mdbVar"
-#define METADATA_VALUE_PREFIX    "hgt_mdbVal"
 #define TRACK_SEARCH_CURRENT_TAB "tsCurTab"
 #define TRACK_SEARCH_SIMPLE      "tsSimple"
 #define TRACK_SEARCH_ON_NAME     "tsName"
 #define TRACK_SEARCH_ON_TYPE     "tsType"
 #define TRACK_SEARCH_ON_GROUP    "tsGroup"
 #define TRACK_SEARCH_ON_DESCR    "tsDescr"
 #define TRACK_SEARCH_SORT        "tsSort"
 
-// If there are problems with multiSelect support, it can quickly be blocked!
-//#define BLOCK_MULTI_SELECT_SUPPORT
-
 //#define FILES_SEARCH
 #ifdef FILES_SEARCH
-    #define TRACK_SEARCH_ON_FILETYPE "tsFileType"
+    #define FILE_SEARCH_ON_FILETYPE "tsFileType"
 #endif///def FILES_SEARCH
 
-// Currently selected tab
-enum searchTab {
-    simpleTab   = 0,
-    advancedTab = 1,
-    filesTab    = 2,
-};
-
 static int gCmpGroup(const void *va, const void *vb)
 /* Compare groups based on label. */
 {
 const struct group *a = *((struct group **)va);
 const struct group *b = *((struct group **)vb);
 return strcmp(a->label, b->label);
 }
 
 // Would like to do a radio button choice ofsorts
 enum sortBy
     {
     sbRelevance=0,
     sbAbc      =1,
     sbHierarchy=2,
     };
@@ -149,56 +136,30 @@
                     break;
                     }
                 }
             if(found)
                 numMatches++;
             else
                 break;
             }
         if(numMatches == wordCount)
             return TRUE;
         }
     }
 return FALSE;
 }
 
-static int getTermArray(struct sqlConnection *conn, char ***pLabels, char ***pTerms, char *type)
-// Pull out all term fields from ra entries with given type
-// Returns count of items found and items via the terms argument.
-{
-int ix = 0, count = 0;
-char **labels;
-char **values;
-struct slPair *pairs = mdbValLabelSearch(conn, type, MDB_VAL_STD_TRUNCATION, TRUE, FALSE); // Tables not files
-count = slCount(pairs) + 1; // make room for "Any"
-AllocArray(labels, count);
-AllocArray(values, count);
-labels[ix] = cloneString(ANYLABEL);
-values[ix] = cloneString(ANYLABEL);
-struct slPair *pair = NULL;
-while((pair = slPopHead(&pairs)) != NULL)
-    {
-    ix++;
-    labels[ix] = pair->name;
-    values[ix] = pair->val;
-    freeMem(pair);
-    }
-*pLabels = labels;
-*pTerms = values;
-return count;
-}
-
 static int getFormatTypes(char ***pLabels, char ***pTypes)
 {
 char *crudeTypes[] = {
     ANYLABEL,
     "bam",
     "psl",
     "chain",
     "netAlign",
     "maf",
     "bed",
     "bigBed",
     "ctgPos",
     "expRatio",
     "genePred",
     "broadPeak",
@@ -249,291 +210,30 @@
 int ix = 0, count = sizeof(crudeTypes)/sizeof(char *);
 char **labels;
 char **values;
 AllocArray(labels, count);
 AllocArray(values, count);
 for(ix=0;ix<count;ix++)
     {
     labels[ix] = cloneString(nicerTypes[ix]);
     values[ix] = cloneString(crudeTypes[ix]);
     }
 *pLabels = labels;
 *pTypes = values;
 return count;
 }
 
-#ifdef FILES_SEARCH
-static int getFileFormatTypes(char ***pLabels, char ***pTypes)
-{
-char *crudeTypes[] = {
-    ANYLABEL,
-    "bam",
-    "bam.bai",
-    "tagAlign",
-    "bed.gz",
-    "bigBed",
-    "broadPeak",
-    "narrowPeak",
-    "fastq",
-    "bigWig",
-    "wig"
-};
-char *nicerTypes[] = {
-    ANYLABEL,
-    "Alignment binary (bam) - binary SAM",
-    "Alignment binary index (bai) - binary SAM index",
-    "Alignment tags (tagAlign)",
-    "bed - browser extensible data",
-    "bigBed - self index, often remote bed format",
-    "Peaks Broad (broadPeak) - ENCODE large region peak format",
-    "Peaks Narrow (narrowPeak) - ENCODE small region peak format",
-    "Raw Sequence (fastq) - High throughput sequence format",
-    "Signal (bigWig) - self index, often remote wiggle format",
-    "Signal (wig) - wiggle format"
-};
-
-int ix = 0, count = sizeof(crudeTypes)/sizeof(char *);
-char **labels;
-char **values;
-AllocArray(labels, count);
-AllocArray(values, count);
-for(ix=0;ix<count;ix++)
-    {
-    labels[ix] = cloneString(nicerTypes[ix]);
-    values[ix] = cloneString(crudeTypes[ix]);
-    }
-*pLabels = labels;
-*pTypes = values;
-return count;
-}
-#endif///def FILES_SEARCH
-
-static int metaDbVars(struct sqlConnection *conn, char *** metaVars, char *** metaLabels)
-// Search the assemblies metaDb table; If name == NULL, we search every metadata field.
-{
-char query[256];
-struct slPair *oneTerm,*whiteList = mdbCvWhiteList(TRUE,FALSE);
-int count =0, whiteCount = slCount(whiteList);
-char **retVar = needMem(sizeof(char *) * whiteCount);
-char **retLab = needMem(sizeof(char *) * whiteCount);
-
-for(oneTerm=whiteList;oneTerm!=NULL;oneTerm=oneTerm->next)
-    {
-    safef(query, sizeof(query), "select count(*) from metaDb where var = '%s'",oneTerm->name);
-    if(sqlQuickNum(conn,query) > 0)
-        {
-        retVar[count] = oneTerm->name;
-        retLab[count] = oneTerm->val;
-        count++;
-        }
-    }
-// Don't do it, unless you clone strings above:  slPairFreeValsAndList(&whileList);
-
-*metaVars = retVar;
-*metaLabels = retLab;
-return count;
-}
-
-static int printMdbSelects(struct sqlConnection *conn,struct cart *cart,enum searchTab selectedTab,char ***pMdbVar,char ***pMdbVal,int *numMetadataNonEmpty,int cols)
-// Prints a table of mdb selects if appropriate and returns number of them
-// TODO: move to lib since hgTracks and hgFileSearch share it
-{
-// figure out how many metadata selects are visible.
-int delSearchSelect = cartUsualInt(cart, TRACK_SEARCH_DEL_ROW, 0);   // 1-based row to delete
-int addSearchSelect = cartUsualInt(cart, TRACK_SEARCH_ADD_ROW, 0);   // 1-based row to insert after
-int numMetadataSelects = 0;
-char **mdbVar = NULL;
-char **mdbVal = NULL;
-char **mdbVars = NULL;
-char **mdbVarLabels = NULL;
-int i, count = metaDbVars(conn, &mdbVars, &mdbVarLabels);
-
-for(;;)
-    {
-    char buf[256];
-    safef(buf, sizeof(buf), "%s%d", METADATA_NAME_PREFIX, numMetadataSelects + 1);
-    char *str = cartOptionalString(cart, buf);
-    if(isEmpty(str))
-        break;
-    else
-        numMetadataSelects++;
-    }
-
-if(delSearchSelect)
-    numMetadataSelects--;
-if(addSearchSelect)
-    numMetadataSelects++;
-
-if(numMetadataSelects)
-    {
-    mdbVar = needMem(sizeof(char *) * numMetadataSelects);
-    mdbVal = needMem(sizeof(char *) * numMetadataSelects);
-    *pMdbVar = mdbVar;
-    *pMdbVal = mdbVal;
-    int i;
-    for(i = 0; i < numMetadataSelects; i++)
-        {
-        char buf[256];
-        int offset;   // used to handle additions/deletions
-        if(addSearchSelect > 0 && i >= addSearchSelect)
-            offset = 0; // do nothing to offset (i.e. copy data from previous row)
-        else if(delSearchSelect > 0 && i + 1 >= delSearchSelect)
-            offset = 2;
-        else
-            offset = 1;
-        safef(buf, sizeof(buf), "%s%d", METADATA_NAME_PREFIX, i + offset);
-        mdbVar[i] = cloneString(cartOptionalString(cart, buf));
-        if(selectedTab!=simpleTab)
-            {
-            int j;
-            boolean found = FALSE;
-            // We need to make sure mdbVar[i] is valid in this assembly; if it isn't, reset it to "cell".
-            for(j = 0; j < count && !found; j++)
-                if(sameString(mdbVars[j], mdbVar[i]))
-                    found = TRUE;
-            if(found)
-                {
-                safef(buf, sizeof(buf), "%s%d", METADATA_VALUE_PREFIX, i + offset);
-                enum mdbCvSearchable searchBy = mdbCvSearchMethod(mdbVar[i]);
-#ifdef BLOCK_MULTI_SELECT_SUPPORT
-                if (searchBy == cvsSearchByMultiSelect)  // NOTE: Temprorarily bypass cv.ra
-                    searchBy =  cvsSearchBySingleSelect;
-#endif///def BLOCK_MULTI_SELECT_SUPPORT
-                if (searchBy == cvsSearchByMultiSelect)
-                    {
-                    // Multi-selects as comma delimited list of values
-                    struct slName *vals = cartOptionalSlNameList(cart,buf);
-                    if (vals)
-                        {
-                        mdbVal[i] = slNameListToString(vals,','); // A comma delimited list of values
-                        slNameFreeList(&vals);
-                        }
-                    }
-                else
-                    mdbVal[i] = cloneString(cartUsualString(cart, buf,ANYLABEL));
-
-                if (mdbVal[i] != NULL && sameString(mdbVal[i], ANYLABEL))
-                    mdbVal[i] = NULL;
-                }
-            else
-                {
-                mdbVar[i] = cloneString("cell");
-                mdbVal[i] = NULL;
-                }
-            if(!isEmpty(mdbVal[i]))
-                (*numMetadataNonEmpty)++;
-            }
-        }
-    if(delSearchSelect > 0)
-        {
-        char buf[255];
-        safef(buf, sizeof(buf), "%s%d", METADATA_NAME_PREFIX, numMetadataSelects + 1);
-        cartRemove(cart, buf);
-        safef(buf, sizeof(buf), "%s%d", METADATA_VALUE_PREFIX, numMetadataSelects + 1);
-        cartRemove(cart, buf);
-        }
-    }
-else
-    {
-    // create defaults
-    numMetadataSelects = 2;
-    mdbVar = needMem(sizeof(char *) * numMetadataSelects);
-    mdbVal = needMem(sizeof(char *) * numMetadataSelects);
-    mdbVar[0] = "cell";
-    mdbVar[1] = "antibody";
-    mdbVal[0] = ANYLABEL;
-    mdbVal[1] = ANYLABEL;
-    }
-
-    hPrintf("<tr><td colspan='%d' align='right' class='lineOnTop' style='height:20px; max-height:20px;'><em style='color:%s; width:200px;'>ENCODE terms</em></td></tr>\n", cols,COLOR_DARKGREY);
-    for(i = 0; i < numMetadataSelects; i++)
-        {
-        char **terms = NULL, **labels = NULL;
-        char buf[256];
-        int len;
-
-    #define PLUS_MINUS_BUTTON "<input type='button' id='%sButton%d' value='%c' style='font-size:.7em;' title='%s' onclick='findTracksMdbSelectPlusMinus(this,%d)'>"
-    #define PRINT_PM_BUTTON(type,num,value) printf(PLUS_MINUS_BUTTON, (type), (num), (value), ((value) == '+' ? "add another row after":"delete"), (num))
-        hPrintf("<tr valign='top' class='mdbSelect'><td nowrap>\n");
-        if(numMetadataSelects > 2 || i >= 2)
-            PRINT_PM_BUTTON("minus", i + 1, '-');
-        else
-            hPrintf("&nbsp;");
-        PRINT_PM_BUTTON("plus", i + 1, '+');
-
-        hPrintf("</td><td>and&nbsp;</td><td colspan=3 nowrap>\n");
-        safef(buf, sizeof(buf), "%s%i", METADATA_NAME_PREFIX, i + 1);
-        enum mdbCvSearchable searchBy = mdbCvSearchMethod(mdbVar[i]);
-    #ifdef BLOCK_MULTI_SELECT_SUPPORT
-        if (searchBy == cvsSearchByMultiSelect)  // NOTE: Temprorarily bypass cv.ra
-            searchBy =  cvsSearchBySingleSelect;
-    #else///ndef BLOCK_MULTI_SELECT_SUPPORT
-        cgiDropDownWithTextValsAndExtra(buf, mdbVarLabels, mdbVars,count,mdbVar[i],"class='mdbVar' style='font-size:.9em;' onchange='findTracksMdbVarChanged(this);'");
-        safef(buf, sizeof(buf), "%s%i", METADATA_VALUE_PREFIX, i + 1);
-    #endif///ndef BLOCK_MULTI_SELECT_SUPPORT
-        if (searchBy == cvsSearchByMultiSelect)
-            {
-        #ifdef BLOCK_MULTI_SELECT_SUPPORT
-            cgiDropDownWithTextValsAndExtra(buf, mdbVarLabels, mdbVars,count,mdbVar[i],"class='mdbVar' style='font-size:.9em;' onchange='findTracksMdbVarChanged(this);'");
-            safef(buf, sizeof(buf), "%s%i", METADATA_VALUE_PREFIX, i + 1);
-        #endif///ndef BLOCK_MULTI_SELECT_SUPPORT
-            printf("</td>\n<td align='right' id='isLike%i' style='width:10px; white-space:nowrap;'>is among</td>\n<td nowrap id='%s' style='max-width:600px;'>\n",i + 1,buf);
-            #define MULTI_SELECT_CBS_FORMAT "<SELECT MULTIPLE=true name='%s' style='display: none; min-width:200px; font-size:.9em;' class='filterBy mdbVal' onchange='findTracksMdbValChanged(this)'>\n"
-            printf(MULTI_SELECT_CBS_FORMAT,buf);
-            len = getTermArray(conn, &labels, &terms, mdbVar[i]);
-            int tix=0;
-            for(;tix < len;tix++)
-                {
-                char *selected = findWordByDelimiter(terms[tix],',', mdbVal[i]);
-                printf("<OPTION%s value='%s'>%s</OPTION>\n",(selected != NULL?" SELECTED":""),terms[tix],labels[tix]);
-                }
-            printf("</SELECT>\n");
-            }
-        else if (searchBy == cvsSearchBySingleSelect)
-            {
-#ifdef BLOCK_MULTI_SELECT_SUPPORT
-            cgiDropDownWithTextValsAndExtra(buf, mdbVarLabels, mdbVars,count,mdbVar[i],"class='mdbVar noMulti' style='font-size:.9em;' onchange='findTracksMdbVarChanged(this);'");
-            safef(buf, sizeof(buf), "%s%i", METADATA_VALUE_PREFIX, i + 1);
-        #endif///ndef BLOCK_MULTI_SELECT_SUPPORT
-            hPrintf("</td>\n<td align='right' id='isLike%i' style='width:10px; white-space:nowrap;'>is</td>\n<td nowrap id='%s' style='max-width:600px;'>\n",i + 1,buf);
-            len = getTermArray(conn, &labels, &terms, mdbVar[i]);
-            cgiMakeDropListFull(buf, labels, terms, len, mdbVal[i], "class='mdbVal' style='min-width:200px; font-size:.9em;' onchange='findTracksMdbValChanged(this);'");
-            }
-        else if (searchBy == cvsSearchByFreeText)
-            {
-#ifdef BLOCK_MULTI_SELECT_SUPPORT
-            cgiDropDownWithTextValsAndExtra(buf, mdbVarLabels, mdbVars,count,mdbVar[i],"class='mdbVar noMulti' style='font-size:.9em;' onchange='findTracksMdbVarChanged(this);'");
-            safef(buf, sizeof(buf), "%s%i", METADATA_VALUE_PREFIX, i + 1);
-        #endif///ndef BLOCK_MULTI_SELECT_SUPPORT
-            hPrintf("</td><td align='right' id='isLike%i' style='width:10px; white-space:nowrap;'>contains</td>\n<td nowrap id='%s' style='max-width:600px;'>\n",i + 1,buf);
-            hPrintf("<input type='text' name='%s' value='%s' class='mdbVal freeText' style='max-width:310px; width:310px; font-size:.9em;' onchange='findTracksMdbVarChanged(true);'>\n",
-                    buf,(mdbVal[i] ? mdbVal[i]: ""));
-            }
-        else if (searchBy == cvsSearchByDateRange || searchBy == cvsSearchByIntegerRange)
-            {
-            // TO BE IMPLEMENTED
-            }
-        hPrintf("<span id='helpLink%i'>&nbsp;</span></td>\n", i + 1);
-        hPrintf("</tr>\n");
-        }
-
-    hPrintf("<tr><td colspan='%d' align='right' style='height:10px; max-height:10px;'>&nbsp;</td></tr>", cols);
-    //hPrintf("<tr><td colspan='%d' align='right' class='lineOnTop' style='height:20px; max-height:20px;'>&nbsp;</td></tr>", cols);
-
-return numMetadataSelects;
-}
-
 static struct slRef *simpleSearchForTracksstruct(struct trix *trix,char **descWords,int descWordCount)
 // Performs the simple search and returns the found tracks.
 {
 struct slRef *tracks = NULL;
 
 struct trixSearchResult *tsList;
 for(tsList = trixSearch(trix, descWordCount, descWords, TRUE); tsList != NULL; tsList = tsList->next)
     {
     struct track *track = (struct track *) hashFindVal(trackHash, tsList->itemId);
     if (track != NULL)  // It is expected that this is NULL (e.g. when the trix references trackDb tracks which have no tables)
         {
         refAdd(&tracks, track);
         }
     }
 return tracks;
@@ -863,58 +563,53 @@
         "<li>Drop-down list of terms (cells, antibodies, etc.) should be multi-select with checkBoxes as seen in filterComposites. Perhaps saved for v2.0.</li>"
         "</ul></p>\n");
     }
 #endif///def OMIT
 hPrintf("</div>"); // This div allows the clear button to empty it
 }
 
 void doSearchTracks(struct group *groupList)
 {
 if (!advancedJavascriptFeaturesEnabled(cart))
     {
     warn("Requires advanced javascript features.");
     return;
     }
 
-#ifndef BLOCK_MULTI_SELECT_SUPPORT
 webIncludeResourceFile("ui.dropdownchecklist.css");
 //jsIncludeFile("ui.core.js",NULL);   // NOTE: This appears to be not needed as long as jquery-ui.js comes before ui.dropdownchecklist.js
 jsIncludeFile("ui.dropdownchecklist.js",NULL);
 // This line is needed to get the multi-selects initialized
 hPrintf("<script type='text/javascript'>$(document).ready(function() { $('.filterBy').each( function(i) { $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true });});});</script>\n");
-#endif///ndef BLOCK_MULTI_SELECT_SUPPORT
 
 struct group *group;
 char *groups[128];
 char *labels[128];
 int numGroups = 1;
 groups[0] = ANYLABEL;
 labels[0] = ANYLABEL;
 char *nameSearch = cartOptionalString(cart, TRACK_SEARCH_ON_NAME);
 char *typeSearch = cartOptionalString(cart, TRACK_SEARCH_ON_TYPE);
 #ifdef FILES_SEARCH
-char *fileTypeSearch = cartOptionalString(cart, TRACK_SEARCH_ON_FILETYPE);
+char *fileTypeSearch = cartOptionalString(cart, FILE_SEARCH_ON_FILETYPE);
 #endif///def FILES_SEARCH
 char *descSearch=FALSE;
 char *groupSearch = cartOptionalString(cart, TRACK_SEARCH_ON_GROUP);
 boolean doSearch = sameString(cartOptionalString(cart, TRACK_SEARCH), "Search") || cartUsualInt(cart, TRACK_SEARCH_PAGER, -1) >= 0;
 struct sqlConnection *conn = hAllocConn(database);
 boolean metaDbExists = sqlTableExists(conn, "metaDb");
-int numMetadataSelects, tracksFound = 0;
-int numMetadataNonEmpty = 0;
-char **mdbVar = NULL;
-char **mdbVal = NULL;
+int tracksFound = 0;
 #ifdef ONE_FUNC
 struct hash *parents = newHash(4);
 #endif///def ONE_FUNC
 struct trix *trix;
 char trixFile[HDB_MAX_PATH_STRING];
 char **descWords = NULL;
 int descWordCount = 0;
 boolean searchTermsExist = FALSE;
 int cols;
 char buf[512];
 
 enum searchTab selectedTab = simpleTab;
 char *currentTab = cartUsualString(cart, TRACK_SEARCH_CURRENT_TAB, "simpleTab");
 if(sameString(currentTab, "simpleTab"))
     {
@@ -1034,35 +729,44 @@
     searchTermsExist = TRUE;
 
 // Track Type is (drop down)
 hPrintf("<tr><td colspan=2></td><td align='right'>and&nbsp;</td>\n");
 hPrintf("<td nowrap><b style='max-width:100px;'>Data Format:</b></td>");
 hPrintf("<td align='right'>is</td>\n");
 hPrintf("<td colspan='%d'>", cols - 4);
 char **formatTypes = NULL;
 char **formatLabels = NULL;
 int formatCount = getFormatTypes(&formatLabels, &formatTypes);
 cgiMakeDropListFull(TRACK_SEARCH_ON_TYPE, formatLabels, formatTypes, formatCount, typeSearch, "class='typeSearch' style='min-width:40%; font-size:.9em;'");
 hPrintf("</td></tr>\n");
 if (selectedTab==advancedTab && typeSearch)
     searchTermsExist = TRUE;
 
-// Metadata selects require careful accounting
+// mdb selects
+struct slPair *mdbSelects = NULL;
 if(metaDbExists)
-    numMetadataSelects = printMdbSelects(conn, cart, selectedTab, &mdbVar, &mdbVal, &numMetadataNonEmpty, cols);
-else
-    numMetadataSelects = 0;
+    {
+    struct slPair *mdbVars = mdbVarsRelevant(conn);
+    mdbSelects = mdbSelectPairs(cart,selectedTab, mdbVars);
+    char *output = mdbSelectsHtmlRows(conn,mdbSelects,mdbVars,cols);
+    if (output)
+        {
+        printf(output);
+        freeMem(output);
+        }
+    slPairFreeList(&mdbVars);
+    }
 
 hPrintf("</table>\n");
 hPrintf("<input type='submit' name='%s' id='searchSubmit' value='search' style='font-size:.8em;'>\n", TRACK_SEARCH);
 hPrintf("<input type='button' name='clear' value='clear' class='clear' style='font-size:.8em;' onclick='findTracksClear();'>\n");
 hPrintf("<input type='submit' name='submit' value='cancel' class='cancel' style='font-size:.8em;'>\n");
 //hPrintf("<a target='_blank' href='../goldenPath/help/trackSearch.html'>help</a>\n");
 hPrintf("</div>\n");
 
 #ifdef FILES_SEARCH
 // Files tab
 hPrintf("<div id='filesTab' style='width:inherit;'>\n"
         "<table id='filesTable' cellSpacing=0 style='width:inherit; font-size:.9em;'>\n");
 cols = 8;
 
 //// Track Name contains
@@ -1088,41 +792,53 @@
 //hPrintf("<tr><td colspan=2></td><td align='right'>and&nbsp;</td>\n");
 //hPrintf("<td><b style='max-width:100px;'>Group:</b></td>");
 //hPrintf("<td align='right'>is</td>\n");
 //hPrintf("<td colspan='%d'>", cols - 4);
 //cgiMakeDropListFull(TRACK_SEARCH_ON_GROUP, labels, groups, numGroups, groupSearch, "class='groupSearch' style='min-width:40%; font-size:.9em;'");
 //hPrintf("</td></tr>\n");
 //if (selectedTab==fileTab && groupSearch)
 //    searchTermsExist = TRUE;
 
 // Track Type is (drop down)
 hPrintf("<tr><td colspan=2></td><td align='right'>&nbsp;</td>\n");
 //hPrintf("<tr><td colspan=2></td><td align='right'>and&nbsp;</td>\n"); // Bring back "and" if using "Track Name,Description or Group
 hPrintf("<td nowrap><b style='max-width:100px;'>Data Format:</b></td>");
 hPrintf("<td align='right'>is</td>\n");
 hPrintf("<td colspan='%d'>", cols - 4);
-formatCount = getFileFormatTypes(&formatLabels, &formatTypes);
-cgiMakeDropListFull(TRACK_SEARCH_ON_FILETYPE, formatLabels, formatTypes, formatCount, fileTypeSearch, "class='fileTypeSearch' style='min-width:40%; font-size:.9em;'");
+char *dropDownHtml = fileFormatSelectHtml(FILE_SEARCH_ON_FILETYPE,fileTypeSearch,"style='min-width:40%; font-size:.9em;'");
+if (dropDownHtml)
+    {
+    puts(dropDownHtml);
+    freeMem(dropDownHtml);
+    }
 hPrintf("</td></tr>\n");
 if (selectedTab==filesTab && fileTypeSearch)
     searchTermsExist = TRUE;
 
-// Metadata selects require careful accounting
+// mdb selects
 if(metaDbExists)
-    numMetadataSelects = printMdbSelects(conn, cart, selectedTab, &mdbVar, &mdbVal, &numMetadataNonEmpty, cols);
-else
-    numMetadataSelects = 0;
+    {
+    struct slPair *mdbVars = mdbVarsRelevant(conn);
+    mdbSelects = mdbSelectPairs(cart,selectedTab, mdbVars);
+    char *output = mdbSelectsHtmlRows(conn,mdbSelects,mdbVars,cols);
+    if (output)
+        {
+        printf(output);
+        freeMem(output);
+        }
+    slPairFreeList(&mdbVars);
+    }
 
 hPrintf("</table>\n");
 hPrintf("<input type='submit' name='%s' id='searchSubmit' value='search' style='font-size:.8em;'>\n", TRACK_SEARCH);
 hPrintf("<input type='button' name='clear' value='clear' class='clear' style='font-size:.8em;' onclick='findTracksClear();'>\n");
 hPrintf("<input type='submit' name='submit' value='cancel' class='cancel' style='font-size:.8em;'>\n");
 //hPrintf("<a target='_blank' href='../goldenPath/help/trackSearch.html'>help</a>\n");
 hPrintf("</div>\n");
 
 #endif///def FILES_SEARCH
 hPrintf("</div>\n");
 
 hPrintf("</form>\n");
 hPrintf("</div>"); // Restricts to max-width:1000px;
 
 if (measureTiming)
@@ -1144,68 +860,58 @@
     while (val != NULL)
         {
         slNameAddTail(&descList, val);
         descWordCount++;
         val = nextWord(&tmp);
         }
     descWords = needMem(sizeof(char *) * descWordCount);
     for(i = 0, el = descList; el != NULL; i++, el = el->next)
         descWords[i] = strLower(el->name);
     }
 if (doSearch && selectedTab==simpleTab && descWordCount <= 0)
     doSearch = FALSE;
 
 if(doSearch)
     {
-    // Convert to slPair list
-    int ix=0;
-    struct slPair *mdbPairs = NULL;
-    for(ix = 0; ix < numMetadataSelects; ix++)
-        {
-        if(!isEmpty(mdbVal[ix]))
-            slAddHead(&mdbPairs,slPairNew(mdbVar[ix],mdbVal[ix]));
-        }
-    slReverse(&mdbPairs);
-
     // Now search
     struct slRef *tracks = NULL;
     if(selectedTab==simpleTab)
         tracks = simpleSearchForTracksstruct(trix,descWords,descWordCount);
     else if(selectedTab==advancedTab)
-        tracks = advancedSearchForTracks(conn,groupList,descWords,descWordCount,nameSearch,typeSearch,descSearch,groupSearch,mdbPairs);
+        tracks = advancedSearchForTracks(conn,groupList,descWords,descWordCount,nameSearch,typeSearch,descSearch,groupSearch,mdbSelects);
 #ifdef FILES_SEARCH
-    else if(selectedTab==filesTab && mdbPairs != NULL)
-        fileSearchResults(database, conn, mdbPairs, fileTypeSearch);
+    else if(selectedTab==filesTab && mdbSelects != NULL)
+        fileSearchResults(database, conn, mdbSelects, fileTypeSearch);
 #endif///def FILES_SEARCH
 
     if (measureTiming)
         uglyTime("Searched for tracks");
 
     // Sort and Print results
     if(selectedTab!=filesTab)
         {
         enum sortBy sortBy = cartUsualInt(cart,TRACK_SEARCH_SORT,sbRelevance);
         tracksFound = slCount(tracks);
         if(tracksFound > 1)
             findTracksSort(&tracks,sortBy);
 
         displayFoundTracks(cart,tracks,tracksFound,sortBy);
 
         if (measureTiming)
             uglyTime("Displayed found tracks");
         }
-    slPairFreeList(&mdbPairs);
+    slPairFreeList(&mdbSelects);
     }
 hFreeConn(&conn);
 
 webNewSection("About Track Search");
 if(metaDbExists)
     hPrintf("<p>Search for terms in track names, descriptions, groups, and ENCODE "
             "metadata.  If multiple terms are entered, only tracks with all terms "
             "will be part of the results.");
 else
     hPrintf("<p>Search for terms in track descriptions, groups, and names. "
             "If multiple terms are entered, only tracks with all terms "
             "will be part of the results.");
 hPrintf("<BR><a target='_blank' href='../goldenPath/help/trackSearch.html'>more help</a></p>\n");
 webEndSectionTables();
 }