733ba7b804e84d7b60ccad1f137398ecd52db983 chmalee Tue Apr 23 18:15:09 2024 -0700 Add a general highlight trackDb variable(s), working like trackDb filters, except put a color behind the item, refs #24507 diff --git src/hg/lib/hui.c src/hg/lib/hui.c index 588f050..4df1012 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -3964,71 +3964,72 @@ { safef(labelSetting, sizeof labelSetting, "%s.%s", field, FILTER_LABEL_NAME_CAP); trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL); } if (trackDbLabel == NULL) { safef(labelSetting, sizeof labelSetting, "%s%s", field, FILTER_LABEL_NAME_CAP); trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL); } return trackDbLabel; } static filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, struct trackDbFilter *tdbFilter, char *name) /* Build a filterBy_t structure from a FilterValues statement. */ { +boolean isHighlight = startsWith("highlightValues.", tdbFilter->name); char *field = tdbFilter->fieldName; if (isEmpty(tdbFilter->setting)) errAbort("FilterValues setting of field '%s' must have a value.", tdbFilter->fieldName); char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, tdbFilter->name, tdbFilter->setting); filterBy_t *filterBy; AllocVar(filterBy); filterBy->column = cloneString(field); filterBy->title = cloneString(field); /// title should come from AS file, or trackDb variable struct asColumn *asCol = asColumnFind(as, field); if (asCol != NULL) filterBy->title = asCol->comment; else errAbort("Building filter on field %s which is not in AS file.", field); char *trackDbLabel = getLabelSetting(cart, tdb, field); if (trackDbLabel) filterBy->title = trackDbLabel; filterBy->useIndex = FALSE; filterBy->slValues = slNameListFromCommaEscaped(value); chopUpValues(filterBy); if (cart != NULL) { char suffix[256]; - safef(suffix, sizeof(suffix), "%s.%s", "filterBy", filterBy->column); + safef(suffix, sizeof(suffix), "%s.%s", isHighlight ? "highlightBy" : "filterBy", filterBy->column); boolean parentLevel = isNameAtParentLevel(tdb,tdb->track); if (cartLookUpVariableClosestToHome(cart,tdb,parentLevel,suffix,&(filterBy->htmlName))) { filterBy->slChoices = cartOptionalSlNameList(cart,filterBy->htmlName); freeMem(filterBy->htmlName); } } if (filterBy->slChoices == NULL) // no settings in cart, initialize from trackDb { char *setting = getFilterValueDefaultsSetting(cart, tdb, field); filterBy->slChoices = slNameListFromCommaEscaped(setting); } struct dyString *dy = dyStringNew(128); -dyStringPrintf(dy, "%s.%s.%s", name, "filterBy", filterBy->column); +dyStringPrintf(dy, "%s.%s.%s", name, isHighlight ? "highlightBy": "filterBy", filterBy->column); filterBy->htmlName = dy->string; return filterBy; } filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct trackDbFilter *trackDbFilters, char *name) /* Build a filterBy_t list from tdb variables of the form *FilterValues */ { struct asObject *as = asForTdb(NULL, tdb); if (as == NULL) errAbort("Unable to get autoSql for %s", name); filterBy_t *filterByList = NULL, *filter; struct trackDbFilter *fieldFilter; while ((fieldFilter = slPopHead(&trackDbFilters)) != NULL) { @@ -4036,30 +4037,36 @@ 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 if (differentString(subName, "highlightBy")) { struct trackDbFilter *trackDbFilters = tdbGetTrackFilterByFilters( tdb); if (trackDbFilters) return filterByValues(tdb, cart, trackDbFilters, name); } +else + { + struct trackDbFilter *trackDbHighlights = tdbGetTrackHighlightByHighlights(tdb); + if (trackDbHighlights) + return filterByValues(tdb, cart, trackDbHighlights, name); + } 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; for (ix=0;ixhelp" int count = slCount(filterBySet); if (count == 1) puts(""); else printf("%s items by: (select multiple categories and items - %s)" "
\n",filterTypeTitle,FILTERBY_HELP_LINK); #ifdef ADVANCED_BUTTON if (tdbIsBigBed(tdb)) { @@ -4359,38 +4367,51 @@ selectStatement[0] = 0; if(count == 1) printf("%s by %s%s",filterTypeTitle,filterBy->title,selectStatement); else printf("%s",filterBy->title); puts(""); } puts(""); for (filterBy = filterBySet; filterBy != NULL; filterBy = filterBy->next) { puts(""); } puts(""); int ix=0; for (filterBy = filterBySet; filterBy != NULL; filterBy = filterBy->next, ix++) { char *setting = getFilterType(cart, tdb, filterBy->column, FILTERBY_DEFAULT); puts(""); } puts("
"); char *setting = getFilterType(cart, tdb, filterBy->column, FILTERBY_DEFAULT); if (advancedFilter(cart, tdb, setting)) { char cartSettingString[4096]; + if (isHighlight) + { + safef(cartSettingString, sizeof cartSettingString, "%s.%s.%s", prefix,HIGHLIGHT_TYPE_NAME_LOW, filterBy->column); + printf("
Highlight if "); + // ADVANCED BUTTON printf(" "); + } + else + { safef(cartSettingString, sizeof cartSettingString, "%s.%s.%s", prefix,FILTER_TYPE_NAME_LOW, filterBy->column); printf("
Match if "); // ADVANCED BUTTON printf(" "); } + } puts("
"); // value is always "All", even if label is different, to simplify javascript code int valIx = 1; if (filterByColumnIsMultiple(cart, tdb, setting)) printf( "\n", selectIdPrefix,ix,filterBy->htmlName); @@ -4431,38 +4452,38 @@ } } printf(">%s\n",label); } printf("\n"); puts("
"); } void filterBySetCfgUi(struct cart *cart, struct trackDb *tdb, filterBy_t *filterBySet, boolean onOneLine, char *prefix) /* Does the filter UI for a list of filterBy structure */ { -filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", "fbc", "All", prefix); +filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", "fbc", "All", prefix, FALSE); } void highlightBySetCfgUi(struct cart *cart, struct trackDb *tdb, - filterBy_t *filterBySet, boolean onOneLine, char *prefix) + filterBy_t *filterBySet, boolean onOneLine, char *prefix, boolean isHighlight) /* Does the highlight UI for a list of filterBy structure */ { -filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", "hbc", "None", prefix); +filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", "hbc", "None", prefix, TRUE); } #define COLOR_BG_DEFAULT_IX 0 #define COLOR_BG_ALTDEFAULT_IX 1 #define DIVIDING_LINE "
\n" #define DIVIDER_PRINT(color) printf(DIVIDING_LINE,COLOR_BG_DEFAULT,(color)) static boolean divisionIfNeeded(char **lastDivide,dividers_t *dividers,membership_t *membership) // Keeps track of location within subtracks in order to provide requested division lines { boolean division = FALSE; if (dividers) { if (lastDivide != NULL) @@ -6578,31 +6599,31 @@ } // Defaulting min and max within limits. Sorry for the horizontal ifs, // but stacking the group makes them easier to follow if (min && limitMin && (int)(*limitMin) != NO_VALUE && ((int)(*min) == NO_VALUE || *min < *limitMin)) *min = *limitMin; if (min && limitMax && (int)(*limitMax) != NO_VALUE && *min > *limitMax) *min = *limitMax; if (max && limitMax && (int)(*limitMax) != NO_VALUE && ((int)(*max) == NO_VALUE || *max > *limitMax)) *max = *limitMax; if (max && limitMin && (int)(*limitMin) != NO_VALUE && *max < *limitMin) *max = *limitMin; } static boolean showScoreFilter(struct cart *cart, struct trackDb *tdb, boolean *opened, boolean boxed, boolean parentLevel,char *name, char *title, - char *label, char *scoreName) + char *label, char *scoreName, boolean isHighlight) // Shows a score filter control with minimum value and optional range { char *setting = trackDbSetting(tdb, scoreName); if (setting) { if (*opened == FALSE) { boxed = cfgBeginBoxAndTitle(tdb, boxed, title); puts(""); *opened = TRUE; } printf("
%s:",label); char varName[256]; char altLabel[256]; char *filterName = getScoreNameAdd(tdb, scoreName, _BY_RANGE); @@ -6692,53 +6713,93 @@ return tdbGetTrackFilters( tdb, FILTER_NUMBER_WILDCARD_LOW, FILTER_NUMBER_NAME_LOW, FILTER_NUMBER_WILDCARD_CAP, FILTER_NUMBER_NAME_CAP); } struct trackDbFilter *tdbGetTrackTextFilters( struct trackDb *tdb) // get the text filters out of trackDb { return tdbGetTrackFilters( tdb, FILTER_TEXT_WILDCARD_LOW, FILTER_TEXT_NAME_LOW, FILTER_TEXT_WILDCARD_CAP, FILTER_TEXT_NAME_CAP); } struct trackDbFilter *tdbGetTrackFilterByFilters( struct trackDb *tdb) // get the values filters out of trackDb { return tdbGetTrackFilters( tdb, FILTER_VALUES_WILDCARD_LOW, FILTER_VALUES_NAME_LOW, FILTER_VALUES_WILDCARD_CAP, FILTER_VALUES_NAME_CAP); } +struct trackDbFilter *tdbGetTrackNumHighlights( struct trackDb *tdb) +// get the number filters out of trackDb +{ +return tdbGetTrackFilters( tdb, HIGHLIGHT_NUMBER_WILDCARD_LOW, HIGHLIGHT_NUMBER_NAME_LOW, HIGHLIGHT_NUMBER_WILDCARD_CAP, HIGHLIGHT_NUMBER_NAME_CAP); +} + +struct trackDbFilter *tdbGetTrackTextHighlights( struct trackDb *tdb) +// get the text filters out of trackDb +{ +return tdbGetTrackFilters( tdb, HIGHLIGHT_TEXT_WILDCARD_LOW, HIGHLIGHT_TEXT_NAME_LOW, HIGHLIGHT_TEXT_WILDCARD_CAP, HIGHLIGHT_TEXT_NAME_CAP); +} + +struct trackDbFilter *tdbGetTrackHighlightByHighlights( struct trackDb *tdb) +// get the values filters out of trackDb +{ +return tdbGetTrackFilters( tdb, HIGHLIGHT_VALUES_WILDCARD_LOW, HIGHLIGHT_VALUES_NAME_LOW, HIGHLIGHT_VALUES_WILDCARD_CAP, HIGHLIGHT_VALUES_NAME_CAP); +} + +char *prevHighlightColor(struct cart *cart, struct trackDb *tdb) +/* Return the cart string for the highlight color if it has been changed else the default */ +{ +return cartOrTdbString(cart, tdb, HIGHLIGHT_COLOR_CART_VAR, HIGHLIGHT_COLOR_DEFAULT); +} + +void printHighlightColorPicker(struct cart *cart, struct trackDb *tdb) +{ +jsIncludeFile("ajax.js", NULL); +jsIncludeFile("hui.js", NULL); +puts("
"); +puts("Choose highlight color:"); +puts("
"); +jsInlineF("var cartHighlightColor = \"%s\"\n;", prevHighlightColor(cart, tdb)); +jsInlineF("makeHighlightPicker(\"hgTrackUiHighlight\", document.getElementById(\"hgTrackUiColorPicker\"), \"%s\");\n", tdb->track); +} + int defaultFieldLocation(char *field) /* Sometimes we get bigBed filters with field names that are not in the AS file. * Try to guess what the user means. */ { if (sameString("score", field)) return 4; if (sameString("signal", field)) return 6; if (sameString("signalValue", field)) return 6; if (sameString("pValue", field)) return 7; if (sameString("qValue", field)) return 8; return -1; } static int numericFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean *opened, - boolean boxed, boolean parentLevel,char *name, char *title) + boolean boxed, boolean parentLevel,char *name, char *title, + boolean isHighlight) // Shows all *Filter style filters. Note that these are in random order and have no graceful title { int count = 0; -struct trackDbFilter *trackDbFilters = tdbGetTrackNumFilters(tdb); +struct trackDbFilter *trackDbFilters = NULL; +if (isHighlight) + trackDbFilters = tdbGetTrackNumHighlights(tdb); +else + trackDbFilters = tdbGetTrackNumFilters(tdb); if (trackDbFilters) { puts("
"); struct trackDbFilter *filter = NULL; struct sqlConnection *conn = NULL; if (!isHubTrack(db)) conn = hAllocConnTrack(db, tdb); struct asObject *as = asForTdb(conn, tdb); hFreeConn(&conn); while ((filter = slPopHead(&trackDbFilters)) != NULL) { char *field = filter->fieldName; char *scoreName = cloneString(filter->name); char *trackDbLabel = getLabelSetting(cart, tdb, field); @@ -6751,31 +6812,33 @@ field = asCol->comment; } else if (defaultFieldLocation(field) < 0) errAbort("Building filter on field %s which is not in AS file.", field); } char labelBuf[1024]; char *label = labelBuf; char *filterName = getScoreNameAdd(tdb, scoreName, _BY_RANGE); boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, filterName); if (trackDbLabel) label = trackDbLabel; else safef(labelBuf, sizeof(labelBuf),"%s%s", filterByRange ? "": "Minimum ", field); - showScoreFilter(cart,tdb,opened,boxed,parentLevel,name,title,label,scoreName); + if (isHighlight && count == 0) + printHighlightColorPicker(cart, tdb); + showScoreFilter(cart,tdb,opened,boxed,parentLevel,name,title,label,scoreName,isHighlight); count++; } if (as != NULL) asObjectFree(&as); } if (count > 0) puts("
"); return count; } boolean bedHasFilters(struct trackDb *tdb) // Does track have filters { if (trackDbSettingClosestToHome(tdb, FILTER_BY)) @@ -6840,64 +6903,70 @@ safef(settingString, sizeof settingString, "%s.%s", FILTER_TYPE_NAME_LOW, field); char *setting = cartOrTdbString(cart, tdb, settingString, NULL); if (setting == NULL) { safef(settingString, sizeof settingString, "%s.%s", field, FILTER_TYPE_NAME_CAP); setting = cartOrTdbString(cart, tdb, settingString, NULL); } if (setting == NULL) { safef(settingString, sizeof settingString, "%s%s", field, FILTER_TYPE_NAME_CAP); setting = cartOrTdbString(cart, tdb, settingString, def); } return setting; } -static int textFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb) +static int textFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean isHighlight) /* Show all the text filters for this track. */ { int count = 0; -struct trackDbFilter *trackDbFilters = tdbGetTrackTextFilters(tdb); +struct trackDbFilter *trackDbFilters = NULL; +if (isHighlight) + trackDbFilters = tdbGetTrackTextHighlights(tdb); +else + trackDbFilters = tdbGetTrackTextFilters(tdb); if (trackDbFilters) { puts("
"); struct trackDbFilter *filter = NULL; struct sqlConnection *conn = NULL; if (!isHubTrack(db)) conn = hAllocConnTrack(db, tdb); struct asObject *as = asForTdb(conn, tdb); hFreeConn(&conn); while ((filter = slPopHead(&trackDbFilters)) != NULL) { char *trackDbLabel = getLabelSetting(cart, tdb, filter->fieldName); char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, filter->setting); if (as != NULL) { struct asColumn *asCol = asColumnFind(as, filter->fieldName); if (asCol != NULL) { if (trackDbLabel == NULL) trackDbLabel = asCol->comment; } else if (defaultFieldLocation(filter->fieldName) < 0) errAbort("Building filter on field %s which is not in AS file.", filter->fieldName); } if (trackDbLabel == NULL) trackDbLabel = filter->fieldName; + if (isHighlight && count == 0) + printHighlightColorPicker(cart, tdb); count++; - printf("

