64d40b9204a60274a49330c6b61d146e5cdc6d87 max Wed Sep 9 05:54:34 2026 -0700 Faceted composite: a container's max display mode must clamp a pinned child, not drop it Selecting only CpG methylation on the Fiber-seq compendium and hitting submit drew nothing at all, with no message to say why. Two things combined. The children of a faceted composite can be pinned to one display mode with onlyVisibility, and five of the six Fiber-seq data types are pinned to full because they are signal tracks. The container's own "Maximum display mode" is a ceiling over those children, and it was set to pack. tdbVisLimitedByAncestors() then took a pinned child that sat above the ceiling and returned hide for it, so every pinned-to-full track disappeared and only the peaks, pinned to dense, came through. With every data type but peaks unchecked, that left an empty image. A ceiling should limit a child, not delete it, so use tvMin the same way the unpinned case a line below already did. A bigWig draws the same at pack as at full, minus the horizontal grid, so nothing is lost here. The page was also asking for the wrong ceiling. On a faceted composite the dropdown is a ceiling rather than a display mode, since each child carries its own, so taking the container out of hide should ask for full, the one value that clips nothing. Pack was chosen on the reasoning that it suits a mix of signal and feature tracks, which is the right instinct for a plain composite and the wrong one here. refs #36210 diff --git src/hg/lib/hui.c src/hg/lib/hui.c index 41aba5d4191..29ebdd124c6 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -1,10952 +1,10954 @@ /* hui - human genome user interface common controls. */ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "hash.h" #include "cheapcgi.h" #include "htmshell.h" #include "jksql.h" #include "jsHelper.h" #include "sqlNum.h" #include "cart.h" #include "hdb.h" #include "hui.h" #include "hCommon.h" #include "hgConfig.h" #include "chainCart.h" #include "chainDb.h" #include "netCart.h" #include "obscure.h" #include "wiggle.h" #include "phyloTree.h" #include "hgMaf.h" #include "udc.h" #include "customTrack.h" #include "encode/encodePeak.h" #include "mdb.h" #include "web.h" #include "hPrint.h" #include "fileUi.h" #include "bigBed.h" #include "bigRmskUi.h" #include "bigWig.h" #include "regexHelper.h" #include "snakeUi.h" #include "vcfUi.h" #include "vcf.h" #include "errCatch.h" #include "samAlignment.h" #include "myVariants.h" #include "bedDetail.h" #include "pgSnp.h" #include "memgfx.h" #include "trackHub.h" #include "genark.h" #include "gtexUi.h" #include "genbank.h" #include "htmlPage.h" #include "longRange.h" #include "barChartUi.h" #include "interactUi.h" #include "interact.h" #include "hicUi.h" #include "bigDbSnp.h" #include "customComposite.h" #include "trackVersion.h" #include "hubConnect.h" #include "bigBedFilter.h" #include "bedMethyl.h" // TODO: these should go away after refactoring of multi-region link #include "hex.h" #include "net.h" #include "trashDir.h" #include #define SMALLBUF 256 #define MAX_SUBGROUP 9 #define ADD_BUTTON_LABEL "Show All" #define CLEAR_BUTTON_LABEL "Hide All" #define JBUFSIZE 2048 #define DEF_BUTTON "\"%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)); #define PM_BUTTON "\"%s\"\n" #define PM_BUTTON_JS "setCheckBoxesThatContain('%s',%s,true,'%s','','%s');" #define PLUS_BUTTON(nameOrId,anc,beg,contains) \ printf(PM_BUTTON, (anc), "add_sm.gif", "+"); \ safef(id, sizeof id, "btn_%s", (anc)); \ jsOnEventByIdF("click", id, PM_BUTTON_JS, (nameOrId),"true", (beg),(contains)); #define MINUS_BUTTON(nameOrId,anc,beg,contains) \ printf(PM_BUTTON, (anc), "remove_sm.gif", "-"); \ safef(id, sizeof id, "btn_%s", (anc)); \ jsOnEventByIdF("click", id, PM_BUTTON_JS, (nameOrId),"false", (beg),(contains)); boolean isEncode2(char *database, char *track) /* Return true for tracks created by UCSC DCC during ENCODE production phase */ { if (startsWith("wgEncode", track)) return (sameString(database, "hg18") || sameString(database, "hg19") || sameString(database, "mm9")); return FALSE; } static char *htmlStringForDownloadsLink(char *database, struct trackDb *tdb, char *name,boolean nameIsFile) // Returns an HTML string for a downloads link { // If has fileSortOrder, then link to new hgFileUi if (!nameIsFile && trackDbSetting(tdb, FILE_SORT_ORDER) != NULL) { char * link = needMem(PATH_LEN); // 512 should be enough safef(link,PATH_LEN,"%s", hgFileUiName(),database, /*cartSessionVarName(),cartSessionId(cart),*/ tdb->track, name); // Note the hgsid would be needed if downloads page ever saved fileSortOrder to cart. return link; } else if (trackDbSetting(tdb, "wgEncode") != NULL && isEncode2(database, tdb->track)) // Downloads directory if this is ENCODE { const char *compositeDir = metadataFindValue(tdb, MDB_OBJ_TYPE_COMPOSITE); if (compositeDir == NULL && tdbIsComposite(tdb)) compositeDir = tdb->track; if (compositeDir != NULL) { struct dyString *dyLink = dyStringCreate("%s", hDownloadsServer(), database, ENCODE_DCC_DOWNLOADS, compositeDir, (nameIsFile?name:""), nameIsFile?"file":"files",name); return dyStringCannibalize(&dyLink); } } return NULL; } static boolean makeNamedDownloadsLink(char *database, struct trackDb *tdb,char *name) // Make a downloads link (if appropriate and then returns TRUE) { char *htmlString = htmlStringForDownloadsLink(database,trackDbTopLevelSelfOrParent(tdb),name,FALSE); if (htmlString == NULL) return FALSE; printf("%s", htmlString); freeMem(htmlString); return TRUE; } boolean makeDownloadsLink(char *database, struct trackDb *tdb) // Make a downloads link (if appropriate and then returns TRUE) { return makeNamedDownloadsLink(database, tdb,"Downloads"); } void makeTopLink(struct trackDb *tdb) // Link to top of UI page { if (trackDbSetting(tdb, "dimensions")) { char *upArrow = "⇑"; enum browserType browser = cgiBrowser(); if (browser == btIE || browser == btFF) upArrow = "↑"; // Note: the nested spans are so that javascript can determine position // and selectively display the link when appropriate printf("",upArrow); } } boolean makeSchemaLink(char *db,struct trackDb *tdb,char *label) // Make a table schema link (if appropriate and then returns TRUE) { #define SCHEMA_LINKED "%s" if (trackDataAccessible(db, tdb) && differentString("longTabix", tdb->type)) // FIXME: hgTables.showSchmaLongTabix is a currently a dummy routine, so let's not got here // until it's implemented { char *tbOff = trackDbSetting(tdb, "tableBrowser"); if (isNotEmpty(tbOff) && sameString(nextWord(&tbOff), "off")) { puts("(Download unavailable, see below)"); return FALSE; } char *hint = " title='Open data format (table schema) in new window'"; if (label == NULL) label = " View data format"; struct trackDb *topLevel = trackDbTopLevelSelfOrParent(tdb); printf(SCHEMA_LINKED, db, topLevel->grp, topLevel->track, tdb->table, hint, label); return TRUE; } return FALSE; } char *wgEncodeVocabLink(char *term,char *value,char *title, char *label,char *suffix) // returns allocated string of HTML link to controlled vocabulary term { #define VOCAB_LINK "%s" struct dyString *dyLink = NULL; char *encTerm = cgiEncode(term); char *encValue = cgiEncode(value); dyLink = dyStringCreate(VOCAB_LINK,encTerm,encValue,title,label); if (suffix != NULL) dyStringAppend(dyLink,suffix); // Don't encode since this may contain HTML freeMem(encTerm); freeMem(encValue); return dyStringCannibalize(&dyLink); } char *pairsAsHtmlTable( struct slPair *pairs, struct trackDb *tdb, boolean showLongLabel,boolean showShortLabel) /* Return a string which is an HTML table of the tags for this track. */ { if (pairs == NULL) return ""; struct dyString *dyTable = dyStringCreate(""); // the labels and the metadata pairs all come from trackDb, which a hub controls, escape if (showLongLabel) dyStringPrintf(dyTable,"", htmlEncode(tdb->longLabel)); if (showShortLabel) dyStringPrintf(dyTable,"" "",htmlEncode(tdb->shortLabel)); for(; pairs; pairs = pairs->next) { if (!sameString(pairs->name, "meta") && !isEmpty((char *)pairs->val)) dyStringPrintf(dyTable,"" "",htmlEncode(pairs->name), htmlEncode((char *)pairs->val)); } dyStringAppend(dyTable,"
%s
shortLabel:%s
%s:%s
"); return dyStringCannibalize(&dyTable); } char *metadataAsHtmlTable(char *db,struct trackDb *tdb,boolean showLongLabel,boolean showShortLabel) // If metadata from metaDb exists, return string of html with table definition { struct slPair *pairs = NULL; if ((pairs = trackDbMetaPairs(tdb)) != NULL) return pairsAsHtmlTable(pairs, tdb, showLongLabel, showShortLabel); const struct mdbObj *safeObj = metadataForTable(db,tdb,NULL); if (safeObj == NULL || safeObj->vars == NULL) return NULL; //struct dyString *dyTable = dyStringCreate("",tdb->table); struct dyString *dyTable = dyStringCreate("
"); if (showLongLabel) dyStringPrintf(dyTable,"",tdb->longLabel); if (showShortLabel) dyStringPrintf(dyTable,"" "",tdb->shortLabel); // Get the hash of mdb and cv term types struct hash *cvTermTypes = (struct hash *)cvTermTypeHash(); struct mdbObj *mdbObj = mdbObjClone(safeObj); // Important if we are going to remove vars! // Don't bother showing these mdbObjRemoveVars(mdbObj,MDB_OBJ_TYPE_COMPOSITE " " MDB_VAR_PROJECT " " MDB_OBJ_TYPE " " MDB_VAR_MD5SUM); mdbObjRemoveHiddenVars(mdbObj); mdbObjReorderByCv(mdbObj,FALSE);// Use cv defined order for visible vars struct mdbVar *mdbVar; for (mdbVar=mdbObj->vars;mdbVar!=NULL;mdbVar=mdbVar->next) { if ((sameString(mdbVar->var,MDB_VAR_FILENAME) || sameString(mdbVar->var,MDB_VAR_FILEINDEX) ) && trackDbSettingClosestToHome(tdb,MDB_VAL_ENCODE_PROJECT) != NULL) { dyStringPrintf(dyTable,"" ""); } else { // Don't bother with tableName if (cvTermTypes && differentString(mdbVar->var,MDB_VAR_TABLENAME)) { struct hash *cvTerm = hashFindVal(cvTermTypes,mdbVar->var); if (cvTerm != NULL) // even if cvTerm isn't used, { // it proves that it exists and a link is desirable if (!cvTermIsHidden(mdbVar->var)) { char *label = (char *)cvLabel(NULL,mdbVar->var); char *linkOfType = wgEncodeVocabLink(CV_TYPE,mdbVar->var,label, label,NULL); if (cvTermIsCvDefined(mdbVar->var)) { label = (char *)cvLabel(mdbVar->var,mdbVar->val); char *linkOfTerm = wgEncodeVocabLink(CV_TERM,mdbVar->val,label, label,NULL); dyStringPrintf(dyTable,"", linkOfType,linkOfTerm); freeMem(linkOfTerm); } else dyStringPrintf(dyTable,"", linkOfType,mdbVar->val); freeMem(linkOfType); continue; } } } dyStringPrintf(dyTable,"" "",mdbVar->var,mdbVar->val); } } dyStringAppend(dyTable,"
%s
shortLabel:%s
%s:",mdbVar->var); struct slName *fileSet = slNameListFromComma(mdbVar->val); while (fileSet != NULL) { struct slName *file = slPopHead(&fileSet); dyStringAppend(dyTable,htmlStringForDownloadsLink(db, tdb, file->name, TRUE)); if (fileSet != NULL) dyStringAppend(dyTable,"
"); slNameFree(&file); } dyStringAppend(dyTable,"
" "%s:%s
" "%s:%s
%s:%s
"); //mdbObjsFree(&mdbObj); // spill some memory return dyStringCannibalize(&dyTable); } boolean compositeMetadataToggle(char *db,struct trackDb *tdb,char *title, boolean embeddedInText,boolean showLongLabel) // If metadata from metaTbl exists, create a link that will allow toggling it's display { boolean hasMetaInHub = (trackDbSetting(tdb, "metaDb") != NULL) || (trackDbSetting(tdb, "metaTab") != NULL); if (!hasMetaInHub) { const struct mdbObj *safeObj = metadataForTable(db,tdb,NULL); if (safeObj == NULL || safeObj->vars == NULL) return FALSE; } char id[256]; safef(id, sizeof id, "div_%s_link", tdb->track); printf("%s%s", (embeddedInText?" ":"

