4b7050d8e3bce0273a6012e16587db4d8a75e127 braney Wed Aug 29 14:57:27 2018 -0700 add filtering in lists refs #20227 diff --git src/hg/lib/hui.c src/hg/lib/hui.c index b9a30e2..8f6d815 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -40,30 +40,31 @@ #include "makeItemsItem.h" #include "bedDetail.h" #include "pgSnp.h" #include "memgfx.h" #include "trackHub.h" #include "gtexUi.h" #include "genbank.h" #include "htmlPage.h" #include "longRange.h" #include "barChartUi.h" #include "interactUi.h" #include "interact.h" #include "customComposite.h" #include "trackVersion.h" #include "hubConnect.h" +#include "bigBedFilter.h" #define SMALLBUF 256 #define MAX_SUBGROUP 9 #define ADD_BUTTON_LABEL "add" #define CLEAR_BUTTON_LABEL "clear" #define JBUFSIZE 2048 #define DEF_BUTTON "<IMG id=\"btn_%s\" src=\"../images/%s\" alt=\"%s\">\n" #define DEF_BUTTON_JS "setCheckBoxesThatContain('%s',true,false,'%s','','%s');" \ "setCheckBoxesThatContain('%s',false,false,'%s','_defOff','%s');" #define DEFAULT_BUTTON(nameOrId,anc,beg,contains) \ printf(DEF_BUTTON,(anc),"defaults_sm.png","default"); \ safef(id, sizeof id, "btn_%s", (anc)); \ jsOnEventByIdF("click", id, DEF_BUTTON_JS,(nameOrId),(beg),(contains),(nameOrId),(beg),(contains)); @@ -3624,31 +3625,31 @@ strSwapChar(chipper,'_',' '); // Title does not have underscores } else if (filterBy->valueAndLabel) errAbort("filterBy values either all have labels in form of value|label " "or none do."); } } } filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, char *filterName) /* Build a filterBy_t structure from a <column>FilterValues statement. */ { char *setting = trackDbSetting(tdb, filterName); char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filterName, setting); char *field = cloneString(filterName); -int ix = strlen(field) - strlen("FilterValues"); +int ix = strlen(field) - strlen(FILTER_VALUES_NAME); assert(ix > 0); field[ix] = '\0'; filterBy_t *filterBy; AllocVar(filterBy); filterBy->column = field; filterBy->title = field; /// title should come from AS file, or trackDb variable struct asColumn *asCol = asColumnFind(as, field); if (asCol != NULL) filterBy->title = asCol->comment; filterBy->useIndex = FALSE; filterBy->slValues = slNameListFromComma(value); chopUpValues(filterBy); if (cart != NULL) { @@ -3677,31 +3678,31 @@ struct asObject *as = asForTdb(NULL, tdb); filterBy_t *filterByList = NULL, *filter; struct slName *fieldFilter; while ((fieldFilter = slPopHead(&filterValues)) != NULL) { if ((filter = buildFilterBy(tdb, cart, as, fieldFilter->name)) != NULL) slAddHead(&filterByList, filter); } return filterByList; } filterBy_t *filterBySetGetGuts(struct trackDb *tdb, struct cart *cart, char *name, char *subName, char *settingName) // Gets one or more "filterBy" settings (ClosestToHome). returns NULL if not found { // first check to see if this tdb is using "new" FilterValues cart variables -struct slName *filterValues = trackDbSettingsWildMatch(tdb, "*FilterValues"); +struct slName *filterValues = trackDbSettingsWildMatch(tdb, FILTER_VALUES_WILDCARD); if (filterValues) return filterByValues(tdb, cart, filterValues); filterBy_t *filterBySet = NULL; char *setting = trackDbSettingClosestToHome(tdb, settingName); if(setting == NULL) return NULL; if ( name == NULL ) name = tdb->track; setting = cloneString(setting); char *filters[10]; // multiple filterBys are delimited by space but spaces inside filter can be protected "by quotes" int filterCount = chopByWhiteRespectDoubleQuotes(setting, filters, ArraySize(filters)); int ix; @@ -3950,40 +3951,60 @@ else printf("<B>%s items by:</B> (select multiple categories and items - %s)" "<TABLE cellpadding=3><TR valign='top'>\n",filterTypeTitle,FILTERBY_HELP_LINK); filterBy_t *filterBy = NULL; if (cartOptionalString(cart, "ajax") == NULL) { webIncludeResourceFile("ui.dropdownchecklist.css"); jsIncludeFile("ui.dropdownchecklist.js",NULL); jsIncludeFile("ddcl.js",NULL); } int ix=0; for(filterBy = filterBySet;filterBy != NULL; filterBy = filterBy->next, ix++) { + char settingString[4096]; + safef(settingString, sizeof settingString, "%s%s", filterBy->column, FILTER_TYPE_NAME); + char *setting = cartOrTdbString(cart, tdb, settingString, NULL); + boolean isMultiple = (setting != NULL) && (sameString(setting, FILTERBY_MULTIPLE) ||sameString(setting, FILTERBY_MULTIPLE_LIST_OR) ||sameString(setting, FILTERBY_MULTIPLE_LIST_AND)); + puts("<TD>"); + char selectStatement[4096]; + if (isMultiple) + safef(selectStatement, sizeof selectStatement, " (select multiple items - %s)", FILTERBY_HELP_LINK); + else + selectStatement[0] = 0; if(count == 1) - printf("<B>%s by %s</B> (select multiple items - %s)",filterTypeTitle,filterBy->title,FILTERBY_HELP_LINK); + printf("<B>%s by %s</B>%s",filterTypeTitle,filterBy->title,selectStatement); else printf("<B>%s</B>",filterBy->title); printf("<BR>\n"); + if (isMultiple) + { + char cartSettingString[4096]; + safef(cartSettingString, sizeof cartSettingString, "%s.%s", tdb->track, settingString); + printf("<b>Match if "); + cgiMakeRadioButton(cartSettingString, FILTERBY_MULTIPLE_LIST_AND, sameString(setting, FILTERBY_MULTIPLE_LIST_AND)); + printf(" all "); + cgiMakeRadioButton(cartSettingString, FILTERBY_MULTIPLE_LIST_OR, sameString(setting, FILTERBY_MULTIPLE_LIST_OR)); + printf(" one or more match</b> "); + } // TODO: columnCount (Number of filterBoxes per row) should be configurable through tdb setting - #define FILTER_BY_FORMAT "<SELECT id='%s%d' name='%s' multiple style='display: none; font-size:.9em;' class='filterBy'><BR>\n" - printf(FILTER_BY_FORMAT,selectIdPrefix,ix,filterBy->htmlName); + #define FILTER_BY_FORMAT "<SELECT id='%s%d' name='%s' %s style='display: none; font-size:.9em;' class='filterBy'><BR>\n" + printf(FILTER_BY_FORMAT,selectIdPrefix,ix,filterBy->htmlName, isMultiple ? "multiple" : ""); // value is always "All", even if label is different, to simplify javascript code printf("<OPTION%s value=\"All\">%s</OPTION>\n", (filterByAllChosen(filterBy)?" SELECTED":""), allLabel); struct slName *slValue; int ix=1; for (slValue=filterBy->slValues;slValue!=NULL;slValue=slValue->next,ix++) { char varName[32]; char *label = NULL; char *name = NULL; if (filterBy->useIndex) { safef(varName, sizeof(varName), "%d",ix); name = varName; @@ -5727,56 +5748,56 @@ else printf("<TD align='left'%s",altLabel); } puts("</TR>"); return TRUE; } return FALSE; } static int numericFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean *opened, boolean boxed, boolean parentLevel,char *name, char *title) // Shows all *Filter style filters. Note that these are in random order and have no graceful title { int count = 0; -struct slName *filterSettings = trackDbSettingsWildMatch(tdb, "*Filter"); +struct slName *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_NUMBER_WILDCARD); if (filterSettings) { puts("<BR>"); struct slName *filter = NULL; #ifdef EXTRA_FIELDS_SUPPORT struct extraField *extras = extraFieldsGet(db,tdb); #else///ifndef EXTRA_FIELDS_SUPPORT struct sqlConnection *conn = NULL; if (!isHubTrack(db)) conn = hAllocConnTrack(db, tdb); struct asObject *as = asForTdb(conn, tdb); hFreeConn(&conn); #endif///ndef EXTRA_FIELDS_SUPPORT while ((filter = slPopHead(&filterSettings)) != NULL) { if (differentString(filter->name,NO_SCORE_FILTER)) { // Determine floating point or integer char *setting = trackDbSetting(tdb, filter->name); boolean isFloat = (strchr(setting,'.') != NULL); char *scoreName = cloneString(filter->name); char *field = filter->name; // No need to clone: will be thrown away at end of cycle - int ix = strlen(field) - strlen("Filter"); + int ix = strlen(field) - strlen(FILTER_NUMBER_NAME); assert(ix > 0); field[ix] = '\0'; #ifdef EXTRA_FIELDS_SUPPORT if (extras != NULL) { struct extraField *extra = extraFieldsFind(extras, field); if (extra != NULL) { // Found label so replace field field = extra->label; if (!isFloat) isFloat = (extra->type == ftFloat); } } #else///ifndef EXTRA_FIELDS_SUPPORT @@ -5816,31 +5837,31 @@ if (count > 0) puts("</TABLE>"); return count; } boolean bedScoreHasCfgUi(struct trackDb *tdb) // Confirms that this track has a bedScore Cfg UI { // Assumes that cfgType == cfgBedScore if (trackDbSettingClosestToHome(tdb, FILTER_BY)) return TRUE; if (trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN)) return TRUE; boolean blocked = FALSE; -struct slName *filterSettings = trackDbSettingsWildMatch(tdb, "*Filter"); +struct slName *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_NUMBER_WILDCARD); if (filterSettings != NULL) { boolean one = FALSE; struct slName *oneFilter = filterSettings; for (;oneFilter != NULL;oneFilter=oneFilter->next) { if (sameWord(NO_SCORE_FILTER,oneFilter->name)) { blocked = TRUE; continue; } if (differentString(oneFilter->name,SCORE_FILTER)) // scoreFilter is implicit { // but could be blocked one = TRUE; break; @@ -5848,39 +5869,39 @@ } slNameFreeList(&filterSettings); if (one) return TRUE; } if (!blocked) // scoreFilter is implicit unless NO_SCORE_FILTER return TRUE; return FALSE; } void textFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb) /* Show all the text filters for this track. */ { -struct slName *filter, *filterSettings = trackDbSettingsWildMatch(tdb, "*FilterText"); +struct slName *filter, *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_TEXT_WILDCARD); if (filterSettings) { while ((filter = slPopHead(&filterSettings)) != NULL) { char *setting = trackDbSetting(tdb, filter->name); char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, setting); char *field = cloneString(filter->name); - int ix = strlen(field) - strlen("FilterText"); + int ix = strlen(field) - strlen(FILTER_TEXT_NAME); assert(ix > 0); field[ix] = '\0'; printf("<P><B>Filter items by regular expression in '%s' field: ", field); char cgiVar[128]; safef(cgiVar,sizeof(cgiVar),"%s.%s",tdb->track,filter->name); cgiMakeTextVar(cgiVar, value, 45); } } } void scoreCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title, int maxScore, boolean boxed) // Put up UI for filtering bed track based on a score @@ -6353,31 +6374,31 @@ // warn("SELECT FROM %s WHERE %s",tdb->table,dyStringContents(extraWhere)); return extraWhere; } struct dyString *dyAddAllScoreFilters(struct cart *cart, struct trackDb *tdb, struct dyString *extraWhere,boolean *and) // creates the where clause condition to gather together all random double filters // Filters are expected to follow // {fiterName}: trackDb min or min:max - default value(s); // {filterName}Min or {filterName}: min (user supplied) cart variable; // {filterName}Max: max (user supplied) cart variable; // {filterName}Limits: trackDb allowed range "0.0:10.0" Optional // uses: defaultLimits: function param if no tdb limits settings found) // The 'and' param and dyString in/out allows stringing multiple where clauses together { -struct slName *filterSettings = trackDbSettingsWildMatch(tdb, "*Filter"); +struct slName *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_NUMBER_WILDCARD); if (filterSettings) { struct slName *filter = NULL; while ((filter = slPopHead(&filterSettings)) != NULL) { if (differentString(filter->name,"noScoreFilter") && differentString(filter->name,"scoreFilter")) // TODO: scoreFilter could be included { char *field = cloneString(filter->name); int ix = strlen(field) - strlen("filter"); assert(ix > 0); field[ix] = '\0'; char *setting = trackDbSetting(tdb, filter->name); // How to determine float or int ? // If actual tracDb setting has decimal places, then float!