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 @@ -1,10370 +1,10469 @@ /* 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 "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 "hicUi.h" #include "bigDbSnp.h" #include "customComposite.h" #include "trackVersion.h" #include "hubConnect.h" #include "bigBedFilter.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 "add" #define CLEAR_BUTTON_LABEL "clear" #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")) 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 *file,char *term,char *value,char *title, char *label,char *suffix) // returns allocated string of HTML link to controlled vocabulary term { #define VOCAB_LINK_WITH_FILE "%s" #define VOCAB_LINK "%s" struct dyString *dyLink = NULL; char *encTerm = cgiEncode(term); char *encValue = cgiEncode(value); if (file != NULL) { char *encFile = cgiEncode(file); dyLink = dyStringCreate(VOCAB_LINK_WITH_FILE,encFile,encTerm,encValue,title,label); freeMem(encFile); } else 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(""); if (showLongLabel) dyStringPrintf(dyTable,"",tdb->longLabel); if (showShortLabel) dyStringPrintf(dyTable,"" "",tdb->shortLabel); for(; pairs; pairs = pairs->next) { if (!sameString(pairs->name, "meta") && !isEmpty((char *)pairs->val)) dyStringPrintf(dyTable,"" "",pairs->name, (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(NULL,CV_TYPE,mdbVar->var,label, label,NULL); if (cvTermIsCvDefined(mdbVar->var)) { label = (char *)cvLabel(mdbVar->var,mdbVar->val); char *linkOfTerm = wgEncodeVocabLink(NULL,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