Filter items in '%s' field: ", trackDbLabel); + printf("

%s items in '%s' field: ", isHighlight ? "Highlight": "Filter", trackDbLabel); char cgiVar[128]; safef(cgiVar,sizeof(cgiVar),"%s.%s",tdb->track,filter->name); cgiMakeTextVar(cgiVar, value, 45); char *setting = getFilterType(cart, tdb, filter->fieldName, FILTERTEXT_WILDCARD); safef(cgiVar,sizeof(cgiVar),"%s.%s.%s",tdb->track,FILTER_TYPE_NAME_LOW, filter->fieldName); printf(" using "); printf(""); printf("  \n", tdb->track); printf("

"); } @@ -6920,50 +6989,80 @@ if (cartOptionalString(cart, "ajax") == NULL) { webIncludeResourceFile("ui.dropdownchecklist.css"); jsIncludeFile("ui.dropdownchecklist.js",NULL); jsIncludeFile("ddcl.js",NULL); } boolean parentLevel = isNameAtParentLevel(tdb,name); if (parentLevel) if (trackDbSettingOn(tdb->parent, "noParentConfig")) return; boolean skipScoreFilter = FALSE; // Numeric filters are first boolean isBoxOpened = FALSE; -if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title) > 0) +if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, FALSE) > 0) skipScoreFilter = TRUE; -if (textFiltersShowAll(db, cart, tdb)) +if (textFiltersShowAll(db, cart, tdb, FALSE)) skipScoreFilter = TRUE; // Add any multi-selects next filterBy_t *filterBySet = filterBySetGet(tdb,cart,name); if (filterBySet != NULL) { if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL) jsIncludeFile("hui.js",NULL); if (!isBoxOpened) // Note filterBy boxes are not double "boxed", printf("
"); // if there are no other filters filterBySetCfgUi(cart,tdb,filterBySet,TRUE, name); filterBySetFree(&filterBySet); skipScoreFilter = TRUE; } +// add any highlights: +// Numeric highlights are first +boolean didHighlightSelector = FALSE; +if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, TRUE) > 0) + { + didHighlightSelector = TRUE; + skipScoreFilter = TRUE; + } + +if (textFiltersShowAll(db, cart, tdb, TRUE)) + { + didHighlightSelector = TRUE; + skipScoreFilter = TRUE; + } + +filterBy_t *highlightBySet = highlightBySetGet(tdb,cart,name); +if (highlightBySet != NULL) + { + if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL) + jsIncludeFile("hui.js",NULL); + if (!didHighlightSelector) + printHighlightColorPicker(cart, tdb); + + if (!isBoxOpened) // Note filterBy boxes are not double "boxed", + printf("
"); // if there are no other filters + highlightBySetCfgUi(cart,tdb,highlightBySet,TRUE, name, TRUE); + filterBySetFree(&highlightBySet); + skipScoreFilter = TRUE; + } + boolean scoreFilterOk = (trackDbSettingClosestToHome(tdb, NO_SCORE_FILTER) == NULL) && !skipScoreFilter; boolean glvlScoreMin = (trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN) != NULL); if (! (scoreFilterOk || glvlScoreMin)) { cfgEndBox(boxed); return; } boxed = cfgBeginBoxAndTitle(tdb, boxed, title); if (scoreFilterOk) { int minLimit=0,maxLimit=maxScore,minVal=0,maxVal=maxScore; getScoreIntRangeFromCart(cart,tdb,parentLevel,SCORE_FILTER,&minLimit,&maxLimit, &minVal, &maxVal); @@ -7491,35 +7590,35 @@ || trackDbSettingClosestToHome(tdb, PVALUE_FILTER) || trackDbSettingClosestToHome(tdb, QVALUE_FILTER) || trackDbSettingClosestToHome(tdb, SCORE_FILTER )); } return FALSE; } void encodePeakCfgUi(struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed) // Put up UI for filtering wgEnocde peaks based on score, Pval and Qval { boolean parentLevel = isNameAtParentLevel(tdb,name); boolean opened = FALSE; showScoreFilter(cart,tdb,&opened,boxed,parentLevel,name,title, - "Minimum Signal value", SIGNAL_FILTER); + "Minimum Signal value", SIGNAL_FILTER, FALSE); showScoreFilter(cart,tdb,&opened,boxed,parentLevel,name,title, - "Minimum P-Value (-log10)",PVALUE_FILTER); + "Minimum P-Value (-log10)",PVALUE_FILTER, FALSE); showScoreFilter(cart,tdb,&opened,boxed,parentLevel,name,title, - "Minimum Q-Value (-log10)",QVALUE_FILTER); + "Minimum Q-Value (-log10)",QVALUE_FILTER, FALSE); char *setting = trackDbSettingClosestToHomeOrDefault(tdb, SCORE_FILTER,NULL);//"0:1000"); if (setting) { if (!opened) { boxed = cfgBeginBoxAndTitle(tdb, boxed, title); puts(""); opened = TRUE; } char varName[256]; int minLimit=0,maxLimit=1000,minVal=0,maxVal=NO_VALUE; colonPairToInts(setting,&minVal,&maxVal); getScoreIntRangeFromCart(cart,tdb,parentLevel,SCORE_FILTER,&minLimit,&maxLimit, &minVal, &maxVal); @@ -7690,31 +7789,31 @@ && !sameString(tdb->track, "ensGeneNonCoding") && !sameString(tdb->track, "encodeGencodeRaceFrags")) baseColorDropLists(cart, tdb, name); filterBy_t *filterBySet = filterBySetGet(tdb,cart,name); if (filterBySet != NULL) { printf("
"); filterBySetCfgUi(cart,tdb,filterBySet,FALSE, name); filterBySetFree(&filterBySet); } filterBy_t *highlightBySet = highlightBySetGet(tdb,cart,name); if (highlightBySet != NULL) { printf("
"); - highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE, name); + highlightBySetCfgUi(cart,tdb,highlightBySet,FALSE, name, TRUE); filterBySetFree(&highlightBySet); } squishyPackOption(cart, name, title, tdb); wigOption(cart, name, title, tdb); cfgEndBox(boxed); } static boolean isSpeciesOn(struct cart *cart, struct trackDb *tdb, char *species, char *option, int optionSize, boolean defaultState) /* check the cart to see if species is turned off or on (default is defaultState) */ { boolean parentLevel = isNameAtParentLevel(tdb,option); if (*option == '\0') safef(option, optionSize, "%s.%s", tdb->track, species); else