"),id,tdb->track, (title?title:"")); jsOnEventByIdF("click", id, "return metadataShowHide(\"%s\",%s,true);", tdb->track, showLongLabel?"true":"false"); printf("

",tdb->track, metadataAsHtmlTable(db,tdb,showLongLabel,FALSE)); return TRUE; } /* Multi-region UI */ boolean makeMultiRegionLink(char *db, struct trackDb *tdb, struct cart *cart) /* Make a link to launch browser in multi-region custom URL mode, based on * track setting. This includes creating a custom track displaying the regions. * The link switches to exit multi-region if browser is already in multi-region mode * based on regions defined for this track. */ { char *regionUrl = trackDbSetting(tdb, "multiRegionsBedUrl"); if (isEmpty(regionUrl)) return FALSE; // make custom track for regions, alternating colors // TODO: truncate CT name and label at word boundary // TODO: fix bedPackDense to work with multi-region // TODO: limit number of regions ? struct dyString *dsCustomText = dyStringCreate( "track name=\'%s ROI\' description=\'[Regions of Interest] %s' " "visibility=dense bedPackDense=on labelOnFeature=on itemRgb=on noScoreFilter=on\n", tdb->shortLabel, tdb->longLabel); #ifdef LATER // TODO: libify struct dyString *ds = NULL; struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { int sd = netUrlOpen(regionUrl); if (sd >= 0) { char *newUrl = NULL; int newSd = 0; if (netSkipHttpHeaderLinesHandlingRedirect(sd, regionUrl, &newSd, &newUrl)) { if (newUrl) /* redirect can modify the url */ { freeMem(newUrl); sd = newSd; } ds = netSlurpFile(sd); close(sd); } } } errCatchEnd(errCatch); if (errCatch->gotError) warn("%s", errCatch->message->string); // how come no warning if bad file ? errCatchFree(&errCatch); #endif // TODO: support $D, etc. in URL int sd = netUrlOpen(regionUrl); if (sd < 0) return FALSE; struct dyString *dsRegionBed = netSlurpFile(sd); close(sd); if (!dsRegionBed) return FALSE; char *regionBedTxt = dyStringCannibalize(&dsRegionBed); // count fields in BED. Accept up to BED9 (user-spec colors) char *bedTxt = cloneString(regionBedTxt); struct lineFile *lf = lineFileOnString(NULL, TRUE, bedTxt); char *words[9]; int bedSize = lineFileChopNext(lf, words, sizeof words); lineFileClose(&lf); freeMem(bedTxt); lf = lineFileOnString(NULL, TRUE, regionBedTxt); // TODO: refactor with interact multi-region static char *colorLight = "184,201,255"; // blue static char *colorDark = "0,0,0"; // black char *color = colorLight; boolean doLightColor = TRUE; int id = 1; char name[100]; char userColor[10]; struct bed *region; struct tempName mrTn; trashDirFile(&mrTn, "hgt", "custRgn_track", ".bed"); FILE *f = fopen(mrTn.forCgi, "w"); if (f == NULL) errAbort("can't create temp file %s", mrTn.forCgi); char regionInfo[1024]; char *regionFile = cloneString(mrTn.forCgi); // TODO: trackDb setting ? #define MULTI_REGION_BED_DEFAULT_PADDING 1000 int padding = MULTI_REGION_BED_DEFAULT_PADDING; safef(regionInfo, sizeof regionInfo, "#padding %d\n", padding); mustWrite(f, regionInfo, strlen(regionInfo)); #ifdef LATER safef(regionInfo, sizeof regionInfo, "#shortDesc %s\n", name); mustWrite(f, regionInfo, strlen(regionInfo)); #endif // write to trash file and custom track int regionCount = 0; while (lineFileChopNext(lf, words, bedSize)) { region = bedLoadN(words, bedSize); if (bedSize < 9) { // assign alternating light/dark color color = doLightColor ? colorLight : colorDark; doLightColor = !doLightColor; } else { struct rgbColor rgb = bedColorToRgb(region->itemRgb); safef(userColor, sizeof userColor, "%d,%d,%d", rgb.r, rgb.g, rgb.b); } if (bedSize < 4) { // region label based on chrom and an item number safef(name, sizeof name, "r%d/%s", id++, region->chrom); } else { strcpy(name, region->name); } // write to trash file safef(regionInfo, sizeof regionInfo, "%s\t%d\t%d\n", region->chrom, region->chromStart, region->chromEnd); mustWrite(f, regionInfo, strlen(regionInfo)); // write to custom track int start = max(region->chromStart - padding, 0); int end = min(region->chromEnd + padding, hChromSize(db, region->chrom)); dyStringPrintf(dsCustomText, "%s\t%d\t%d\t%s\t" "0\t.\t%d\t%d\t%s\n", region->chrom, start, end, name, start, end, color); regionCount++; } lineFileClose(&lf); fclose(f); // create SHA1 file; used to see if file has changed unsigned char hash[SHA_DIGEST_LENGTH]; SHA1((const unsigned char *)regionInfo, strlen(regionInfo), hash); char newSha1[(SHA_DIGEST_LENGTH + 1) * 2]; hexBinaryString(hash, SHA_DIGEST_LENGTH, newSha1, (SHA_DIGEST_LENGTH + 1) * 2); char sha1File[1024]; safef(sha1File, sizeof sha1File, "%s.sha1", mrTn.forCgi); f = mustOpen(sha1File, "w"); mustWrite(f, newSha1, strlen(newSha1)); carefulClose(&f); char customHtml[1000]; safef(customHtml, sizeof customHtml, "

Description

\n" "

This custom track displays regions of interest for the " "%s track.

", db, tdb->track, tdb->shortLabel); // TODO: support #padding in custom regions file enum trackVisibility vis = hTvFromString(cartUsualString(cart, tdb->track, hStringFromTv(tdb->visibility))); if (vis == tvHide) vis = tvDense; printf("

"); printf("" "Display regions of interest (%d)", MULTI_REGION_BED_WIN_FULL, tdb->track, cgiEncode(regionFile), tdb->track, hStringFromTv(vis), CT_CUSTOM_DOC_TEXT_VAR, cgiEncode(customHtml), CT_CUSTOM_TEXT_VAR, cgiEncode(dyStringCannibalize(&dsCustomText)), regionCount); printf(" in multi-region view (custom regions mode)"); printf("   "); printf("(Help)\n"); printf("

"); return TRUE; } static void printDownloadUrl(char *downloadUrl, char *database, char *track) /* given a string