b8d33c3a3fc4c67dcea6509e744160f8d2c21e7c
tdreszer
  Fri Feb 25 12:33:30 2011 -0800
Support for multi-selects for mdb searches on advanced tab. Requires cv.ra change to be active.
diff --git src/hg/hgTracks/searchTracks.c src/hg/hgTracks/searchTracks.c
index 40e6b3d..dabb9f2 100644
--- src/hg/hgTracks/searchTracks.c
+++ src/hg/hgTracks/searchTracks.c
@@ -9,43 +9,47 @@
 #include "hash.h"
 #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"
 #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. */
 {
@@ -319,30 +323,31 @@
         {
         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);
@@ -376,32 +381,49 @@
         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);
-            mdbVal[i] = cloneString(cartOptionalString(cart, buf));
-            if(sameString(mdbVal[i], ANYLABEL))
+                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);
@@ -410,94 +432,99 @@
         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>", cols,COLOR_DARKGREY);
+    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));
-    #ifndef PLUS_MINUS_BUTTON
-        #define PRINT_BUTTON(name,value,msg,js) printf("<input type='submit' name='%s' value='%s' style='font-size:.7em;' title='%s this row' onclick='%s'>", (name), (value), (msg), (js));
-    #endif//ndef PLUS_MINUS_BUTTON
-        hPrintf("<tr class='mdbSelect'><td nowrap>\n");
+    #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)
-            {
-        #ifdef PLUS_MINUS_BUTTON
             PRINT_PM_BUTTON("minus", i + 1, '-');
-        #else///ifndef PLUS_MINUS_BUTTON
-            safef(buf, sizeof(buf), "return delSearchSelect(this, %d);", i + 1);
-            PRINT_BUTTON(TRACK_SEARCH, "-", "delete this row", buf);
-        #endif//ndef PLUS_MINUS_BUTTON
-            }
         else
             hPrintf("&nbsp;");
-    #ifdef PLUS_MINUS_BUTTON
         PRINT_PM_BUTTON("plus", i + 1, '+');
-    #else///ifndef PLUS_MINUS_BUTTON
-        hPrintf("</td><td>\n");
-        safef(buf, sizeof(buf), "return addSearchSelect(this, %d);", i + 1);
-        PRINT_BUTTON(TRACK_SEARCH, "+", "add another row after this row", buf);
-    #endif//ndef PLUS_MINUS_BUTTON
 
         hPrintf("</td><td>and&nbsp;</td><td colspan=3 nowrap>\n");
         safef(buf, sizeof(buf), "%s%i", METADATA_NAME_PREFIX, i + 1);
-        cgiDropDownWithTextValsAndExtra(buf, mdbVarLabels, mdbVars,count,mdbVar[i],"class='mdbVar' style='font-size:.9em;' onchange='findTracksMdbVarChanged(this);'");
-        // TODO: move to lib since hgTracks and hgApi share
         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)
             {
-            // TO BE IMPLEMENTED
+        #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 (any of)</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);
-            hPrintf("</td><td align='right' id='isLike%d' style='width:10px;'>is</td><td nowrap id='%s' style='max-width:600px;'>\n",i + 1,buf);
+        #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]);
-        #ifdef PLUS_MINUS_BUTTON
-            cgiMakeDropListFull(buf, labels, terms, len, mdbVal[i], "class='mdbVal single' style='min-width:200px; font-size:.9em;' onchange='findTracksMdbValChanged(this);'");
-        #else///ifndef PLUS_MINUS_BUTTON
-            cgiMakeDropListFull(buf, labels, terms, len, mdbVal[i], "class='mdbVal single' style='min-width:200px; font-size:.9em;' onchange='findTracksSearchButtonsEnable(true);'");
-        #endif//ndef PLUS_MINUS_BUTTON
+            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);
-            hPrintf("</td><td align='right' id='isLike%d' style='width:10px;'>contains</td><td nowrap id='%s' style='max-width:600px;'>\n",i + 1,buf);
-            hPrintf("<input type='text' name='%s' value='%s' class='mdbVal freeText' onkeyup='findTracksSearchButtonsEnable(true);' style='max-width:310px; width:310px; font-size:.9em;'>",
+        #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%d'>help</span></td>\n", i + 1);
+        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;
@@ -835,30 +862,39 @@
         "<li>Look and feel of found track list (here) and composite subtrack list (hgTrackUi) should converge.  Jim suggests look and feel of hgTracks 'Configure Tracks...' list instead.</li>"
         "<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);
 #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);