66ea6cb4eaf2036e464be55b295765ed5105a0fb
max
Wed Apr 22 09:42:25 2026 -0700
hgTrackUi/hui: render filter UI on supertrack configuration pages
Supertracks (group of tracks with superTrack on, no data of their own)
previously had no way to expose a shared filter: their trackDb stanza
can declare filter.* / filterByRange.* / filterValues.*, but those
settings were never drawn on the supertrack's hgTrackUi page. So users
had to open each subtrack's own configuration page and set the same
filter there, and the "lrSv.filter.svLen" cart namespace went unused.
This change wires that up:
- hgTrackUi.c (superTrackUi): after listing the subtracks, if the
supertrack tdb declares any filter.* settings call scoreCfgUi() to
render the standard filter UI. Cart variables land under the
supertrack's own name (e.g. "lrSv.filter.svLen.min"), and subtracks
already inherit them through cartOptionalStringClosestToHome()
walking tdb->parent. Subtrack-level values continue to override.
- hui.c:
- buildFilterBy() / filterByValues(): tolerate a NULL autoSql object,
so supertracks (which have no data table) don't errAbort when they
declare filterValues.* of virtual aggregated fields. Missing-field
errAbort still fires in the normal subtrack case.
- scoreCfgUi() / cfgByCfgType(): when called with title == NULL (the
supertrack filter path), suppress the default "
" separator and
the " " between the title bar and the filter block; the caller
renders its own section heading.
- asForTdb(): handle conn == NULL by returning NULL rather than
crashing, since supertrack filter rendering has no associated
sqlConnection.
refs #37426
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index cac6a74ed83..9ce7959c8b6 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -1,10696 +1,10710 @@
/* 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 "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 "\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 "\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(" "
"Top%s",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("
");
if (showLongLabel)
dyStringPrintf(dyTable,"
%s
",tdb->longLabel);
if (showShortLabel)
dyStringPrintf(dyTable,"
",tdb->longLabel);
if (showShortLabel)
dyStringPrintf(dyTable,"
shortLabel:
"
"
%s
",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,"
",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
\n");
freeMem(cg->align);
freez(pCg);
}
}
/****** Some stuff for hide/dense/full controls ******/
static char *hTvStrings[] =
/* User interface strings for track visibility controls. */
{
"hide",
"dense",
"full",
"pack",
"squish",
"show"
};
enum trackVisibility hTvFromStringNoAbort(char *s)
// Given a string representation of track visibility, return as equivalent enum.
{
int vis = stringArrayIx(s, hTvStrings, ArraySize(hTvStrings));
if (vis < 0)
{
vis = 0; // don't generate bogus value on invalid input
}
return vis;
}
enum trackVisibility hTvFromString(char *s)
// Given a string representation of track visibility, return as equivalent enum.
{
enum trackVisibility vis = hTvFromStringNoAbort(s);
if ((int)vis < 0)
errAbort("Unknown visibility %s. Use one of: hide, dense, squish, pack, full.", s);
return vis;
}
char *hStringFromTv(enum trackVisibility vis)
// Given enum representation convert to string.
{
return hTvStrings[vis];
}
void hTvDropDownClassWithJavascript(char *varName, char *id, enum trackVisibility vis, boolean canPack,
char *class, struct slPair *events)
// Make track visibility drop down for varName with style class
{
static char *noPack[] =
{
"hide",
"dense",
"full",
};
static char *pack[] =
{
"hide",
"dense",
"squish",
"pack",
"full",
};
static int packIx[] = {tvHide,tvDense,tvSquish,tvPack,tvFull};
if (canPack)
cgiMakeDropListClassWithIdStyleAndJavascript(varName, id, pack, ArraySize(pack),
pack[packIx[vis]], class, TV_DROPDOWN_STYLE,
events);
else
cgiMakeDropListClassWithIdStyleAndJavascript(varName, id, noPack, ArraySize(noPack),
noPack[vis], class, TV_DROPDOWN_STYLE, events);
}
static char *denseOnly[] =
{
"hide",
"dense",
NULL
};
static char *squishOnly[] =
{
"hide",
"squish",
NULL
};
static char *packOnly[] =
{
"hide",
"pack",
NULL
};
static char *fullOnly[] =
{
"hide",
"full",
NULL
};
static char *noPack[] =
{
"hide",
"dense",
"full",
NULL
};
static char *pack[] =
{
"hide",
"dense",
"squish",
"pack",
"full",
NULL
};
char ** hTvGetVizArr(enum trackVisibility vis, boolean canPack, char* visOnly)
/* return a NULL-terminated array of char* with possible track visibilities */
{
if (visOnly != NULL)
{
if (sameWord(visOnly,"dense"))
return denseOnly;
else if (sameWord(visOnly,"squish"))
return squishOnly;
else if (sameWord(visOnly,"pack"))
return packOnly;
else if (sameWord(visOnly,"full"))
return fullOnly;
else /* default when not recognized */
return denseOnly;
}
else
{
if (canPack)
return pack;
else
return noPack;
}
}
void hTvDropDownClassVisOnlyAndExtraWithLabel(char *varName, enum trackVisibility vis,
boolean canPack, char *class, char *visOnly, struct slPair *events,
char *label)
// Make track visibility drop down for varName with style class, optional aria-label,
// and potentially limited to visOnly
{
char** vizArr = hTvGetVizArr(vis, canPack, visOnly);
char* checked = vizArr[vis];
int vizArrLen = arrNullLen(vizArr);
// Same as hTvDropDownClassWithJavascript():
// Normal track with no special limits needs mapping to get back checked value
static int packIx[] = {tvHide,tvDense,tvSquish,tvPack,tvFull};
if (visOnly==NULL && canPack)
checked = vizArr[packIx[vis]];
cgiMakeDropListClassWithIdStyleJavascriptAndLabel(varName, NULL, vizArr, vizArrLen, checked, class, TV_DROPDOWN_STYLE, events, label);
}
void hTvDropDownClassVisOnlyAndExtra(char *varName, enum trackVisibility vis,
boolean canPack, char *class, char *visOnly, struct slPair *events)
// Make track visibility drop down for varName with style class, and potentially limited to visOnly
{
hTvDropDownClassVisOnlyAndExtraWithLabel(varName, vis, canPack, class, visOnly, events, NULL);
}
void hideShowDropDownWithClassExtraAndLabel(char *varName, char *id, boolean show, char *class,
struct slPair *events, char *ariaLabel)
// Make hide/show dropdown for varName with optional aria-label
{
static char *hideShow[] =
{
"hide",
"show"
};
cgiMakeDropListClassWithIdStyleJavascriptAndLabel(varName, id, hideShow, ArraySize(hideShow),
hideShow[show], class, TV_DROPDOWN_STYLE, events, ariaLabel);
}
void hideShowDropDownWithClassAndExtra(char *varName, char *id, boolean show, char *class, struct slPair *events)
// Make hide/show dropdown for varName
{
hideShowDropDownWithClassExtraAndLabel(varName, id, show, class, events, NULL);
}
/****** Some stuff for stsMap related controls *******/
static char *stsMapOptions[] =
{
"All Genetic",
"Genethon",
"Marshfield",
"deCODE",
"GeneMap 99",
"Whitehead YAC",
"Whitehead RH",
"Stanford TNG",
};
enum stsMapOptEnum smoeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, stsMapOptions);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *smoeEnumToString(enum stsMapOptEnum x)
/* Convert from enum to string representation. */
{
return stsMapOptions[x];
}
void smoeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, stsMapOptions, ArraySize(stsMapOptions),
curVal);
}
/****** Some stuff for stsMapMouseNew related controls *******/
static char *stsMapMouseOptions[] =
{
"All Genetic",
"WICGR Genetic Map",
"MGD Genetic Map",
"RH",
};
enum stsMapMouseOptEnum smmoeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, stsMapMouseOptions);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *smmoeEnumToString(enum stsMapMouseOptEnum x)
/* Convert from enum to string representation. */
{
return stsMapMouseOptions[x];
}
void smmoeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, stsMapMouseOptions, ArraySize(stsMapMouseOptions),
curVal);
}
/****** Some stuff for stsMapRat related controls *******/
static char *stsMapRatOptions[] =
{
"All Genetic",
"FHHxACI",
"SHRSPxBN",
"RH",
};
enum stsMapRatOptEnum smroeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, stsMapRatOptions);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *smroeEnumToString(enum stsMapRatOptEnum x)
/* Convert from enum to string representation. */
{
return stsMapRatOptions[x];
}
void smroeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, stsMapRatOptions, ArraySize(stsMapRatOptions),
curVal);
}
/****** Some stuff for fishClones related controls *******/
static char *fishClonesOptions[] =
{
"Fred Hutchinson CRC",
"National Cancer Institute",
"Sanger Centre",
"Roswell Park Cancer Institute",
"Cedars-Sinai Medical Center",
"Los Alamos National Lab",
"UC San Francisco",
};
enum fishClonesOptEnum fcoeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, fishClonesOptions);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *fcoeEnumToString(enum fishClonesOptEnum x)
/* Convert from enum to string representation. */
{
return fishClonesOptions[x];
}
void fcoeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, fishClonesOptions, ArraySize(fishClonesOptions),
curVal);
}
/****** Some stuff for recombRate related controls *******/
static char *recombRateOptions[] =
{
"deCODE Sex Averaged Distances",
"deCODE Female Distances",
"deCODE Male Distances",
"Marshfield Sex Averaged Distances",
"Marshfield Female Distances",
"Marshfield Male Distances",
"Genethon Sex Averaged Distances",
"Genethon Female Distances",
"Genethon Male Distances",
};
enum recombRateOptEnum rroeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, recombRateOptions);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *rroeEnumToString(enum recombRateOptEnum x)
/* Convert from enum to string representation. */
{
return recombRateOptions[x];
}
void rroeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, recombRateOptions, ArraySize(recombRateOptions),
curVal);
}
/****** Some stuff for recombRateRat related controls *******/
static char *recombRateRatOptions[] =
{
"SHRSPxBN Sex Averaged Distances",
"FHHxACI Sex Averaged Distances",
};
enum recombRateRatOptEnum rrroeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, recombRateRatOptions);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *rrroeEnumToString(enum recombRateRatOptEnum x)
/* Convert from enum to string representation. */
{
return recombRateRatOptions[x];
}
void rrroeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, recombRateRatOptions, ArraySize(recombRateRatOptions),
curVal);
}
/****** Some stuff for recombRateMouse related controls *******/
static char *recombRateMouseOptions[] =
{
"WI Genetic Map Sex Averaged Distances",
"MGD Genetic Map Sex Averaged Distances",
};
enum recombRateMouseOptEnum rrmoeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, recombRateMouseOptions);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *rrmoeEnumToString(enum recombRateMouseOptEnum x)
/* Convert from enum to string representation. */
{
return recombRateMouseOptions[x];
}
void rrmoeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, recombRateMouseOptions, ArraySize(recombRateMouseOptions),
curVal);
}
/****** Some stuff for CGH NCI60 related controls *******/
static char *cghNci60Options[] =
{
"Tissue Averages",
"BREAST",
"CNS",
"COLON",
"LEUKEMIA",
"LUNG",
"MELANOMA",
"OVARY",
"PROSTATE",
"RENAL",
"All Cell Lines",
};
enum cghNci60OptEnum cghoeStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, cghNci60Options);
if (x < 0)
errAbort("Unknown option %s", string);
return x;
}
char *cghoeEnumToString(enum cghNci60OptEnum x)
/* Convert from enum to string representation. */
{
return cghNci60Options[x];
}
void cghoeDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, cghNci60Options, ArraySize(cghNci60Options),
curVal);
}
/****** Some stuff for nci60 related controls *******/
static char *nci60Options[] =
{
"Tissue Averages",
"All Cell Lines",
"BREAST",
"CNS",
"COLON",
"LEUKEMIA",
"MELANOMA",
"OVARIAN",
"PROSTATE",
"RENAL",
"NSCLC",
"DUPLICATE",
"UNKNOWN"
};
enum nci60OptEnum nci60StringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, nci60Options);
if (x < 0)
errAbort("hui::nci60StringToEnum() - Unknown option %s", string);
return x;
}
char *nci60EnumToString(enum nci60OptEnum x)
/* Convert from enum to string representation. */
{
return nci60Options[x];
}
void nci60DropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, nci60Options, ArraySize(nci60Options),
curVal);
}
/*** Control of base/codon coloring code: ***/
/* All options (parallel to enum baseColorDrawOpt): */
static char *baseColorDrawAllOptionLabels[] =
{
BASE_COLOR_DRAW_OFF_LABEL,
BASE_COLOR_DRAW_GENOMIC_CODONS_LABEL,
BASE_COLOR_DRAW_ITEM_CODONS_LABEL,
BASE_COLOR_DRAW_DIFF_CODONS_LABEL,
BASE_COLOR_DRAW_ITEM_BASES_CDS_LABEL,
BASE_COLOR_DRAW_DIFF_BASES_CDS_LABEL,
};
static char *baseColorDrawAllOptionValues[] =
{
BASE_COLOR_DRAW_OFF,
BASE_COLOR_DRAW_GENOMIC_CODONS,
BASE_COLOR_DRAW_ITEM_CODONS,
BASE_COLOR_DRAW_DIFF_CODONS,
BASE_COLOR_DRAW_ITEM_BASES,
BASE_COLOR_DRAW_DIFF_BASES,
};
/* Subset of options for tracks with CDS info but not item sequence: */
static char *baseColorDrawGenomicOptionLabels[] =
{
BASE_COLOR_DRAW_OFF_LABEL,
BASE_COLOR_DRAW_GENOMIC_CODONS_LABEL,
};
static char *baseColorDrawGenomicOptionValues[] =
{
BASE_COLOR_DRAW_OFF,
BASE_COLOR_DRAW_GENOMIC_CODONS,
};
/* Subset of options for tracks with aligned item sequence but not CDS: */
static char *baseColorDrawItemOptionLabels[] =
{
BASE_COLOR_DRAW_OFF_LABEL,
BASE_COLOR_DRAW_ITEM_BASES_NC_LABEL,
BASE_COLOR_DRAW_DIFF_BASES_NC_LABEL,
};
static char *baseColorDrawItemOptionValues[] =
{
BASE_COLOR_DRAW_OFF,
BASE_COLOR_DRAW_ITEM_BASES,
BASE_COLOR_DRAW_DIFF_BASES,
};
enum baseColorDrawOpt baseColorDrawOptStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, baseColorDrawAllOptionValues);
if (x < 0)
errAbort("hui::baseColorDrawOptStringToEnum() - Unknown option %s", string);
return x;
}
static boolean baseColorGotCds(struct trackDb *tdb)
/* Return true if this track has CDS info according to tdb (or is genePred). */
{
boolean gotIt = FALSE;
char *setting = trackDbSetting(tdb, BASE_COLOR_USE_CDS);
if (isNotEmpty(setting))
{
if (sameString(setting, "all") || sameString(setting, "given") ||
sameString(setting, "genbank") || startsWith("table", setting))
gotIt = TRUE;
else if (! sameString(setting, "none"))
errAbort("trackDb for %s, setting %s: unrecognized value \"%s\". "
"must be one of {none, all, given, genbank, table}.",
tdb->track, BASE_COLOR_USE_CDS, setting);
}
else if (startsWith("genePred", tdb->type) || startsWith("bigGenePred", tdb->type))
gotIt = TRUE;
return gotIt;
}
static boolean baseColorGotSequence(struct trackDb *tdb)
/* Return true if this track has aligned sequence according to tdb. */
{
boolean gotIt = FALSE;
char *setting = trackDbSetting(tdb, BASE_COLOR_USE_SEQUENCE);
if (isNotEmpty(setting))
{
if (sameString(setting, "genbank") || sameString(setting, "seq") ||
sameString(setting, "ss") || startsWith("extFile", setting) ||
sameString(setting, "hgPcrResult") || sameString(setting, "nameIsSequence") ||
sameString(setting, "seq1Seq2") || sameString(setting, "lfExtra") ||
sameString(setting, "lrg") || sameString(setting, "2bit") ||
startsWith("table ", setting) || startsWithWord("db", setting))
gotIt = TRUE;
else if (differentString(setting, "none"))
errAbort("trackDb for %s, setting %s: unrecognized value \"%s\". "
"must be one of {none, genbank, seq, ss, extFile, nameIsSequence, seq1Seq2,"
"hgPcrResult, lfExtra, lrg, 2bit, table table}.",
tdb->track, BASE_COLOR_USE_SEQUENCE, setting);
}
return gotIt;
}
static void baseColorDropLists(struct cart *cart, struct trackDb *tdb, char *name)
/* draw the baseColor drop list options */
{
enum baseColorDrawOpt curOpt = baseColorDrawOptEnabled(cart, tdb);
char *curValue = baseColorDrawAllOptionValues[curOpt];
char var[512];
safef(var, sizeof(var), "%s." BASE_COLOR_VAR_SUFFIX, name);
boolean gotCds = baseColorGotCds(tdb);
boolean gotSeq = baseColorGotSequence(tdb);
if (gotCds && gotSeq)
{
puts("
Color track by codons or bases:");
cgiMakeDropListFull(var, baseColorDrawAllOptionLabels,
baseColorDrawAllOptionValues,
ArraySize(baseColorDrawAllOptionLabels),
curValue, NULL, NULL);
printf("Help on mRNA coloring ",
CDS_MRNA_HELP_PAGE);
}
else if (gotCds)
{
char buf[256];
char *disabled = NULL;
safef(buf, sizeof(buf), "codonColoringChanged('%s');", name);
puts("
Color track by codons:");
cgiMakeDropListFull(var, baseColorDrawGenomicOptionLabels,
baseColorDrawGenomicOptionValues,
ArraySize(baseColorDrawGenomicOptionLabels),
curValue, "change", buf);
printf("Help on codon coloring ",
CDS_HELP_PAGE);
safef(buf, sizeof(buf), "%s.%s", name, CODON_NUMBERING_SUFFIX);
if (curOpt == baseColorDrawOff)
disabled = "disabled";
printf(" Show codon numbering:\n",
name, curOpt == baseColorDrawOff ? "class='disabled'" : ""); cgiMakeCheckBoxMore(buf, cartUsualBooleanClosestToHome(cart, tdb, FALSE, CODON_NUMBERING_SUFFIX, TRUE), disabled);
}
else if (gotSeq)
{
puts("
Color track by bases:");
cgiMakeDropListFull(var, baseColorDrawItemOptionLabels,
baseColorDrawItemOptionValues,
ArraySize(baseColorDrawItemOptionLabels),
curValue, NULL, NULL);
printf("Help on base coloring ",
CDS_BASE_HELP_PAGE);
}
}
void baseColorDrawOptDropDown(struct cart *cart, struct trackDb *tdb)
/* Make appropriately labeled drop down of options if any are applicable.*/
{
baseColorDropLists(cart, tdb, tdb->track);
}
static enum baseColorDrawOpt limitDrawOptForType(struct trackDb *tdb, enum baseColorDrawOpt drawOpt)
/* If tdb->type is genePred, but something fancier like mRNA codons is enabled because the setting
* is coming from a view that also includes a PSL track, downgrade it to genomic codons to avoid
* drawing problems caused by the inappropriate setting. #21194 */
{
if (startsWith("genePred", tdb->type) && drawOpt > baseColorDrawGenomicCodons)
drawOpt = baseColorDrawGenomicCodons;
return drawOpt;
}
enum baseColorDrawOpt baseColorDrawOptEnabled(struct cart *cart,
struct trackDb *tdb)
/* Query cart & trackDb to determine what drawing mode (if any) is enabled. */
{
char *stringVal = NULL;
assert(cart);
assert(tdb);
/* trackDb can override default of OFF; cart can override trackDb. */
stringVal = trackDbSettingClosestToHomeOrDefault(tdb, BASE_COLOR_DEFAULT,
BASE_COLOR_DRAW_OFF);
stringVal = cartUsualStringClosestToHome(cart, tdb, FALSE, BASE_COLOR_VAR_SUFFIX,stringVal);
return limitDrawOptForType(tdb, baseColorDrawOptStringToEnum(stringVal));
}
/*** Control of fancy indel display code: ***/
static boolean tdbOrCartBoolean(struct cart *cart, struct trackDb *tdb,
char *settingName, char *defaultOnOff)
/* Query cart & trackDb to determine if a boolean variable is set. */
{
boolean alreadySet;
alreadySet = !sameString("off",trackDbSettingOrDefault(tdb, settingName, defaultOnOff));
alreadySet = cartUsualBooleanClosestToHome(cart, tdb, FALSE, settingName, alreadySet);
// NOTE: parentLevel=FALSE because tdb param already is at appropriate level
return alreadySet;
}
static boolean indelAppropriate(struct trackDb *tdb)
/* Return true if it makes sense to offer indel display options for tdb. */
{
return (tdb && (startsWith("psl", tdb->type) || startsWith("bigPsl", tdb->type) ||
startsWithWord("chain", tdb->type) || startsWithWord("bigChain", tdb->type) ||
sameString("bam", tdb->type) || sameString("lrg", tdb->track)));
}
static void indelEnabledByName(struct cart *cart, struct trackDb *tdb, char *name,
float basesPerPixel, boolean *retDoubleInsert, boolean *retQueryInsert,
boolean *retPolyA)
/* Query cart & trackDb to determine what indel display (if any) is enabled. Set
* basesPerPixel to 0.0 to disable check for zoom level. */
{
struct trackDb *tdbLevel = tdb;
if (differentString(tdb->track, name) && tdb->parent != NULL)
tdbLevel = tdb->parent;
boolean apropos = indelAppropriate(tdb);
if (apropos && (basesPerPixel > 0.0))
{
// check indel max zoom
float showIndelMaxZoom = trackDbFloatSettingOrDefault(tdbLevel, "showIndelMaxZoom", -1.0);
if ((showIndelMaxZoom >= 0)
&& ((basesPerPixel > showIndelMaxZoom) || (showIndelMaxZoom == 0.0)))
apropos = FALSE;
}
if (retDoubleInsert)
*retDoubleInsert = apropos && tdbOrCartBoolean(cart, tdbLevel, INDEL_DOUBLE_INSERT, "off");
if (retQueryInsert)
*retQueryInsert = apropos && tdbOrCartBoolean(cart, tdbLevel, INDEL_QUERY_INSERT, "off");
if (retPolyA)
*retPolyA = apropos && tdbOrCartBoolean(cart, tdbLevel, INDEL_POLY_A, "off");
}
void indelEnabled(struct cart *cart, struct trackDb *tdb, float basesPerPixel,
boolean *retDoubleInsert, boolean *retQueryInsert,
boolean *retPolyA)
/* Query cart & trackDb to determine what indel display (if any) is enabled. Set
* basesPerPixel to 0.0 to disable check for zoom level. */
{
indelEnabledByName(cart,tdb,tdb->track,basesPerPixel,retDoubleInsert,retQueryInsert,retPolyA);
}
static void indelShowOptionsWithNameExt(struct cart *cart, struct trackDb *tdb, char *name,
char *queryTerm,
boolean includeDoubleInsert, boolean includePolyA)
/* Make HTML inputs for indel display options if any are applicable. */
{
if (indelAppropriate(tdb))
{
boolean showDoubleInsert, showQueryInsert, showPolyA;
char var[512];
indelEnabledByName(cart, tdb, name, 0.0, &showDoubleInsert, &showQueryInsert, &showPolyA);
printf("
Draw a vertical purple line for an insertion at the beginning or "
"end of the %s, orange for insertion in the middle of the %s
\n"
"
", queryTerm, queryTerm);
if (includePolyA)
{
safef(var, sizeof(var), "%s.%s", name, INDEL_POLY_A);
/* We can highlight valid polyA's only if we have query sequence --
* so indelPolyA code piggiebacks on baseColor code: */
if (baseColorGotSequence(tdb))
{
cgiMakeCheckBox(var, showPolyA);
printf("
Draw a vertical green line where %s has a polyA tail "
"insertion
\n", queryTerm);
}
}
printf("
\n");
}
}
static void indelShowOptionsWithName(struct cart *cart, struct trackDb *tdb, char *name)
/* Make HTML inputs for indel display options if any are applicable. */
{
indelShowOptionsWithNameExt(cart, tdb, name, "query", TRUE, TRUE);
}
void indelShowOptions(struct cart *cart, struct trackDb *tdb)
/* Make HTML inputs for indel display options if any are applicable. */
{
indelShowOptionsWithName(cart, tdb, tdb->track);
}
#define BAM_DEFAULT_SHOW_DIFF_BASES_MAX_ZOOM "100"
void bamAddBaseAndIndelSettings(struct trackDb *tdb)
/* Unless already set in tdb, add settings to enable base-level differences and indel display. */
{
struct hash *settings = tdb->settingsHash;
if (!hashLookup(settings, BASE_COLOR_USE_SEQUENCE))
hashAdd(settings, BASE_COLOR_USE_SEQUENCE, cloneString("lfExtra"));
if (!hashLookup(settings, BASE_COLOR_DEFAULT))
hashAdd(settings, BASE_COLOR_DEFAULT, cloneString(BASE_COLOR_DRAW_DIFF_BASES));
if (!hashLookup(settings, SHOW_DIFF_BASES_ALL_SCALES))
hashAdd(settings, SHOW_DIFF_BASES_ALL_SCALES, cloneString("."));
if (!hashLookup(settings, INDEL_DOUBLE_INSERT))
hashAdd(settings, INDEL_DOUBLE_INSERT, cloneString("on"));
if (!hashLookup(settings, INDEL_QUERY_INSERT))
hashAdd(settings, INDEL_QUERY_INSERT, cloneString("on"));
if (!hashLookup(settings, INDEL_POLY_A))
hashAdd(settings, INDEL_POLY_A, cloneString("off"));
if (!hashLookup(settings, "showDiffBasesMaxZoom"))
hashAdd(settings, "showDiffBasesMaxZoom", cloneString(BAM_DEFAULT_SHOW_DIFF_BASES_MAX_ZOOM));
}
/****** base position (ruler) controls *******/
static char *zoomOptions[] =
{
ZOOM_1PT5X,
ZOOM_3X,
ZOOM_10X,
ZOOM_100X,
ZOOM_BASE
};
void zoomRadioButtons(char *var, char *curVal)
/* Make a list of radio buttons for all zoom options */
{
int i;
int size = ArraySize(zoomOptions);
for (i = 0; i < size; i++)
{
char *s = zoomOptions[i];
cgiMakeRadioButton(var, s, sameString(s, curVal));
printf(" %s ", s);
}
}
/****** Some stuff for affy related controls *******/
static char *affyOptions[] =
{
"Chip Type",
"Chip ID",
"Tissue Averages"
};
enum affyOptEnum affyStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, affyOptions);
if (x < 0)
errAbort("hui::affyStringToEnum() - Unknown option %s", string);
return x;
}
char *affyEnumToString(enum affyOptEnum x)
/* Convert from enum to string representation. */
{
return affyOptions[x];
}
void affyDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, affyOptions, ArraySize(affyOptions),
curVal);
}
/****** Some stuff for affy all exon related controls *******/
static char *affyAllExonOptions[] =
{
"Chip",
"Tissue Averages"
};
enum affyAllExonOptEnum affyAllExonStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, affyAllExonOptions);
if (x < 0)
errAbort("hui::affyAllExonStringToEnum() - Unknown option %s", string);
return x;
}
char *affyAllExonEnumToString(enum affyAllExonOptEnum x)
/* Convert from enum to string representation. */
{
return affyAllExonOptions[x];
}
void affyAllExonDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, affyAllExonOptions, ArraySize(affyAllExonOptions),
curVal);
}
/****** Some stuff for Rosetta related controls *******/
static char *rosettaOptions[] =
{
"All Experiments",
"Common Reference and Other",
"Common Reference",
"Other Exps"
};
static char *rosettaExonOptions[] =
{
"Confirmed Only",
"Predicted Only",
"All",
};
enum rosettaOptEnum rosettaStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, rosettaOptions);
if (x < 0)
errAbort("hui::rosettaStringToEnum() - Unknown option %s", string);
return x;
}
char *rosettaEnumToString(enum rosettaOptEnum x)
/* Convert from enum to string representation. */
{
return rosettaOptions[x];
}
void rosettaDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, rosettaOptions, ArraySize(rosettaOptions),
curVal);
}
enum rosettaExonOptEnum rosettaStringToExonEnum(char *string)
/* Convert from string to enum representation of exon types. */
{
int x = stringIx(string, rosettaExonOptions);
if (x < 0)
errAbort("hui::rosettaStringToExonEnum() - Unknown option %s", string);
return x;
}
char *rosettaExonEnumToString(enum rosettaExonOptEnum x)
/* Convert from enum to string representation of exon types. */
{
return rosettaExonOptions[x];
}
void rosettaExonDropDown(char *var, char *curVal)
/* Make drop down of exon type options. */
{
cgiMakeDropList(var, rosettaExonOptions, ArraySize(rosettaExonOptions), curVal);
}
/****** Options for the net track level display options *******/
static char *netLevelOptions[] =
{
NET_LEVEL_0,
NET_LEVEL_1,
NET_LEVEL_2,
NET_LEVEL_3,
NET_LEVEL_4,
NET_LEVEL_5,
NET_LEVEL_6
};
enum netLevelEnum netLevelStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, netLevelOptions);
if (x < 0)
errAbort("hui::netLevelStringToEnum() - Unknown option %s", string);
return x;
}
char *netLevelEnumToString(enum netLevelEnum x)
/* Convert from enum to string representation. */
{
return netLevelOptions[x];
}
void netLevelDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, netLevelOptions, ArraySize(netLevelOptions), curVal);
}
/****** Options for the net track color options *******/
static char *netColorOptions[] =
{
CHROM_COLORS,
GRAY_SCALE
};
enum netColorEnum netColorStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, netColorOptions);
if (x < 0)
errAbort("hui::netColorStringToEnum() - Unknown option %s", string);
return x;
}
char *netColorEnumToString(enum netColorEnum x)
/* Convert from enum to string representation. */
{
return netColorOptions[x];
}
void netColorDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, netColorOptions, ArraySize(netColorOptions), curVal);
}
/****** Options for the chain track color options *******/
static char *chainColorOptions[] =
{
CHROM_COLORS,
SCORE_COLORS,
NO_COLORS
};
enum chainColorEnum chainColorStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, chainColorOptions);
if (x < 0)
errAbort("hui::chainColorStringToEnum() - Unknown option %s", string);
return x;
}
char *chainColorEnumToString(enum chainColorEnum x)
/* Convert from enum to string representation. */
{
return chainColorOptions[x];
}
void chainColorDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, chainColorOptions, ArraySize(chainColorOptions), curVal);
}
/****** Options for the wiggle track Windowing *******/
static char *wiggleWindowingOptions[] =
{
"mean+whiskers",
"maximum",
"mean",
"minimum",
"sum",
};
enum wiggleWindowingEnum wiggleWindowingStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleWindowingOptions);
if (x < 0)
errAbort("hui::wiggleWindowingStringToEnum() - Unknown option %s", string);
return x;
}
char *wiggleWindowingEnumToString(enum wiggleWindowingEnum x)
/* Convert from enum to string representation. */
{
return wiggleWindowingOptions[x];
}
void wiggleWindowingDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleWindowingOptions, ArraySize(wiggleWindowingOptions),
curVal);
}
/****** Options for the wiggle track Smoothing *******/
static char *wiggleSmoothingOptions[] =
{
"OFF", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",
"12", "13", "14", "15", "16"
};
enum wiggleSmoothingEnum wiggleSmoothingStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleSmoothingOptions);
if (x < 0)
errAbort("hui::wiggleSmoothingStringToEnum() - Unknown option %s", string);
return x;
}
char *wiggleSmoothingEnumToString(enum wiggleSmoothingEnum x)
/* Convert from enum to string representation. */
{
return wiggleSmoothingOptions[x];
}
void wiggleSmoothingDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleSmoothingOptions, ArraySize(wiggleSmoothingOptions),
curVal);
}
/****** Options for the wiggle track y Line Mark On/Off *******/
static char *wiggleYLineMarkOptions[] =
{
"OFF",
"ON"
};
enum wiggleYLineMarkEnum wiggleYLineMarkStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleYLineMarkOptions);
if (x < 0)
errAbort("hui::wiggleYLineMarkStringToEnum() - Unknown option %s", string);
return x;
}
char *wiggleYLineMarkEnumToString(enum wiggleYLineMarkEnum x)
/* Convert from enum to string representation. */
{
return wiggleYLineMarkOptions[x];
}
void wiggleYLineMarkDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleYLineMarkOptions, ArraySize(wiggleYLineMarkOptions),
curVal);
}
/****** Options for the wiggle track AutoScale *******/
static char *wiggleScaleOptionsParent[] =
{
"use vertical viewing range setting",
"auto-scale to data view",
"group auto-scale"
};
static char *wiggleScaleOptions[] =
{
"use vertical viewing range setting",
"auto-scale to data view"
};
enum wiggleScaleOptEnum wiggleScaleStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleScaleOptionsParent);
if (x < 0)
errAbort("hui::wiggleScaleStringToEnum() - Unknown option %s", string);
return x;
}
char *wiggleScaleEnumToString(enum wiggleScaleOptEnum x)
/* Convert from enum to string representation. */
{
return wiggleScaleOptionsParent[x];
}
void wiggleScaleDropDownParent(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleScaleOptionsParent, ArraySize(wiggleScaleOptionsParent),
curVal);
}
void wiggleScaleDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleScaleOptions, ArraySize(wiggleScaleOptions),
curVal);
}
/****** Options for the wiggle track type of graph *******/
static char *wiggleGraphOptions[] =
{
"points",
"bar",
};
enum wiggleGraphOptEnum wiggleGraphStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleGraphOptions);
if (x < 0)
errAbort("hui::wiggleGraphStringToEnum() - Unknown option %s", string);
return x;
}
char *wiggleGraphEnumToString(enum wiggleGraphOptEnum x)
/* Convert from enum to string representation. */
{
return wiggleGraphOptions[x];
}
void wiggleGraphDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleGraphOptions, ArraySize(wiggleGraphOptions), curVal);
}
static char *aggregateExtraLabels[] =
{
"none",
"transparent",
"solid",
"stacked",
"add",
"subtract",
};
static char *aggregateExtraValues[] =
{
WIG_AGGREGATE_NONE,
WIG_AGGREGATE_TRANSPARENT,
WIG_AGGREGATE_SOLID,
WIG_AGGREGATE_STACKED,
WIG_AGGREGATE_ADD,
WIG_AGGREGATE_SUBTRACT,
};
static char *aggregateLabels[] =
{
"none",
"transparent",
"solid",
"stacked",
};
static char *aggregateValues[] =
{
WIG_AGGREGATE_NONE,
WIG_AGGREGATE_TRANSPARENT,
WIG_AGGREGATE_SOLID,
WIG_AGGREGATE_STACKED,
};
char *wiggleAggregateFunctionEnumToString(enum wiggleAggregateFunctionEnum x)
/* Convert from enum to string representation. */
{
return aggregateValues[x];
}
enum wiggleAggregateFunctionEnum wiggleAggregateFunctionStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, aggregateExtraValues);
if (x < 0)
errAbort("hui::wiggleAggregateFunctionStringToEnum() - Unknown option %s", string);
return x;
}
void aggregateExtraDropDown(char *var, char *curVal)
/* Make drop down menu for aggregate plus strategy */
{
cgiMakeDropListFull(var, aggregateExtraLabels, aggregateExtraValues,
ArraySize(aggregateExtraValues), curVal, NULL, NULL);
}
void aggregateDropDown(char *var, char *curVal)
/* Make drop down menu for aggregate strategy */
{
cgiMakeDropListFull(var, aggregateLabels, aggregateValues,
ArraySize(aggregateValues), curVal, NULL, NULL);
}
static char *viewFuncLabels[] =
{
"show all",
"add all",
"subtract from the first",
};
static char *viewFuncValues[] =
{
WIG_VIEWFUNC_SHOW_ALL,
WIG_VIEWFUNC_ADD_ALL,
WIG_VIEWFUNC_SUBTRACT_ALL,
};
char *wiggleViewFuncEnumToString(enum wiggleViewFuncEnum x)
/* Convert from enum to string representation. */
{
return viewFuncLabels[x];
}
enum wiggleViewFuncEnum wiggleViewFuncStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, viewFuncValues);
if (x < 0)
errAbort("hui::wiggleViewFuncStringToEnum() - Unknown option %s", string);
return x;
}
void viewFuncDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropListFull(var, viewFuncLabels, viewFuncValues,
ArraySize(viewFuncValues), curVal, NULL, NULL);
}
static char *wiggleTransformFuncOptions[] =
{
"NONE",
"LOG"
};
static char *wiggleTransformFuncLabels[] =
{
"NONE",
"LOG (ln(1+x))"
};
enum wiggleTransformFuncEnum wiggleTransformFuncToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleTransformFuncOptions);
if (x < 0)
errAbort("hui::wiggleTransformFuncToEnum() - Unknown option %s", string);
return x;
}
void wiggleTransformFuncDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropListFull(var, wiggleTransformFuncLabels, wiggleTransformFuncOptions,
ArraySize(wiggleTransformFuncOptions), curVal, NULL, NULL);
}
static char *wiggleAlwaysZeroOptions[] =
{
"OFF",
"ON"
};
enum wiggleAlwaysZeroEnum wiggleAlwaysZeroToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleAlwaysZeroOptions);
if (x < 0)
errAbort("hui::wiggleAlwaysZeroToEnum() - Unknown option %s", string);
return x;
}
void wiggleAlwaysZeroDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleAlwaysZeroOptions,
ArraySize(wiggleAlwaysZeroOptions), curVal);
}
/****** Options for the wiggle track horizontal grid lines *******/
static char *wiggleGridOptions[] =
{
"ON",
"OFF"
};
enum wiggleGridOptEnum wiggleGridStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleGridOptions);
if (x < 0)
errAbort("hui::wiggleGridStringToEnum() - Unknown option %s", string);
return x;
}
char *wiggleGridEnumToString(enum wiggleGridOptEnum x)
/* Convert from enum to string representation. */
{
return wiggleGridOptions[x];
}
void wiggleGridDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleGridOptions, ArraySize(wiggleGridOptions),
curVal);
}
/****** Some stuff for wiggle track related controls *******/
static char *wiggleOptions[] =
{
"samples only",
"linear interpolation"
};
enum wiggleOptEnum wiggleStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, wiggleOptions);
if (x < 0)
errAbort("hui::wiggleStringToEnum() - Unknown option %s", string);
return x;
}
char *wiggleEnumToString(enum wiggleOptEnum x)
/* Convert from enum to string representation. */
{
return wiggleOptions[x];
}
void wiggleDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, wiggleOptions, ArraySize(wiggleOptions),
curVal);
}
/****** Some stuff for GCwiggle track related controls *******/
static char *GCwiggleOptions[] =
{
"samples only",
"linear interpolation"
};
enum GCwiggleOptEnum GCwiggleStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, GCwiggleOptions);
if (x < 0)
errAbort("hui::GCwiggleStringToEnum() - Unknown option %s", string);
return x;
}
char *GCwiggleEnumToString(enum GCwiggleOptEnum x)
/* Convert from enum to string representation. */
{
return GCwiggleOptions[x];
}
void GCwiggleDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, GCwiggleOptions, ArraySize(GCwiggleOptions),
curVal);
}
/****** Some stuff for chimp track related controls *******/
static char *chimpOptions[] =
{
"samples only",
"linear interpolation"
};
enum chimpOptEnum chimpStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, chimpOptions);
if (x < 0)
errAbort("hui::chimpStringToEnum() - Unknown option %s", string);
return x;
}
char *chimpEnumToString(enum chimpOptEnum x)
/* Convert from enum to string representation. */
{
return chimpOptions[x];
}
void chimpDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, chimpOptions, ArraySize(chimpOptions),
curVal);
}
/*
#define POP_METHOD_AUTO "auto"
#define POP_METHOD_MANUAL "manual"
static char *popMethodLabels[] =
{
"auto",
"manual",
};
static char *popMethodValues[] =
{
POP_METHOD_AUTO,
POP_METHOD_MANUAL,
};
*/
/****** Some stuff for mRNA and EST related controls *******/
static void addMrnaFilter(struct mrnaUiData *mud, char *track, char *label, char *key, char *table)
/* Add an mrna filter */
{
struct mrnaFilter *fil;
AllocVar(fil);
fil->label = label;
fil->suffix = cloneString(key);
fil->table = table;
slAddTail(&mud->filterList, fil);
}
static struct mrnaUiData *newEmptyMrnaUiData(char *track)
/* Make a new in extra-ui data structure for a bed. */
{
struct mrnaUiData *mud;
AllocVar(mud);
mud->filterTypeSuffix = cloneString("Ft");
mud->logicTypeSuffix = cloneString("Lt");
return mud;
}
struct mrnaUiData *newBedUiData(char *track)
/* Make a new in extra-ui data structure for a bed. */
{
struct mrnaUiData *mud = newEmptyMrnaUiData(track);
addMrnaFilter(mud, track, "name", "name",track);
return mud;
}
struct mrnaUiData *newMrnaUiData(char *track, boolean isXeno)
/* Make a new in extra-ui data structure for mRNA. */
{
struct mrnaUiData *mud = newEmptyMrnaUiData(track);
if (isXeno)
addMrnaFilter(mud, track, "organism", "org", organismTable);
addMrnaFilter(mud, track, "accession", "acc", "acc");
addMrnaFilter(mud, track, "author", "aut", authorTable);
addMrnaFilter(mud, track, "library", "lib", libraryTable);
addMrnaFilter(mud, track, "tissue", "tis", tissueTable);
addMrnaFilter(mud, track, "cell", "cel", cellTable);
addMrnaFilter(mud, track, "keyword", "key", keywordTable);
addMrnaFilter(mud, track, "gene", "gen", geneNameTable);
addMrnaFilter(mud, track, "product", "pro", productNameTable);
addMrnaFilter(mud, track, "description", "des", descriptionTable);
return mud;
}
int trackNameAndLabelCmp(const void *va, const void *vb)
// Compare to sort on label.
{
const struct trackNameAndLabel *a = *((struct trackNameAndLabel **)va);
const struct trackNameAndLabel *b = *((struct trackNameAndLabel **)vb);
return strcmp(a->label, b->label);
}
char *trackFindLabel(struct trackNameAndLabel *list, char *label)
// Try to find label in list. Return NULL if it's not there.
{
struct trackNameAndLabel *el;
for (el = list; el != NULL; el = el->next)
{
if (sameString(el->label, label))
return label;
}
return NULL;
}
char *genePredDropDown(struct cart *cart, struct hash *trackHash,
char *formName, char *varName)
/* Make gene-prediction drop-down(). Return track name of
* currently selected one. Return NULL if no gene tracks.
* If formName isn't NULL, it's the form for auto submit (onchange attr).
* If formName is NULL, no submit occurs when menu is changed */
{
char *cartTrack = cartOptionalString(cart, varName);
struct hashEl *trackList, *trackEl;
char *selectedName = NULL;
struct trackNameAndLabel *nameList = NULL, *name;
char *trackName = NULL;
/* Make alphabetized list of all genePred track names. */
trackList = hashElListHash(trackHash);
for (trackEl = trackList; trackEl != NULL; trackEl = trackEl->next)
{
struct trackDb *tdb = trackEl->val;
char *dupe = cloneString(tdb->type);
char *type = firstWordInLine(dupe);
if ((sameString(type, "genePred")) && (!sameString(tdb->table, "tigrGeneIndex") && !tdbIsComposite(tdb) && !tdbIsCompositeView(tdb)))
{
AllocVar(name);
name->name = tdb->track;
name->label = tdb->longLabel;
slAddHead(&nameList, name);
}
freez(&dupe);
}
slSort(&nameList, trackNameAndLabelCmp);
/* No gene tracks - not much we can do. */
if (nameList == NULL)
{
slFreeList(&trackList);
return NULL;
}
/* Try to find current track - from cart first, then
* knownGenes, then refGenes. */
if (cartTrack != NULL)
selectedName = trackFindLabel(nameList, cartTrack);
if (selectedName == NULL)
selectedName = trackFindLabel(nameList, "Known Genes");
if (selectedName == NULL)
selectedName = trackFindLabel(nameList, "SGD Genes");
if (selectedName == NULL)
selectedName = trackFindLabel(nameList, "BDGP Genes");
if (selectedName == NULL)
selectedName = trackFindLabel(nameList, "WormBase Genes");
if (selectedName == NULL)
selectedName = trackFindLabel(nameList, "RefSeq Genes");
if (selectedName == NULL)
selectedName = nameList->name;
/* Make drop-down list. */
{
char javascript[SMALLBUF], *autoSubmit, *event;
int nameCount = slCount(nameList);
char **menu;
int i;
AllocArray(menu, nameCount);
for (name = nameList, i=0; name != NULL; name = name->next, ++i)
{
menu[i] = name->label;
}
if (formName == NULL)
{
autoSubmit = NULL;
event = NULL;
}
else
{
safef(javascript, sizeof(javascript),
"document.%s.submit();", formName);
autoSubmit = javascript;
event = "change";
}
cgiMakeDropListFull(varName, menu, menu, nameCount, selectedName, event, autoSubmit);
freez(&menu);
}
/* Convert to track name */
for (name = nameList; name != NULL; name = name->next)
{
if (sameString(selectedName, name->label))
trackName = name->name;
}
/* Clean up and return. */
slFreeList(&nameList);
slFreeList(&trackList);
return trackName;
}
void rAddTrackListToHash(struct hash *trackHash, struct trackDb *tdbList, char *chrom,
boolean leafOnly)
/* Recursively add trackList to trackHash */
{
struct trackDb *tdb;
for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
{
if (hTrackOnChrom(tdb, chrom))
{
if (tdb->subtracks == NULL || !leafOnly)
hashAdd(trackHash, tdb->track, tdb);
}
rAddTrackListToHash(trackHash, tdb->subtracks, chrom, leafOnly);
}
}
struct hash *trackHashMakeWithComposites(char *db,char *chrom,struct trackDb **tdbList,
bool withComposites)
// Make hash of trackDb items for this chromosome, including composites, not just the subtracks.
// May pass in prepopulated trackDb list, or may receive the trackDb list as an inout.
{
struct trackDb *theTdbs = NULL;
if (tdbList == NULL || *tdbList == NULL)
{
theTdbs = hTrackDb(db);
if (tdbList != NULL)
*tdbList = theTdbs;
}
else
theTdbs = *tdbList;
struct hash *trackHash = newHash(7);
rAddTrackListToHash(trackHash, theTdbs, chrom, !withComposites);
return trackHash;
}
/****** Stuff for acembly related options *******/
static char *acemblyOptions[] =
{
"all genes",
"main",
"putative",
};
enum acemblyOptEnum acemblyStringToEnum(char *string)
/* Convert from string to enum representation. */
{
int x = stringIx(string, acemblyOptions);
if (x < 0)
errAbort("hui::acemblyStringToEnum() - Unknown option %s", string);
return x;
}
char *acemblyEnumToString(enum acemblyOptEnum x)
/* Convert from enum to string representation. */
{
return acemblyOptions[x];
}
void acemblyDropDown(char *var, char *curVal)
/* Make drop down of options. */
{
cgiMakeDropList(var, acemblyOptions, ArraySize(acemblyOptions),
curVal);
}
static boolean parseAssignment(char *words, char **name, char **value)
/* parse =, destroying input words in the process */
{
char *p;
if ((p = index(words, '=')) == NULL)
return FALSE;
*p++ = 0;
if (name)
*name = words;
if (value)
*value = p;
return TRUE;
}
static char *getPrimaryType(char *primarySubtrack, struct trackDb *tdb)
/* Do not free when done. */
{
char *type = NULL;
if (primarySubtrack)
{
struct slRef *tdbRef, *tdbRefList = trackDbListGetRefsToDescendants(tdb->subtracks);
for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
{
struct trackDb *subtrack = tdbRef->val;
if (sameString(subtrack->track, primarySubtrack))
{
type = subtrack->type;
break;
}
}
slFreeList(&tdbRefList);
}
return type;
}
boolean hSameTrackDbType(char *type1, char *type2)
// Compare type strings: identical first word or allowed compatibilities
{
// OLD_CODE
//return (sameString(type1, type2) ||
// (startsWith("wig ", type1) && startsWith("wig ", type2)));
int wordLength = strlen(type1);
// Many types have additional args which should not interfere (e.g. "bigWig 0 20")
// Note: beds of different size are ok
char *firstWhite = skipToSpaces(type1);
if (firstWhite != NULL)
wordLength = (firstWhite - type1) + 1; // include white space
if (sameStringN(type1, type2,wordLength))
return TRUE;
// Allow these cross overs? Why not? (see redmine #7588)
if (startsWith("wig ",type1) && startsWith("bigWig ",type2)) // tested
return TRUE;
if (startsWith("bigWig ",type1) && startsWith("wig ",type2)) // tested
return TRUE;
// Many flavors of bed that could be merged...
if (( startsWith("bed ",type1) // bed to Peak and vis-versa tested
|| startsWith("broadPeak",type1)
|| startsWith("narrowPeak",type1))
&& ( startsWith("bed ",type2)
|| startsWith("broadPeak",type2)
|| startsWith("narrowPeak",type2)))
return TRUE;
// bigBed to bed and vis-versa fails!
//if (( startsWith("bed ",type1)
// || startsWith("bigBed ",type1)
// || startsWith("broadPeak",type1)
// || startsWith("narrowPeak",type1))
//&& ( startsWith("bed ",type2)
// || startsWith("bigBed ",type2)
// || startsWith("broadPeak",type2)
// || startsWith("narrowPeak",type2)))
// return TRUE;
return FALSE;
}
static char *labelRoot(char *label, char** suffix)
/* Parses a label which may be split with a into root and suffix
Always free labelRoot. suffix, which may be null does not need to be freed. */
{
char *root = cloneString(label);
char *extra=strstrNoCase(root," "); // mean don't include the reset as part of the link
if ((long)(extra)==-1)
extra=NULL;
if (extra!=NULL)
{
*extra='\0';
if (suffix != NULL)
{
extra+=5;
*extra=' '; // Converts the to ' ' and include the ' '
*suffix = extra;
}
}
return root;
}
typedef struct _dividers
{
int count;
char**subgroups;
char* setting;
} dividers_t;
static dividers_t *dividersSettingGet(struct trackDb *parentTdb)
// Parses any dividers setting in parent of subtracks
{
dividers_t *dividers = needMem(sizeof(dividers_t));
dividers->setting = cloneString(trackDbSetting(parentTdb, "dividers"));
if (dividers->setting == NULL)
{
freeMem(dividers);
return NULL;
}
dividers->subgroups = needMem(24*sizeof(char*));
dividers->count = chopByWhite(dividers->setting, dividers->subgroups,24);
return dividers;
}
static void dividersFree(dividers_t **dividers)
// frees any previously obtained dividers setting
{
if (dividers && *dividers)
{
freeMem((*dividers)->subgroups);
freeMem((*dividers)->setting);
freez(dividers);
}
}
typedef struct _hierarchy
{
int count;
char* subgroup;
char**membership;
int* indents;
char* setting;
} hierarchy_t;
static hierarchy_t *hierarchySettingGet(struct trackDb *parentTdb)
// Parses any list hierachy instructions setting in parent of subtracks
{
hierarchy_t *hierarchy = needMem(sizeof(hierarchy_t));
hierarchy->setting = cloneString(trackDbSetting(parentTdb, "hierarchy")); // To be freed later
if (hierarchy->setting == NULL)
{
freeMem(hierarchy);
return NULL;
}
int cnt,ix;
char *words[SMALLBUF];
cnt = chopLine(hierarchy->setting, words);
assert(cnt<=ArraySize(words));
if (cnt <= 1)
{
freeMem(hierarchy->setting);
freeMem(hierarchy);
return NULL;
}
hierarchy->membership = needMem(cnt*sizeof(char*));
hierarchy->indents = needMem(cnt*sizeof(int));
hierarchy->subgroup = words[0];
char *name,*value;
for (ix = 1,hierarchy->count=0; ix < cnt; ix++)
{
if (parseAssignment(words[ix], &name, &value))
{
hierarchy->membership[hierarchy->count] = name;
hierarchy->indents[hierarchy->count] = sqlUnsigned(value);
hierarchy->count++;
}
}
return hierarchy;
}
static void hierarchyFree(hierarchy_t **hierarchy)
// frees any previously obtained hierachy settings
{
if (hierarchy && *hierarchy)
{
freeMem((*hierarchy)->setting);
freeMem((*hierarchy)->membership);
freeMem((*hierarchy)->indents);
freez(hierarchy);
}
}
// Four State checkboxes can be checked/unchecked by enable/disabled
// NOTE: fourState is not a bitmap because it is manipulated in javascript and
// int seemed easier at the time
#define FOUR_STATE_EMPTY TDB_EXTRAS_EMPTY_STATE
//#define FOUR_STATE_UNCHECKED 0
//#define FOUR_STATE_CHECKED 1
//#define FOUR_STATE_CHECKED_DISABLED -1
#define FOUR_STATE_DISABLE(val) {while ((val) >= 0) (val) -= 2;}
#define FOUR_STATE_ENABLE(val) {while ((val) < 0) (val) += 2;}
int subtrackFourStateChecked(struct trackDb *subtrack, struct cart *cart)
// Returns the four state checked state of the subtrack
{
char * setting = NULL;
char objName[SMALLBUF];
int fourState = (int)tdbExtrasFourState(subtrack);
if (fourState != FOUR_STATE_EMPTY)
return fourState;
fourState = FOUR_STATE_UNCHECKED; // default to unchecked, enabled
if ((setting = trackDbLocalSetting(subtrack, "parent")) != NULL)
{
if (findWordByDelimiter("off",' ',setting) == NULL)
fourState = FOUR_STATE_CHECKED;
}
// Now check visibility
enum trackVisibility vis = tdbLocalVisibility(cart, subtrack, NULL);
if (vis == tvHide)
{
if (tdbIsCompositeView(subtrack->parent))
{
if (tdbLocalVisibility(cart, subtrack->parent, NULL) == tvHide)
FOUR_STATE_DISABLE(fourState);
}
}
safef(objName, sizeof(objName), "%s_sel", subtrack->track);
setting = cartOptionalString(cart, objName);
if (setting == NULL)
setting = cartOptionalString(cart, trackHubSkipHubName(objName));
if (setting != NULL)
{
if (sameWord("on",setting)) // ouch! cartUsualInt was interpreting "on" as 0, which was bad bug!
fourState = 1;
else
fourState = atoi(setting);
}
tdbExtrasFourStateSet(subtrack,fourState);
return fourState;
}
void subtrackFourStateCheckedSet(struct trackDb *subtrack, struct cart *cart,boolean checked,
boolean enabled)
// Sets the fourState Checked in the cart and updates cached state
{
int fourState = ( checked ? FOUR_STATE_CHECKED : FOUR_STATE_UNCHECKED );
if (!enabled)
FOUR_STATE_DISABLE(fourState);
char objName[SMALLBUF];
char objVal[5];
safef(objName, sizeof(objName), "%s_sel", subtrack->track);
safef(objVal, sizeof(objVal), "%d", fourState);
cartSetString(cart, objName, objVal);
tdbExtrasFourStateSet(subtrack,fourState);
}
static char *tagEncode(char *name)
// Turns out css classes cannot begin with a number. So prepend 'A'
// If this were more widely used, could move to cheapcgi.c.
{
if (!isdigit(*name))
return name;
char *newName = needMem(strlen(name)+2);
*newName = 'A';
strcpy(newName+1,name);
return newName;
}
boolean dimensionsExist(struct trackDb *parentTdb)
// Does this parent track contain dimensions?
{
return (trackDbSetting(parentTdb, "dimensions") != NULL);
}
static dimensions_t *dimensionSettingsGet(struct trackDb *parentTdb)
// Parses any dimemnions setting in parent of subtracks
{
dimensions_t *dimensions = needMem(sizeof(dimensions_t));
dimensions->setting = cloneString(trackDbSetting(parentTdb, "dimensions")); // To be freed later
if (dimensions->setting == NULL)
{
freeMem(dimensions);
return NULL;
}
int cnt,ix;
char *words[SMALLBUF];
cnt = chopLine(dimensions->setting,words);
assert(cnt<=ArraySize(words));
if (cnt <= 0)
{
freeMem(dimensions->setting);
freeMem(dimensions);
return NULL;
}
dimensions->names = needMem(cnt*sizeof(char*));
dimensions->subgroups = needMem(cnt*sizeof(char*));
char *name,*value;
for (ix = 0,dimensions->count=0; ix < cnt; ix++)
{
if (parseAssignment(words[ix], &name, &value))
{
dimensions->names[dimensions->count] = name;
dimensions->subgroups[dimensions->count] = tagEncode(value);
dimensions->count++;
}
}
return dimensions;
}
static void dimensionsFree(dimensions_t **dimensions)
// frees any previously obtained dividers setting
{
if (dimensions && *dimensions)
{
freeMem((*dimensions)->setting);
freeMem((*dimensions)->names);
freeMem((*dimensions)->subgroups);
freez(dimensions);
}
}
#define SUBGROUP_MAX 9
int subgroupCount(struct trackDb *parentTdb)
// How many subGroup setting does this parent have?
{
int ix;
int count = 0;
for (ix=1;ix<=SUBGROUP_MAX;ix++)
{
char subGrp[16];
safef(subGrp, ArraySize(subGrp), "subGroup%d",ix);
if (trackDbSetting(parentTdb, subGrp) != NULL)
count++;
}
return count;
}
char * subgroupSettingByTagOrName(struct trackDb *parentTdb, char *groupNameOrTag)
// look for a subGroup by name (ie subGroup1) or tag (ie view) and return an unallocated char*
{
struct trackDb *ancestor;
for (ancestor = parentTdb; ancestor != NULL; ancestor = ancestor->parent)
{
char *setting = NULL;
if (startsWith("subGroup",groupNameOrTag))
{
setting = trackDbSetting(ancestor, groupNameOrTag);
if (setting != NULL)
return setting;
}
// these views are cached at trackDb read time
setting = trackDbViewSetting(ancestor, groupNameOrTag);
if (setting != NULL)
return setting;
}
return NULL;
}
boolean subgroupingExists(struct trackDb *parentTdb, char *groupNameOrTag)
// Does this parent track contain a particular subgrouping?
{
return (subgroupSettingByTagOrName(parentTdb,groupNameOrTag) != NULL);
}
static members_t *subgroupMembersGet(struct trackDb *parentTdb, char *groupNameOrTag)
// Parse a subGroup setting line into tag,title, names(optional) and values(optional),
// returning the count of members or 0
{
static members_t nullMember; // place holder for NULL
members_t *members = tdbExtrasMembers(parentTdb, groupNameOrTag);
if (members != NULL)
{
if (members == &nullMember)
return NULL;
return members;
}
int ix,count;
char *setting = subgroupSettingByTagOrName(parentTdb, groupNameOrTag);
if (setting == NULL)
{
tdbExtrasMembersSet(parentTdb, groupNameOrTag, &nullMember);
return NULL;
}
members = needMem(sizeof(members_t));
members->setting = cloneString(setting);
#define MAX_SUBGROUP_MEMBERS 2000
char *words[MAX_SUBGROUP_MEMBERS+3]; // members preceded by tag and title, one extra to detect
count = chopLine(members->setting, words);
if (count == ArraySize(words))
warn("Subgroup %s exceeds maximum %d members", words[1], MAX_SUBGROUP_MEMBERS);
if (count <= 1)
{
freeMem(members->setting);
freeMem(members);
tdbExtrasMembersSet(parentTdb, groupNameOrTag, &nullMember);
return NULL;
}
members->groupTag = words[0];
members->groupTitle = strSwapChar(words[1],'_',' '); // Titles replace '_' with space
members->tags = needMem(count*sizeof(char*));
members->titles = needMem(count*sizeof(char*));
for (ix = 2,members->count=0; ix < count; ix++)
{
char *name,*value;
if (parseAssignment(words[ix], &name, &value))
{
members->tags[members->count] = tagEncode(name);
members->titles[members->count] = strSwapChar(value,'_',' ');
members->count++;
}
else
{
warn("Subgroup \"%s\" is missing a tag=val pair", words[1]);
}
}
tdbExtrasMembersSet(parentTdb, groupNameOrTag, members);
return members;
}
static int membersSubGroupIx(members_t* members, char *tag)
// Returns the index of the subgroup within the members struct (or -1)
{
int ix = 0;
for (ix=0;ixcount;ix++)
{
if (members->tags[ix] != NULL && sameString(members->tags[ix],tag))
return ix;
}
return -1;
}
static void subgroupMembersFree(members_t **members)
// frees memory for subgroupMembers lists
{
if (members && *members)
{
// This should only get set through membersForAll which should not be freed.
if ((*members)->selected != NULL || (*members)->subtrackList != NULL)
return;
freeMem((*members)->setting);
freeMem((*members)->tags);
freeMem((*members)->titles);
freez(members);
}
}
static members_t *subgroupMembersWeedOutEmpties(struct trackDb *parentTdb, members_t *members,
struct cart *cart)
// Weed out members of a subgroup without any subtracks, alters memory in place!
{
if (members->count == 0)
{
warn("%s: No subtracks in group: %s. This indicates a problem in the subGroup line for this group.",
parentTdb->track, members->groupTitle);
return members;
}
// First tally all subtrack counts
int ixIn=0;
struct slRef *subtrackRef, *subtrackRefList =
trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
struct trackDb *subtrack;
members->subtrackCount = needMem(members->count * sizeof(int));
members->currentlyVisible = needMem(members->count * sizeof(int));
members->subtrackList = needMem(members->count * sizeof(struct slRef *));
for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
{
subtrack = subtrackRef->val;
char *belongsTo =NULL;
if (subgroupFind(subtrack,members->groupTag,&belongsTo))
{
if (-1 != (ixIn = stringArrayIx(belongsTo, members->tags, members->count)))
{
members->subtrackCount[ixIn]++;
if (cart && fourStateVisible(subtrackFourStateChecked(subtrack,cart)))
members->currentlyVisible[ixIn]++;
refAdd(&(members->subtrackList[ixIn]), subtrack);
}
}
}
// Now weed out empty subgroup tags. Can do this in place since new count <= old count
// NOTE: Don't I wish I had made these as an slList ages ago! (tim)
int ixOut=0;
for (ixIn=ixOut;ixIncount;ixIn++)
{
if (members->subtrackCount[ixIn] > 0)
{
if (ixOut < ixIn)
{
members->tags[ixOut] = members->tags[ixIn];
members->titles[ixOut] = members->titles[ixIn];
members->subtrackCount[ixOut] = members->subtrackCount[ixIn];
members->currentlyVisible[ixOut] = members->currentlyVisible[ixIn];
members->subtrackList[ixOut] = members->subtrackList[ixIn];
if (members->selected != NULL)
members->selected[ixOut] = members->selected[ixIn];
}
ixOut++;
}
else
{
members->tags[ixIn] = NULL;
members->titles[ixIn] = NULL;
members->subtrackCount[ixIn] = 0;
members->currentlyVisible[ixIn] = 0;
//slFreeList(&members->subtrackList[ixIn]); // No freeing at this moment
members->subtrackList[ixIn] = NULL;
if (members->selected != NULL)
members->selected[ixIn] = FALSE;
}
}
members->count = ixOut;
if (members->count == 0) // No members of this subgroup had a subtrack
{
//subgroupMembersFree(&members); // don't bother freeing
return NULL;
}
return members;
}
enum
{
dimV=0, // View first
dimX=1, // X & Y next
dimY=2,
dimA=3, // dimA is start of first of the optional non-matrix, non-view dimensions
};
static char* abcMembersChecked(struct trackDb *parentTdb, struct cart *cart, members_t* members,
char letter)
// returns a string of subGroup tags which are currently checked
{
char settingName[SMALLBUF];
int mIx;
if (members->selected == NULL)
members->selected = needMem(members->count * sizeof(boolean));
safef(settingName, sizeof(settingName), "%s.filterComp.%s",parentTdb->track,members->groupTag);
struct slName *options = cartOptionalSlNameList(cart,settingName);
if (options != NULL)
{
if (sameWord(options->name,"All")) // filterComp returns "All" meaning every option selected
{
slNameFreeList(&options);
options = slNameListFromStringArray(members->tags, members->count);
assert(options != NULL);
}
struct slName *option;
for (option=options;option!=NULL;option=option->next)
{
mIx = membersSubGroupIx(members, option->name);
if (mIx >= 0)
members->selected[mIx] = TRUE;
}
return slNameListToString(options,',');
}
struct dyString *currentlyCheckedTags = NULL;
// Need a string of subGroup tags which are currently checked
safef(settingName,sizeof(settingName),"dimension%cchecked",letter);
char *dimCheckedDefaults = trackDbSettingOrDefault(parentTdb,settingName,"All");
char *checkedDefaults[12];
int defaultCount = 0;
if (dimCheckedDefaults != NULL
&& differentWord(dimCheckedDefaults,"All") && differentWord(dimCheckedDefaults,"Any"))
{
defaultCount = chopCommas(dimCheckedDefaults, checkedDefaults);
int dIx = 0;
for (;dIx < defaultCount;dIx++)
checkedDefaults[dIx] = tagEncode(checkedDefaults[dIx]); // encode these before compare!
} // Will leak, but this is a tiny amount
for (mIx=0;mIxcount;mIx++)
{
safef(settingName, sizeof(settingName), "%s.mat_%s_dim%c_cb",
parentTdb->track,members->tags[mIx],letter);
members->selected[mIx] = TRUE;
if (defaultCount > 0)
members->selected[mIx] =
(-1 != stringArrayIx(members->tags[mIx],checkedDefaults,defaultCount));
members->selected[mIx] = cartUsualBoolean(cart,settingName,members->selected[mIx]);
if (members->selected[mIx])
{
if (currentlyCheckedTags == NULL)
currentlyCheckedTags = dyStringCreate("%s",members->tags[mIx]);
else
dyStringPrintf(currentlyCheckedTags,",%s",members->tags[mIx]);
}
}
if (currentlyCheckedTags)
return dyStringCannibalize(¤tlyCheckedTags);
return NULL;
}
static membersForAll_t *membersForAllSubGroupsWeedOutEmpties(struct trackDb *parentTdb,
membersForAll_t *membersForAll, struct cart *cart)
// Weed through members, tossing those without subtracks
{
// View is always first
if (membersForAll->members[dimV] != NULL)
membersForAll->members[dimV] =
subgroupMembersWeedOutEmpties(parentTdb, membersForAll->members[dimV],cart);
// X and Y are special
if (membersForAll->members[dimX] != NULL)
membersForAll->members[dimX] =
subgroupMembersWeedOutEmpties(parentTdb, membersForAll->members[dimX],cart);
if (membersForAll->members[dimY] != NULL)
membersForAll->members[dimY] =
subgroupMembersWeedOutEmpties(parentTdb, membersForAll->members[dimY],cart);
// Handle the ABC dimensions
int ixIn,ixOut=dimA;
for (ixIn=ixOut;ixIndimMax;ixIn++)
{
if (membersForAll->members[ixIn] != NULL)
membersForAll->members[ixIn] =
subgroupMembersWeedOutEmpties(parentTdb, membersForAll->members[ixIn],cart);
if (membersForAll->members[ixIn] == NULL)
membersForAll->checkedTags[ixOut] = NULL;
else
{
if (ixOut < ixIn) // Collapse if necessary
{ // NOTE: Don't I wish I had made these as an slList ages ago! (tim)
membersForAll->members[ixOut] = membersForAll->members[ixIn];
membersForAll->checkedTags[ixOut] = membersForAll->checkedTags[ixIn];
membersForAll->letters[ixOut] = membersForAll->letters[ixIn];
}
ixOut++;
}
}
membersForAll->dimMax = ixOut;
membersForAll->abcCount = membersForAll->dimMax - dimA;
return membersForAll;
}
membersForAll_t* membersForAllSubGroupsGet(struct trackDb *parentTdb, struct cart *cart)
// Returns all the parents subGroups and members
{
membersForAll_t *membersForAll = tdbExtrasMembersForAll(parentTdb);
if (membersForAll != NULL)
return membersForAll; // Already retrieved, so don't do it again
int ix;
membersForAll = needMem(sizeof(membersForAll_t));
if (tdbIsCompositeView(parentTdb->subtracks)) // view must have viewInMidle tdb in tree
membersForAll->members[dimV]=subgroupMembersGet(parentTdb,"view");
membersForAll->letters[dimV]='V';
membersForAll->dimMax=dimA; // This can expand, depending upon ABC dimensions
membersForAll->dimensions = dimensionSettingsGet(parentTdb);
if (membersForAll->dimensions != NULL)
{
for (ix=0;ixdimensions->count;ix++)
{
char letter = lastChar(membersForAll->dimensions->names[ix]);
if (letter != 'X' && letter != 'Y')
{
membersForAll->members[membersForAll->dimMax] =
subgroupMembersGet(parentTdb, membersForAll->dimensions->subgroups[ix]);
membersForAll->letters[membersForAll->dimMax] = letter;
if (cart != NULL)
membersForAll->checkedTags[membersForAll->dimMax] = abcMembersChecked(
parentTdb,cart,membersForAll->members[membersForAll->dimMax],letter);
membersForAll->dimMax++;
}
else if (letter == 'X')
{
membersForAll->members[dimX] =
subgroupMembersGet(parentTdb, membersForAll->dimensions->subgroups[ix]);
membersForAll->letters[dimX] = letter;
}
else
{
membersForAll->members[dimY] =
subgroupMembersGet(parentTdb, membersForAll->dimensions->subgroups[ix]);
membersForAll->letters[dimY] = letter;
}
}
}
else // No 'dimensions" setting: treat any subGroups as abc dimensions
{
char letter = 'A';
// walk through numbered subgroups
for (ix=1;ixmembers[dimV] && sameWord(tag,"view"))
continue; // View should have already been handled. NOTE: extremely unlikely case
membersForAll->members[membersForAll->dimMax]=subgroupMembersGet(parentTdb, tag);
membersForAll->letters[membersForAll->dimMax]=letter;
if (cart != NULL)
membersForAll->checkedTags[membersForAll->dimMax] = abcMembersChecked(
parentTdb,cart,membersForAll->members[membersForAll->dimMax],letter);
membersForAll->dimMax++;
letter++;
}
}
}
membersForAll->abcCount = membersForAll->dimMax - dimA;
membersForAll = membersForAllSubGroupsWeedOutEmpties(parentTdb, membersForAll, cart);
// NOTE: Dimensions must be defined for filterComposite. Filter dimensioms are all and only ABCs.
// Use dimensionAchecked to define selected
char *filtering = trackDbSettingOrDefault(parentTdb,"filterComposite",NULL);
if (filtering && !sameWord(filtering,"off"))
{
if (membersForAll->dimensions == NULL)
errAbort("If 'filterComposite' defined, must define 'dimensions' also.");
membersForAll->filters = TRUE;
// Default all to multi
for (ix=dimA;ixdimMax;ix++)
{
if (membersForAll->members[ix] != NULL)
membersForAll->members[ix]->fcType = fctMulti;
}
if (!sameWord(filtering,"on"))
{
// Example tdb setting: "filterComposite on" OR
// "filterComposite dimA=one dimB=multi dimC=onlyOne"
// FIXME: do we even support anything but multi???
char *filterGroups[27];
int count = chopLine(filtering,filterGroups);
for (ix=0;ixdimMax && membersForAll->letters[abcIx] != letter;abcIx++)
; // Advance to correct letter
if (abcIx >= membersForAll->dimMax)
errAbort("Invalid 'filterComposite' trackDb setting. Dimension '%s' not found.",
dim);
if (sameWord(filterGroups[ix],"one"))
membersForAll->members[abcIx]->fcType = fctOne;
else if (sameWord(filterGroups[ix],"onlyOne") || sameWord(filterGroups[ix],"oneOnly"))
membersForAll->members[abcIx]->fcType = fctOneOnly;
}
}
}
if (cart != NULL) // Only save this if it is fully populated!
tdbExtrasMembersForAllSet(parentTdb,membersForAll);
return membersForAll;
}
static int membersForAllFindSubGroupIx(membersForAll_t* membersForAll, char *tag)
{ // Returns the index of the subgroups member struct within membersForAll (or -1)
int ix = 0;
for (ix=0;ixdimMax;ix++)
{
if (membersForAll->members[ix] != NULL && sameString(membersForAll->members[ix]->groupTag,tag))
return ix;
}
return -1;
}
const members_t*membersFindByTag(struct trackDb *parentTdb, char *tag)
{ // Uses membersForAll which may be in tdbExtraCache. Do not free
membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,NULL);
if (membersForAll == NULL)
return NULL;
int ix = membersForAllFindSubGroupIx(membersForAll,tag);
if (ix >= 0)
return membersForAll->members[ix];
return NULL;
}
static void membersForAllSubGroupsFree(struct trackDb *parentTdb,
membersForAll_t** membersForAllPtr)
// frees memory for membersForAllSubGroups struct
{
return; // don't bother freeing things, just takes time for no benefit
if (membersForAllPtr && *membersForAllPtr)
{
if (parentTdb != NULL)
{
if (*membersForAllPtr == tdbExtrasMembersForAll(parentTdb))
return; // Don't free something saved to the tdbExtras!
}
membersForAll_t* membersForAll = *membersForAllPtr;
subgroupMembersFree(&(membersForAll->members[dimX]));
subgroupMembersFree(&(membersForAll->members[dimY]));
subgroupMembersFree(&(membersForAll->members[dimV]));
int ix;
for (ix=dimA;ixdimMax;ix++)
{
//ASSERT(membersForAll->members[ix] != NULL);
subgroupMembersFree(&(membersForAll->members[ix]));
if (membersForAll->checkedTags[ix])
freeMem(membersForAll->checkedTags[ix]);
}
dimensionsFree(&(membersForAll->dimensions));
freez(membersForAllPtr);
}
}
int multViewCount(struct trackDb *parentTdb)
// returns the number of multiView views declared
{
char *setting = subgroupSettingByTagOrName(parentTdb,"view");
if (setting == NULL)
return 0;
setting = cloneString(setting);
int cnt;
char *words[32];
cnt = chopLine(setting, words);
freeMem(setting);
return (cnt - 1);
}
membership_t *subgroupMembershipGet(struct trackDb *childTdb)
// gets all the subgroup membership for a child track
{
membership_t *membership = tdbExtrasMembership(childTdb);
if (membership != NULL)
return membership; // Already retrieved, so don't do it again
membership = needMem(sizeof(membership_t));
membership->setting = cloneString(trackDbSetting(childTdb, "subGroups"));
if (membership->setting == NULL)
{
freeMem(membership);
return NULL;
}
int ix,cnt;
char *words[SMALLBUF];
cnt = chopLine(membership->setting, words);
assert(cnt < ArraySize(words));
if (cnt <= 0)
{
freeMem(membership->setting);
freeMem(membership);
return NULL;
}
membership->subgroups = needMem(cnt*sizeof(char*));
membership->membership = needMem(cnt*sizeof(char*));
membership->titles = needMem(cnt*sizeof(char*));
for (ix = 0,membership->count=0; ix < cnt; ix++)
{
char *name,*value;
if (parseAssignment(words[ix], &name, &value))
{
membership->subgroups[membership->count] = name;
membership->membership[membership->count] = tagEncode(value);
// tags will be used as classes by js
members_t* members = subgroupMembersGet(childTdb->parent, name);
membership->titles[membership->count] = NULL; // default
if (members != NULL)
{
int ix2 = stringArrayIx(membership->membership[membership->count],members->tags,
members->count);
if (ix2 != -1)
membership->titles[membership->count] =
strSwapChar(cloneString(members->titles[ix2]),'_',' ');
// subgroupMembersFree(&members); /// don't bother freeing
}
membership->count++;
}
}
tdbExtrasMembershipSet(childTdb,membership);
return membership;
}
static boolean membershipInAllCurrentABCs(membership_t *membership,membersForAll_t*membersForAll)
// looks for a match between a membership set and ABC dimensions currently checked
{
int mIx,aIx,tIx;
for (aIx = dimA; aIx < membersForAll->dimMax; aIx++) // for each ABC subGroup
{
assert(membersForAll->members[aIx]->selected);
// must find atleast one selected tag that we have membership in
boolean matched = FALSE;
for (mIx = 0; mIx members[aIx]->count;mIx++) // for each tag of that subgroup
{
if (membersForAll->members[aIx]->selected[mIx]) // The particular subgroup tag is selected
{
for (tIx=0;tIxcount;tIx++) // what we are members of
{
// subTrack belongs to subGroup and tags match
if (sameString(membersForAll->members[aIx]->groupTag, membership->subgroups[tIx])
&& sameWord(membersForAll->members[aIx]->tags[mIx],membership->membership[tIx]))
{
matched = TRUE;
break;
}
}
}
}
if (!matched)
return FALSE;
}
return TRUE; // passed all tests so must be on all
}
char *compositeGroupLabel(struct trackDb *tdb, char *group, char *id)
// Given ID from group, return corresponding label, looking through parent's subGroupN's
// Given group ID, return group label, looking through parent's subGroupN's
{
members_t *members = subgroupMembersGet(tdb, group);
char *result = NULL;
int i;
for (i=0; icount; ++i)
{
if (sameString(members->tags[i], id))
result = cloneString(members->titles[i]);
}
//subgroupMembersFree(&members); /// don't bother freeing
return result;
}
char *compositeGroupId(struct trackDb *tdb, char *group, char *label)
// Given label, return id, looking through parent's subGroupN's
{
members_t *members = subgroupMembersGet(tdb, group);
char *result = NULL;
int i;
for (i=0; icount; ++i)
{
if (sameString(members->titles[i], label))
result = cloneString(members->tags[i]);
}
//subgroupMembersFree(&members); /// don't bother freeing
return result;
}
static boolean subtrackInAllCurrentABCs(struct trackDb *childTdb,membersForAll_t*membersForAll)
// looks for a match between a membership set and ABC dimensions currently checked
{
membership_t *membership = subgroupMembershipGet(childTdb);
if (membership == NULL)
return FALSE;
boolean found = membershipInAllCurrentABCs(membership,membersForAll);
return found;
}
boolean subgroupFind(struct trackDb *childTdb, char *name,char **value)
// looks for a single tag in a child track's subGroups setting
{
if (value != NULL)
*value = NULL;
membership_t *membership = subgroupMembershipGet(childTdb);
if (membership != NULL)
{
int ix;
for (ix=0;ixcount;ix++)
{
if (sameString(name,membership->subgroups[ix]))
{
if (value != NULL)
*value = cloneString(membership->membership[ix]);
return TRUE;
}
}
}
return FALSE;
}
static char *subtrackColorToCompare(struct trackDb *subtrack)
/* Convert RGB color to string with scaled hue, suitable for alpha sort */
{
struct rgbColor rgbColor;
rgbColor.r = subtrack->colorR;
rgbColor.g = subtrack->colorG;
rgbColor.b = subtrack->colorB;
rgbColor.a = 255;
struct hslColor hslColor = mgRgbToHsl(rgbColor);
int hue = hslColor.h * 10;
char buf[5];
safef(buf, 5, "%04d", hue);
return cloneString(buf);
}
boolean subgroupFindTitle(struct trackDb *parentTdb, char *name,char **value)
// looks for a a subgroup matching the name and returns the title if found
{
if (value != (void*)NULL)
*value = NULL;
members_t*members=subgroupMembersGet(parentTdb, name);
//const members_t *members = membersFindByTag(parentTdb,name);
// Can't use because of dimension dependence
if (members==NULL)
return FALSE;
*value = cloneString(members->groupTitle);
//subgroupMembersFree(&members);
return TRUE;
}
void subgroupFree(char **value)
// frees subgroup memory
{
if (value && *value)
freez(value);
}
boolean subgroupRequired(char *value)
/* Returns whether subgroup much be specified for each track.
* Generally true. Exceptions are specially defined subgroups */
{
return differentString(SUBTRACK_COLOR_SUBGROUP, value);
}
#define SORT_ON_TRACK_NAME "trackName"
#define SORT_ON_RESTRICTED "dateUnrestricted"
sortOrder_t *sortOrderGet(struct cart *cart,struct trackDb *parentTdb)
// Parses any list sort order instructions for parent of subtracks (from cart or trackDb)
// Some trickiness here. sortOrder->sortOrder is from cart (changed by user action),
// as is sortOrder->order, But columns are in original tdb order (unchanging)!
// However, if cart is null, all is from trackDb.ra
{
int ix;
char *setting = trackDbSetting(parentTdb, "sortOrder");
if (setting == NULL) // Must be in trackDb or not a sortable table
return NULL;
sortOrder_t *sortOrder = needMem(sizeof(sortOrder_t));
sortOrder->htmlId = needMem(strlen(parentTdb->track)+15);
safef(sortOrder->htmlId, (strlen(parentTdb->track)+15), "%s.sortOrder", parentTdb->track);
char *cartSetting = NULL;
if (cart != NULL)
cartSetting = cartCgiUsualString(cart, sortOrder->htmlId, setting);
// If setting is bigger than cartSetting, then it may be due to a trackDb change
if (cart != NULL && strlen(cartSetting) > strlen(setting))
sortOrder->sortOrder = cloneString(cartSetting); // cart order
else
sortOrder->sortOrder = cloneString(setting); // old cart value is abandoned!
/* parse setting into sortOrder */
sortOrder->setting = cloneString(setting);
sortOrder->count = chopByWhite(sortOrder->setting,NULL,0); // Get size
if (cart && !stringIn(SORT_ON_TRACK_NAME,setting))
sortOrder->count += 1;
if (cart && !stringIn(SORT_ON_RESTRICTED,setting))
sortOrder->count += 1;
sortOrder->column = needMem(sortOrder->count*sizeof(char*));
int foundColumns = chopByWhite(sortOrder->setting, sortOrder->column,sortOrder->count);
sortOrder->title = needMem(sortOrder->count*sizeof(char*));
sortOrder->forward = needMem(sortOrder->count*sizeof(boolean));
sortOrder->order = needMem(sortOrder->count*sizeof(int));
if (cart && foundColumns < sortOrder->count)
{
int columnCount = foundColumns;
int size = 0;
char *moreOrder = NULL;
if (cart && columnCount < sortOrder->count && !stringIn(SORT_ON_TRACK_NAME,setting))
{
assert(sortOrder->column[columnCount] == NULL);
sortOrder->column[columnCount] = cloneString(SORT_ON_TRACK_NAME "=+");
if (!stringIn(SORT_ON_TRACK_NAME,sortOrder->sortOrder))
{ // little bit more
size = strlen(sortOrder->sortOrder) + strlen(sortOrder->column[columnCount]) + 5;
moreOrder = needMem(size);
safef(moreOrder,size,"%s %s",sortOrder->sortOrder, sortOrder->column[columnCount]);
freeMem(sortOrder->sortOrder);
sortOrder->sortOrder = moreOrder;
}
columnCount++;
}
if (cart && columnCount < sortOrder->count && !stringIn(SORT_ON_RESTRICTED,setting))
{
assert(sortOrder->column[columnCount] == NULL);
sortOrder->column[columnCount] = cloneString(SORT_ON_RESTRICTED "=+");
if (!stringIn(SORT_ON_RESTRICTED,sortOrder->sortOrder))
{
size = strlen(sortOrder->sortOrder) + strlen(sortOrder->column[columnCount]) + 5;
moreOrder = needMem(size);
safef(moreOrder,size,"%s %s",sortOrder->sortOrder, sortOrder->column[columnCount]);
freeMem(sortOrder->sortOrder);
sortOrder->sortOrder = moreOrder;
}
columnCount++;
}
}
for (ix = 0; ixcount; ix++)
{
strSwapChar(sortOrder->column[ix],'=',0); // Don't want 'CEL=+' but 'CEL' and '+'
// find tdb substr in cart current order string
char *pos = stringIn(sortOrder->column[ix], sortOrder->sortOrder);
//assert(pos != NULL && pos[strlen(sortOrder->column[ix])] == '=');
if (pos != NULL && pos[strlen(sortOrder->column[ix])] == '=')
{
int ord=1;
char* pos2 = sortOrder->sortOrder;
for (;*pos2 && pos2 < pos;pos2++)
{
if (*pos2 == '=') // Discovering sort order in cart
ord++;
}
sortOrder->forward[ix] = (pos[strlen(sortOrder->column[ix]) + 1] == '+');
sortOrder->order[ix] = ord;
}
else // give up on cartSetting
{
sortOrder->forward[ix] = TRUE;
sortOrder->order[ix] = ix+1;
}
if (ix < foundColumns)
{
subgroupFindTitle(parentTdb,sortOrder->column[ix],&(sortOrder->title[ix]));
}
}
return sortOrder; // NOTE cloneString:words[0]==*sortOrder->column[0]
} // and will be freed when sortOrder is freed
void sortOrderFree(sortOrder_t **sortOrder)
// frees any previously obtained sortOrder settings
{
if (sortOrder && *sortOrder)
{
int ix;
for (ix=0;ix<(*sortOrder)->count;ix++) { subgroupFree(&((*sortOrder)->title[ix])); }
freeMem((*sortOrder)->sortOrder);
freeMem((*sortOrder)->htmlId);
freeMem((*sortOrder)->column);
freeMem((*sortOrder)->forward);
freeMem((*sortOrder)->order);
freeMem((*sortOrder)->setting);
freez(sortOrder);
}
}
sortableTdbItem *sortableTdbItemCreate(struct trackDb *tdbChild,sortOrder_t *sortOrder)
// creates a sortable tdb item struct, given a child tdb and its parent's sort table
// Errors in interpreting a passed in sortOrder will return NULL
{
sortableTdbItem *item = NULL;
if (tdbChild == NULL || tdbChild->shortLabel == NULL)
return NULL;
AllocVar(item);
item->tdb = tdbChild;
if (sortOrder != NULL) // Add some sort buttons
{
int sIx=0;
for (sIx=sortOrder->count - 1;sIx>=0;sIx--) // walk backwards to ensure sort order in columns
{
sortColumn *column = NULL;
AllocVar(column);
column->fwd = sortOrder->forward[sIx];
char *col = sortOrder->column[sIx];
if (!subgroupFind(item->tdb, col, &(column->value)))
{
if (sameString(col, SUBTRACK_COLOR_SUBGROUP))
{
// convert RGB color to hue so alpha sort can compare
column->value = subtrackColorToCompare(tdbChild);
}
else
{
char *setting = trackDbSetting(item->tdb,col);
if (setting != NULL)
column->value = cloneString(setting);
// No subgroup, assume there is a matching setting (eg longLabel)
}
}
if (column->value != NULL)
slAddHead(&(item->columns), column);
else
{
freez(&column);
if (item->columns != NULL)
slFreeList(&(item->columns));
freeMem(item);
return NULL; // sortOrder setting doesn't match items to be sorted.
}
}
}
return item;
}
static int sortableTdbItemsCmp(const void *va, const void *vb)
// Compare two sortable tdb items based upon sort columns.
{
const sortableTdbItem *a = *((sortableTdbItem **)va);
const sortableTdbItem *b = *((sortableTdbItem **)vb);
sortColumn *colA = a->columns;
sortColumn *colB = b->columns;
int compared = 0;
for (;compared==0 && colA!=NULL && colB!=NULL;colA=colA->next,colB=colB->next)
{
if (colA->value != NULL && colB->value != NULL)
compared = strcmp(colA->value, colB->value) * (colA->fwd? 1: -1);
}
if (compared != 0)
return compared;
return strcasecmp(a->tdb->shortLabel, b->tdb->shortLabel); // Last chance
}
void sortTdbItemsAndUpdatePriorities(sortableTdbItem **items)
// sort items in list and then update priorities of item tdbs
{
if (items != NULL && *items != NULL)
{
slSort(items, sortableTdbItemsCmp);
int priority=1;
sortableTdbItem *item;
for (item = *items; item != NULL; item = item->next)
item->tdb->priority = (float)priority++;
}
}
void sortableTdbItemsFree(sortableTdbItem **items)
// Frees all memory associated with a list of sortable tdb items
{
if (items != NULL && *items != NULL)
{
sortableTdbItem *item;
for (item = *items; item != NULL; item = item->next)
{
sortColumn *column;
for (column = item->columns; column != NULL; column = column->next)
freeMem(column->value);
slFreeList(&(item->columns));
}
slFreeList(items);
}
}
static boolean colonPairToStrings(char * colonPair,char **first,char **second)
// Will set *first and *second to NULL. Must free any string returned!
// No colon: value goes to *first
{
if (first)
*first =NULL; // default to NULL !
if (second)
*second=NULL;
if (colonPair != NULL)
{
if (strchr(colonPair,':'))
{
if (second)
*second = cloneString(strchr(colonPair,':') + 1);
if (first)
*first = strSwapChar(cloneString(colonPair),':',0);
}
else if (first)
*first = cloneString(colonPair);
return (*first != NULL || *second != NULL);
}
return FALSE;
}
boolean colonPairToInts(char * colonPair,int *first,int *second)
{ // Non-destructive. Only sets values if found. No colon: value goes to *first
char *a=NULL;
char *b=NULL;
if (colonPairToStrings(colonPair,&a,&b))
{
if (a!=NULL)
{
if (first)
*first = atoi(a);
freeMem(a);
}
if (b!=NULL)
{
if (second)
*second = atoi(b);
freeMem(b);
}
return TRUE;
}
return FALSE;
}
boolean colonPairToDoubles(char * colonPair,double *first,double *second)
{ // Non-destructive. Only sets values if found. No colon: value goes to *first
char *a=NULL;
char *b=NULL;
if (colonPairToStrings(colonPair,&a,&b))
{
if (a!=NULL)
{
if (first)
*first = strtod(a,NULL);
freeMem(a);
}
if (b!=NULL)
{
if (second)
*second = strtod(b,NULL);
freeMem(b);
}
return TRUE;
}
return FALSE;
}
static void chopUpValues(filterBy_t *filterBy)
/* Chop up strings in filterBy or FilterValues statement. We look for optional labels
* and optional CSS inside curly braces.
*/
{
struct slName *val = filterBy->slValues;
for (;val!=NULL;val=val->next)
{
// chip the style off the end of value or value|label
char *chipper = strrchr(val->name,'{');
if (chipper != NULL)
{
if (val == filterBy->slValues) // First one
{
filterBy->styleFollows = (lastChar(chipper) == '}');
if (filterBy->styleFollows == FALSE) // Must be closed at the end of the string or
filterBy->styleFollows = (*(chipper + 1) == '#'); // Legacy: color only
}
if (filterBy->styleFollows == FALSE)
errAbort("filterBy values either all end in {CSS style} or none do.");
*chipper++ = 0; // delimit by null
char *end = chipper + (strlen(chipper) - 1);
if (*end == '}')
*end = 0;
else if (*(chipper + 1) != '#') // Legacy: Could be color only definition
errAbort("filterBy values ending in style must be enclosed in {curly brackets}.");
}
else if (filterBy->styleFollows)
errAbort("filterBy values either all end in {CSS style} or none do.");
if (filterBy->useIndex)
strSwapChar(val->name,'_',' '); // value is a label so swap underscores
else
{
// now chip the label off the end of value name
chipper =strchr(val->name,'|');
if (chipper != NULL)
{
if (val == filterBy->slValues) // First one
filterBy->valueAndLabel = TRUE;
if (filterBy->valueAndLabel == FALSE)
errAbort("filterBy values either all have labels (as value|label) "
"or none do.");
*chipper++ = 0; // The label is found inside filters->svValues as the next string
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.");
}
}
}
char *extractFieldNameNew(char *trackDbVariable, char *filterType)
/* Extract field name from a filter trackDb variable. Variables are filter*.column */
{
char *ptr = strchr(trackDbVariable, '.');
if (ptr == NULL)
errAbort("%s doesn't have a '.' in it", trackDbVariable);
return ptr + 1;
}
char *extractFieldNameOld(char *trackDbVariable, char *filterType)
/* Extract field name from a filter trackDb variable. Variables can either be
* Filter* or .Filter* */
{
char *field = cloneString(trackDbVariable);
int ix = strlen(field) - strlen(filterType);
field[ix] = '\0';
if (field[ix - 1] == '.')
field[ix - 1] = '\0';
return field;
}
static char *getFilterValueDefaultsSetting(struct cart *cart, struct trackDb *tdb, char *field)
// grab the default setting for a filterValues statement
{
char defaultsSetting[4096];
safef(defaultsSetting, sizeof defaultsSetting, "%s.%s", FILTER_VALUES_DEFAULT_NAME_LOW, field);
char *defaults = cartOrTdbString(cart, tdb, defaultsSetting, NULL);
if (defaults == NULL)
{
safef(defaultsSetting, sizeof defaultsSetting, "%s.%s", field, FILTER_VALUES_DEFAULT_NAME_CAP);
defaults = cartOrTdbString(cart, tdb, defaultsSetting, NULL);
}
if (defaults == NULL)
{
safef(defaultsSetting, sizeof defaultsSetting, "%s%s", field, FILTER_VALUES_DEFAULT_NAME_CAP);
defaults = cartOrTdbString(cart, tdb, defaultsSetting, NULL);
}
return defaults;
}
static char *getLabelSetting(struct cart *cart, struct trackDb *tdb, char *field)
{
char labelSetting[4096];
safef(labelSetting, sizeof labelSetting, "%s.%s", FILTER_LABEL_NAME_LOW, field);
char *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);
}
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("track %s: FilterValues setting of field '%s' must have a value.", tdb->track, 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);
+struct asColumn *asCol = (as != NULL) ? asColumnFind(as, field) : NULL;
if (asCol != NULL)
filterBy->title = asCol->comment;
-else
+else if (as != NULL && getLabelSetting(cart, tdb, field) == NULL)
+ // Only error when there's an autoSql but the field is missing AND there's
+ // no filterLabel override. superTracks/noData tdbs have as==NULL and can
+ // legitimately declare filterValues on virtual fields they only aggregate.
errAbort("Track %s: Building filter on field %s which is not in AS file.", tdb->track, 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", 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, 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 */
{
+// Not every tdb has an autoSql: superTracks and tracks pointing at a
+// bigData file that isn't reachable at UI time both return NULL here.
+// That's fine for filterValues.* settings as long as a filterLabel.*
+// override is provided; buildFilterBy() already tolerates a NULL `as`.
struct asObject *as = asForTdb(NULL, tdb);
-if (as == NULL)
- errAbort("Track %s: Unable to get autoSql for %s", tdb->track, name);
filterBy_t *filterByList = NULL, *filter;
struct trackDbFilter *fieldFilter;
while ((fieldFilter = slPopHead(&trackDbFilters)) != 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
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;ixcolumn = filter;
filter += strlen(filter) + 1;
first = strchr(filter,'=');
if (first != NULL)
*first = '\0';
else
errAbort("filterBySetGet() expected '=' divider between table column and options list: %s", filters[ix]);
filterBy->title = strSwapChar(filter,'_',' '); // Title does not have underscores
filter += strlen(filter) + 1;
// Are values indexes to the string titles?
if (filter[0] == '+')
{
filter += 1;
filterBy->useIndex = TRUE;
}
// Now set up each of the values which may have 1-3 parts (value|label{style})
// the slName list will have the 3 parts delimited by null value\0label\0style\0
stripString(filter, "\""); // Remove any double quotes now and chop by commmas
filterBy->slValues = slNameListFromComma(filter);
chopUpValues(filterBy);
slAddTail(&filterBySet,filterBy); // Keep them in order (only a few)
if (cart != NULL)
{
char suffix[256];
safef(suffix, sizeof(suffix), "%s.%s", subName, filterBy->column);
boolean parentLevel = isNameAtParentLevel(tdb,name);
if (cartLookUpVariableClosestToHome(cart,tdb,parentLevel,suffix,&(filterBy->htmlName)))
{
filterBy->slChoices = cartOptionalSlNameList(cart,filterBy->htmlName);
freeMem(filterBy->htmlName);
}
}
// Note: cannot use found name above because that may be at a higher (composite/view) level
int len = strlen(name) + strlen(filterBy->column) + 15;
filterBy->htmlName = needMem(len);
safef(filterBy->htmlName, len, "%s.%s.%s", name,subName,filterBy->column);
}
freeMem(setting);
return filterBySet;
}
filterBy_t *filterBySetGet(struct trackDb *tdb, struct cart *cart, char *name)
/* Gets one or more "filterBy" settings (ClosestToHome). returns NULL if not found */
{
return filterBySetGetGuts(tdb, cart, name, "filterBy", FILTER_BY);
}
filterBy_t *highlightBySetGet(struct trackDb *tdb, struct cart *cart, char *name)
/* Gets one or more "highlightBy" settings (ClosestToHome). returns NULL if not found */
{
return filterBySetGetGuts(tdb, cart, name, "highlightBy", HIGHLIGHT_BY);
}
void filterBySetFree(filterBy_t **filterBySet)
// Free a set of filterBy structs
{
if (filterBySet != NULL)
{
while (*filterBySet != NULL)
{
filterBy_t *filterBy = slPopHead(filterBySet);
if (filterBy->slValues != NULL)
slNameFreeList(filterBy->slValues);
if (filterBy->slChoices != NULL)
slNameFreeList(filterBy->slChoices);
if (filterBy->htmlName != NULL)
freeMem(filterBy->htmlName);
freeMem(filterBy->column);
freeMem(filterBy);
}
}
}
static char *filterByClauseStd(filterBy_t *filterBy)
// returns the SQL where clause for a single filterBy struct in the standard cases
{
int count = slCount(filterBy->slChoices);
struct dyString *dyClause = dyStringNew(256);
sqlDyStringPrintf(dyClause, "%s", filterBy->column);
if (count == 1)
sqlDyStringPrintf(dyClause, " = ");
else
sqlDyStringPrintf(dyClause, " in (");
struct slName *slChoice = NULL;
boolean first = TRUE;
for (slChoice = filterBy->slChoices;slChoice != NULL;slChoice=slChoice->next)
{
if (!first)
sqlDyStringPrintf(dyClause, ",");
first = FALSE;
if (filterBy->useIndex)
sqlDyStringPrintf(dyClause, "%d",atoi(slChoice->name)); // a number converted to a string
else
sqlDyStringPrintf(dyClause, "\"%s\"",slChoice->name);
}
if (dyStringLen(dyClause) == 0)
{
dyStringFree(&dyClause);
return NULL;
}
if (count > 1)
sqlDyStringPrintf(dyClause, ")");
return dyStringCannibalize(&dyClause);
}
char *filterByClause(filterBy_t *filterBy)
// returns the SQL where clause for a single filterBy struct
{
if (filterByAllChosen(filterBy))
return NULL;
else
return filterByClauseStd(filterBy);
}
struct dyString *dyAddFilterByClause(struct cart *cart, struct trackDb *tdb,
struct dyString *extraWhere,char *column, boolean *and)
// creates the where clause condition to support a filterBy setting.
// Format: filterBy column:Title=value,value [column:Title=value|label,value|label,value|label])
// filterBy filters are multiselect's so could have multiple values selected.
// thus returns the "column1 in (...) and column2 in (...)" clause.
// if 'column' is provided, and there are multiple filterBy columns, only the named column's
// clause is returned.
// The 'and' param and dyString in/out allows stringing multiple where clauses together
{
filterBy_t *filterBySet = filterBySetGet(tdb, cart,NULL);
if (filterBySet== NULL)
return extraWhere;
filterBy_t *filterBy = filterBySet;
for (;filterBy != NULL; filterBy = filterBy->next)
{
if (column != NULL && differentString(column,filterBy->column))
continue;
char *clause = filterByClause(filterBy);
if (clause != NULL)
{
if (*and)
sqlDyStringPrintf(extraWhere, " AND ");
sqlDyStringPrintf(extraWhere,"%-s", clause);
freeMem(clause);
*and = TRUE;
}
}
filterBySetFree(&filterBySet);
return extraWhere;
}
char *filterBySetClause(filterBy_t *filterBySet)
// returns the "column1 in (...) and column2 in (...)" clause for a set of filterBy structs
{
struct dyString *dyClause = dyStringNew(256);
boolean notFirst = FALSE;
filterBy_t *filterBy = NULL;
for (filterBy = filterBySet;filterBy != NULL; filterBy = filterBy->next)
{
char *clause = filterByClause(filterBy);
if (clause != NULL)
{
if (notFirst)
sqlDyStringPrintf(dyClause, " AND ");
sqlDyStringPrintf(dyClause,"%-s", clause);
freeMem(clause);
notFirst = TRUE;
}
}
if (dyStringLen(dyClause) == 0)
{
dyStringFree(&dyClause);
return NULL;
}
return dyStringCannibalize(&dyClause);
}
void filterBySetCfgUiOption(filterBy_t *filterBy, struct slName *slValue, int ix)
/* output one option for filterBy UI */
{
char varName[32];
char *label = NULL;
char *name = NULL;
if (filterBy->useIndex)
{
safef(varName, sizeof(varName), "%d",ix);
name = varName;
label = slValue->name;
}
else
{
label = (filterBy->valueAndLabel? slValue->name + strlen(slValue->name)+1: slValue->name);
name = slValue->name;
}
printf("\n",label);
}
static boolean filterByColumnIsMultiple(struct cart *cart, struct trackDb *tdb, char *setting)
/* Is this filter setting expecting multiple items (e.g. has checkboxes in the UI) */
{
return (sameString(setting, FILTERBY_MULTIPLE) ||
sameString(setting, FILTERBY_MULTIPLE_LIST_OR) ||
sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_OR) ||
sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_AND) ||
sameString(setting, FILTERBY_MULTIPLE_LIST_AND));
}
static boolean advancedFilter(struct cart *cart, struct trackDb *tdb, char *setting)
{
if (!tdbIsBigBed(tdb))
return FALSE;
return sameString(setting, FILTERBY_MULTIPLE_LIST_OR) || sameString(setting, FILTERBY_MULTIPLE_LIST_AND);
}
void filterBySetCfgUiGuts(struct cart *cart, struct trackDb *tdb,
filterBy_t *filterBySet, boolean onOneLine,
char *filterTypeTitle, char *selectIdPrefix, char *allLabel, char *prefix, boolean isHighlight)
// Does the UI for a list of filterBy structure for either filterBy or highlightBy controls
// isHighlight controls the variable name for the cart
{
if (filterBySet == NULL)
return;
#define FILTERBY_HELP_LINK "Help"
int count = slCount(filterBySet);
if (count == 1)
puts("
Match if ");
cgiMakeRadioButton(cartSettingString, HIGHLIGHTBY_MULTIPLE_LIST_AND, sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_AND));
printf(" all ");
cgiMakeRadioButton(cartSettingString, HIGHLIGHTBY_MULTIPLE_LIST_OR, sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_OR));
printf(" one or more match
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
");
// value is always "All", even if label is different, to simplify javascript code
int valIx = 1;
if (filterByColumnIsMultiple(cart, tdb, setting))
printf( "
");
}
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 */
{
char selectIdPrefix[4096];
safef(selectIdPrefix, sizeof(selectIdPrefix), "fbc_%s", prefix);
// Our checklists use ddcl.js, which doesn't seem to play nicely when elements have id strings that include '.'
replaceChar(selectIdPrefix, '.', '_');
filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", selectIdPrefix, "All", prefix, FALSE);
}
void highlightBySetCfgUi(struct cart *cart, struct trackDb *tdb,
filterBy_t *filterBySet, boolean onOneLine, char *prefix, boolean isHighlight)
/* Does the highlight UI for a list of filterBy structure */
{
char selectIdPrefix[4096];
safef(selectIdPrefix, sizeof(selectIdPrefix), "hbc_%s", prefix);
// Our checklists use ddcl.js, which doesn't seem to play nicely when elements have id strings that include '.'
replaceChar(selectIdPrefix, '.', '_');
filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Highlight", selectIdPrefix, "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)
{
int ix;
for (ix=0;ixcount;ix++)
{
int sIx = stringArrayIx(dividers->subgroups[ix],membership->subgroups,
membership->count);
if ((lastDivide[ix] == (void*)NULL && sIx >= 0)
|| (lastDivide[ix] != (void*)NULL && sIx < 0)
|| (strcmp(lastDivide[ix],membership->membership[sIx]) != 0) )
{
division = TRUE;
if (lastDivide[ix] != (void*)NULL)
freeMem(lastDivide[ix]);
lastDivide[ix] = (sIx<0 ? (void*)NULL : cloneString(membership->membership[sIx]));
}
}
}
//if (division)
// DIVIDER_PRINT(COLOR_DARKGREEN);
}
return division;
}
static void indentIfNeeded(hierarchy_t*hierarchy,membership_t *membership)
// inserts any needed indents
{
int indent = 0;
if (hierarchy && hierarchy->count>0)
{
int ix;
for (ix=0;ixcount;ix++)
{
int iIx = stringArrayIx(membership->membership[ix], hierarchy->membership,
hierarchy->count);
if (iIx >= 0)
{
indent = hierarchy->indents[iIx];
break; // Only one
}
}
}
for (;indent>0;indent--)
puts(" ");
}
// FIXME FIXME Should be able to use membersForAll struct to set default sort order from subGroups
// FIXME FIXME This should be done in hgTrackDb at load time and should change tag values to
// FIXME FIXME ensure js still works
boolean tdbAddPrioritiesFromCart(struct cart *cart, struct trackDb *tdbList)
// Updates the tdb->priority from cart for all tracks in list and their descendents.
{
char htmlIdentifier[1024];
struct trackDb *tdb;
boolean cartPriorities = FALSE;
for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
{
safef(htmlIdentifier, sizeof(htmlIdentifier), "%s.priority", tdb->track);
char *cartHas = cartOptionalString(cart,htmlIdentifier);
if (cartHas != NULL)
{
tdb->priority = atof(cartHas);
cartPriorities = TRUE;
}
if (tdbAddPrioritiesFromCart(cart, tdb->subtracks))
cartPriorities = TRUE;
}
return cartPriorities;
}
boolean tdbSortPrioritiesFromCart(struct cart *cart, struct trackDb **tdbList)
// Updates the tdb->priority from cart then sorts the list anew.
// Returns TRUE if priorities obtained from cart
{
boolean cartPriorities = tdbAddPrioritiesFromCart(cart, *tdbList);
slSort(tdbList, trackDbCmp);
return cartPriorities;
}
boolean tdbRefSortPrioritiesFromCart(struct cart *cart, struct slRef **tdbRefList)
// Updates the tdb->priority from cart then sorts the list anew.
// Returns TRUE if priorities obtained from cart
{
char htmlIdentifier[128];
struct slRef *tdbRef;
boolean cartPriorities = FALSE;
for (tdbRef = *tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
{
struct trackDb *tdb = tdbRef->val;
safef(htmlIdentifier, sizeof(htmlIdentifier), "%s.priority", tdb->track);
char *cartHas = cartOptionalString(cart,htmlIdentifier);
if (cartHas != NULL)
{
tdb->priority = atof(cartHas);
cartPriorities = TRUE;
}
}
slSort(tdbRefList, trackDbRefCmp);
return cartPriorities;
}
void bigRmskCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed)
/* UI for the bigRmsk track */
{
boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
boolean showUnalignedExtents = cartUsualBoolean(cart, BIGRMSK_SHOW_UNALIGNED_EXTENTS, BIGRMSK_SHOW_UNALIGNED_EXTENTS_DEFAULT);
boolean showLabels = cartUsualBoolean(cart, BIGRMSK_SHOW_LABELS, BIGRMSK_SHOW_LABELS_DEFAULT);
boolean origPackViz = cartUsualBoolean(cart, BIGRMSK_ORIG_PACKVIZ, BIGRMSK_ORIG_PACKVIZ_DEFAULT);
boolean regexpFilter = cartUsualBoolean(cart, BIGRMSK_REGEXP_FILTER, BIGRMSK_REGEXP_FILTER_DEFAULT);
char * nameFilter = cartUsualString(cart, BIGRMSK_NAME_FILTER, BIGRMSK_NAME_FILTER_DEFAULT);
puts(" ");
printf("Filter by 'name#class/subclass' field (e.g. '*Alu*' would match 'FRAM#SINE/Alu'): ");
cgiMakeTextVar(BIGRMSK_NAME_FILTER, cartUsualString(cart, BIGRMSK_NAME_FILTER, nameFilter), 20);
cgiMakeCheckBox(BIGRMSK_REGEXP_FILTER, regexpFilter);
puts(" regular expression");
cgiMakeCheckBox(BIGRMSK_SHOW_UNALIGNED_EXTENTS, showUnalignedExtents);
puts(" Show unaligned repeat regions");
cgiMakeCheckBox(BIGRMSK_SHOW_LABELS, showLabels);
puts(" Show annotation labels");
cgiMakeCheckBox(BIGRMSK_ORIG_PACKVIZ, origPackViz);
puts(" Display original pack visualization");
cfgEndBox(boxed);
}
void lollyCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed)
/* UI for the wiggle track */
{
int maxHeightPixels;
int minHeightPixels;
char option[256];
int defaultHeight; /* pixels per item */
int settingsDefault;
#define MIN_HEIGHT_LOLLY 32
cartTdbFetchMinMaxPixels(cart, tdb, MIN_HEIGHT_LOLLY, atoi(DEFAULT_HEIGHT_PER), atoi(DEFAULT_HEIGHT_PER),
&minHeightPixels, &maxHeightPixels, &settingsDefault, &defaultHeight);
boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
printf("
");
double minY; /* from trackDb or cart */
double maxY; /* from trackDb or cart */
double tDbMinY; /* data range limits from trackDb type line */
double tDbMaxY; /* data range limits from trackDb type line */
char *words[8]; /* to parse the trackDb type line */
int wordCount = 0; /* to parse the trackDb type line */
wigFetchMinMaxYWithCart(cart, tdb, name, &minY, &maxY, &tDbMinY, &tDbMaxY, wordCount, words);
printf("
");
cfgEndBox(boxed);
// N.B. scoreCfgUi maybe creates a box, so this is called after cfgEndBox
// unclear what the logic is with box creation here
scoreCfgUi(db, cart,tdb,name,title,1000,boxed);
}
void labelMakeCheckBox(struct cart *cart, struct trackDb *tdb, char *sym, char *desc,
boolean defaultOn)
/* add a checkbox for the user to select a component of a label (e.g. ID, name, other info).
* NOTE: This does not have a track name argument, so the correct tdb must be passed in:
* if setting is at composite level, then pass in composite tdb, likewise for view. */
{
char suffix[512];
safef(suffix, sizeof(suffix), "label.%s", sym);
boolean option = cartUsualBooleanClosestToHome(cart, tdb, FALSE, suffix, defaultOn);
char cartVar[1024];
safef(cartVar, sizeof cartVar, "%s.%s", trackHubSkipHubName(tdb->track), suffix);
cgiMakeCheckBox(cartVar, option);
printf(" %s ", desc);
}
void geneTrackMakeCheckBox(struct cart *cart, struct trackDb *tdb, char *sym, char *desc,
boolean defaultOn)
/* add a checkbox for the user to select a component of a label (e.g. ID, name, other info).
* NOTE: This does not have a track name argument, so the correct tdb must be passed in:
* if setting is at composite level, then pass in composite tdb, likewise for view. */
{
char suffix[512];
safef(suffix, sizeof(suffix), "geneTrack.%s", sym);
boolean option = cartUsualBooleanClosestToHome(cart, tdb, FALSE, suffix, defaultOn);
char cartVar[1024];
safef(cartVar, sizeof cartVar, "%s.%s", tdb->track, suffix);
cgiMakeCheckBox(cartVar, option);
printf(" %s ", desc);
}
static void freqSourceSelect(struct cart *cart, struct trackDb *tdb, char *name)
/* Make a select input for preferred source of allele frequencies from
* trackDb setting freqSourceOrder. */
{
char *freqSourceOrder = cloneString(trackDbSetting(tdb, "freqSourceOrder"));
if (isEmpty(freqSourceOrder))
return;
int fsCount = countSeparatedItems(freqSourceOrder, ',');
char *values[fsCount];
chopCommas(freqSourceOrder, values);
char *menu[fsCount];
int i;
for (i = 0; i < fsCount; i++)
{
// Change label of GnomAD to "GnomAD genomes" for clarity when "GnomAD_exomes" is present.
if (sameString(values[i], "GnomAD") && stringIx("GnomAD_exomes", values) >= 0)
menu[i] = "GnomAD genomes";
else
{
menu[i] = cloneString(values[i]);
strSwapChar(menu[i], '_', ' ');
}
}
boolean parentLevel = isNameAtParentLevel(tdb, name);
char *freqProj = cartOptionalStringClosestToHome(cart, tdb, parentLevel, "freqProj");
puts("Frequency source/project to use for Minor Allele Frequency (MAF):");
char cartVar[1024];
safef(cartVar, sizeof cartVar, "%s.freqProj", name);
cgiMakeDropListWithVals(cartVar, menu, values, ArraySize(menu), freqProj);
puts(" ");
}
struct trackDb *tdbOrAncestorByName(struct trackDb *tdb, char *name)
/* For reasons Angie cannot fathom, if a composite or view is passed to cfgByCfgType then
* cfgByCfgType passes a leaf subtrack to its callees like bigDbSnpCfgUi. That is why we
* see so many calls to isNameAtParentLevel, which returns true if the tdb was originally
* at the composite or view level, which we can only tell by comparing with the original track name.
* labelMakeCheckBox, called by many handlers in hgTrackUi that must be always top-level
* (or have a special handler that bypasses cfgByCfgType like refSeqComposite),
* is blissfully unaware of this. It uses the same tdb for looking in cart ClosestToHome
* and for making the HTML element's cart var name, trusting that the correct tdb has been
* handed to it.
* So in order for a callee of cfgByCfgType to call labelMakeCheckBox with the correct tdb,
* we need to walk back up comparing name like isNameAtParentLevel does.
* If name doesn't match tdb or any of its ancestors then this returns NULL. */
{
struct trackDb *correctTdb;
for (correctTdb = tdb; correctTdb != NULL; correctTdb = correctTdb->parent)
if (startsWithWordByDelimiter(correctTdb->track, '.', name))
return correctTdb;
return NULL;
}
void snp153MakeCheckboxGroupSetClearButton(char *buttonVar, boolean isSet)
/* Make a button for setting or clearing a set of checkboxes with the same name.
* Uses only javascript to change the checkboxes, no resubmit. */
{
char id[256];
char javascript[256];
safef(javascript, sizeof(javascript),
"$(\"input:checkbox[name^='%s']\").each(function(){this.checked = %s;$(this).trigger('change');})",
buttonVar, isSet ? "true" : "false");
safef(id, sizeof id, "%s_grp%sBut", buttonVar, isSet ? "Set" : "Clr");
cgiMakeOnClickButton(id, javascript, isSet ? JS_SET_ALL_BUTTON_LABEL : JS_CLEAR_ALL_BUTTON_LABEL);
}
struct trackDb *snp125FetchGeneTracks(char *database, struct cart *cart)
/* Get a list of genePred tracks. */
{
struct trackDb *tdbList = NULL;
struct hash *trackHash = trackHashMakeWithComposites(database,NULL,&tdbList,FALSE);
struct sqlConnection *conn = hAllocConn(database);
struct slName *justGenePredTables = hTrackTablesOfType(conn, "genePred%%"), *gt;
struct slName *bigGenePredTables = hTrackTablesOfType(conn, "bigGenePred%%");
struct slName *genePredTables = slCat(justGenePredTables,bigGenePredTables);
struct trackDb *geneTdbList = NULL, *gTdb;
if (genePredTables != NULL)
{
for (gt = genePredTables; gt != NULL; gt = gt->next)
{
gTdb = hashFindVal(trackHash, gt->name);
if (gTdb && sameString(gTdb->grp, "genes"))
{
// We are going to overwrite gTdb's next pointer and possibly its priority,
// so make a shallow copy:
gTdb = CloneVar(gTdb);
if (gTdb->parent)
gTdb->priority = (gTdb->parent->priority + gTdb->priority/1000);
slAddHead(&geneTdbList, gTdb);
}
}
slSort(&geneTdbList, trackDbCmp);
}
hFreeConn(&conn);
return geneTdbList;
}
void snp153OfferGeneTracksForFunction(char *database, struct cart *cart, char *name, struct trackDb *tdb, struct trackDb *correctTdb)
/* Get a list of genePred tracks and make checkboxes to enable hgc's functional
* annotations. */
{
struct trackDb *geneTdbList = NULL, *gTdb;
geneTdbList = snp125FetchGeneTracks(database, cart);
if (geneTdbList)
{
char sectionVar[256];
safef(sectionVar, sizeof(sectionVar), "%s.geneTracks", name);
jsBeginCollapsibleSection(cart, name, sectionVar,
"Use Gene Tracks for Functional Annotation", FALSE);
printf(" On details page, show function and coding differences relative to: \n");
char buttonVarPrefix[256];
safef(buttonVarPrefix, sizeof(buttonVarPrefix), "%s.geneTrack", name);
snp153MakeCheckboxGroupSetClearButton(buttonVarPrefix, TRUE);
snp153MakeCheckboxGroupSetClearButton(buttonVarPrefix, FALSE);
struct slName *defaultGeneTracks = slNameListFromComma(trackDbSetting(tdb, "defaultGeneTracks"));
int i;
int numCols = 4;
int menuSize = slCount(geneTdbList);
char **values = needMem(menuSize*sizeof(char *));
char **labels = needMem(menuSize*sizeof(char *));
for (i = 0, gTdb = geneTdbList; i < menuSize && gTdb != NULL; i++, gTdb = gTdb->next)
{
values[i] = gTdb->track;
if (gTdb->parent != NULL)
{
struct dyString *dy = dyStringNew(0);
if (gTdb->parent->parent != NULL &&
!startsWith(gTdb->parent->parent->shortLabel, gTdb->parent->shortLabel))
dyStringPrintf(dy, "%s: ", gTdb->parent->parent->shortLabel);
if (!startsWith(gTdb->parent->shortLabel, gTdb->shortLabel))
dyStringPrintf(dy, "%s: ", gTdb->parent->shortLabel);
dyStringPrintf(dy, "%s", gTdb->shortLabel);
labels[i] = dyStringCannibalize(&dy);
}
else
labels[i] = gTdb->shortLabel;
}
printf(" \n");
puts("
");
for (i = 0; i < menuSize; ++i)
{
if (i > 0 && (i % numCols) == 0)
printf("
");
snp153OfferGeneTracksForFunction(db, cart, name, leafTdb, correctTdb);
// End wrapper table for collapsible sections:
puts("
");
wigOption(cart, name, title, leafTdb);
cfgEndBox(boxed);
}
void cfgByCfgType(eCfgType cType,char *db, struct cart *cart, struct trackDb *tdb,char *prefix,
char *title, boolean boxed)
// Methods for putting up type specific cfgs used by composites/subtracks in hui.c
{
// When only one subtrack, then show it's cfg settings instead of composite/view level settings
// This simplifies the UI where hgTrackUi won't have 2 levels of cfg,
// while hgTracks still supports rightClick cfg of the subtrack.
// Don't do this if noParentConfig is specified in the composite parent
boolean noParentConfig = tdb && trackDbSetting(tdb, "noParentConfig");
if (!noParentConfig && (configurableByAjax(tdb,cType) > 0)) // Only if subtrack's configurable by ajax do we
{ // consider this option
if (tdbIsComposite(tdb) // called for the composite
&& !isCustomComposite(tdb)
&& !tdbIsCompositeView(tdb->subtracks) // and there is no view level
&& slCount(tdb->subtracks) == 1) // and there is only one subtrack
{
tdb = tdb->subtracks; // show subtrack cfg instead
prefix = tdb->track;
}
else if (tdbIsSubtrack(tdb) // called with subtrack
&& tdbIsCompositeView(tdb->parent) // subtrack has view
&& differentString(prefix,tdb->track) // and this has been called FOR the view
&& slCount(tdb->parent->subtracks) == 1) // and view has only one subtrack
prefix = tdb->track; // removes reference to view level
}
// Cfg could be explicitly blocked, but if tdb is example subtrack
// then blocking should have occurred before we got here.
if (!tdbIsSubtrack(tdb) && trackDbSettingBlocksConfiguration(tdb,FALSE))
return;
// composite/view must pass in example subtrack
// NOTE: if subtrack types vary then there shouldn't be cfg at composite/view level!
while (tdb->subtracks)
tdb = tdb->subtracks;
switch(cType)
{
case cfgBedScore:
bedScoreCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgPeak:
encodePeakCfgUi(cart,tdb,prefix,title,boxed);
break;
case cfgWig: wigCfgUi(cart,tdb,prefix,title,boxed);
break;
case cfgWigMaf: wigMafCfgUi(cart,tdb,prefix,title,boxed, db);
break;
case cfgGenePred: genePredCfgUi(db, cart,tdb,prefix,title,boxed);
break;
case cfgChain: chainCfgUi(db,cart,tdb,prefix,title,boxed, NULL);
break;
case cfgNetAlign: netAlignCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgBedFilt: bedFiltCfgUi(cart,tdb,prefix,title, boxed);
break;
case cfgBam: bamCfgUi(cart, tdb, prefix, title, boxed);
break;
case cfgVcf: vcfCfgUi(cart, tdb, prefix, title, boxed);
break;
case cfgLong: longRangeCfgUi(cart, tdb, prefix, title, boxed);
break;
case cfgSnake: snakeCfgUi(cart, tdb, prefix, title, boxed);
break;
case cfgPsl: pslCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgBarChart: barChartCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgInteract: interactCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgBigRmsk: bigRmskCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgLollipop: lollyCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgHic: hicCfgUi(db,cart,tdb,prefix,title,boxed);
break;
case cfgBigDbSnp: bigDbSnpCfgUi(db, cart, tdb, prefix, title, boxed);
break;
default: warn("Track type is not known to multi-view composites. type is: %d ",
cType);
break;
}
}
char *encodeRestrictionDate(char *db,struct trackDb *trackDb,boolean excludePast)
// Create a string for ENCODE restriction date of this track
// if return is not null, then free it after use
{
if (!trackDb)
return NULL;
char *date = NULL;
if (metadataForTable(db,trackDb,NULL) != NULL)
{
date = cloneString((char *)metadataFindValue(trackDb,"dateUnrestricted"));
if (date != NULL)
date = strSwapChar(date, ' ', 0); // Truncate time (not expected, but just in case)
if (excludePast && !isEmpty(date) && dateIsOld(date, MDB_ENCODE_DATE_FORMAT))
freez(&date);
}
return date;
}
/* Subtrack configuration settings */
struct subtrackConfigSettings
{
sortOrder_t *sortOrder; /* from trackDb setting */
boolean useDragAndDrop; /* from trackDb setting */
boolean restrictions; /* from metadata ? */
boolean colorPatch; /* from trackDb setting */
boolean displayAll; /* from radiobutton */
int bgColorIx; /* from logic over other settings */
int columnCount; /* determined from trackDb settings */
};
#define LARGE_COMPOSITE_CUTOFF 30
static void printSubtrackListRadioButtons(char *parentTrack, int subCount, boolean displayAll)
// Print radio buttons for all/select
{
printf("List subtracks: ");
char javascript[JBUFSIZE];
safef(javascript, sizeof(javascript),
"showOrHideSelectedSubtracks(true);");
char buffer[SMALLBUF];
if (subCount > LARGE_COMPOSITE_CUTOFF)
safef(buffer,SMALLBUF,"%s.displaySubtracks", parentTrack);
else
safecpy(buffer,SMALLBUF,"displaySubtracks");
cgiMakeOnEventRadioButtonWithClass(buffer, "selected", !displayAll, "allOrOnly", "click", javascript);
puts("only selected/visible ");
safef(javascript, sizeof(javascript),
"showOrHideSelectedSubtracks(false);");
cgiMakeOnEventRadioButtonWithClass(buffer, "all", displayAll, "allOrOnly", "click", javascript);
printf("all");
if (subCount > 5)
printf(" ()");
}
static void printSubtrackTableHeader(struct trackDb *parentTdb, struct slRef *subtrackRefList,
struct subtrackConfigSettings *settings)
/* Print header of subtrack table, including classes describing display appearance and behavior.
Return number of columns */
{
boolean useDragAndDrop = settings->useDragAndDrop;
sortOrder_t *sortOrder = settings->sortOrder;
if (sortOrder != NULL)
puts("");
else
puts("");
int colspan = 3;
if (sortOrder != NULL)
colspan = sortOrder->count+2;
else if (!tdbIsMultiTrack(parentTdb)) // An extra column for subVis/wrench so dragAndDrop works
colspan++;
if (settings->colorPatch)
colspan++;
int columnCount = 0;
if (sortOrder != NULL)
printf("
", useDragAndDrop ? " id='noDrag' class='nodrop nodrag'" : "");
// First table row contains the display "selected/visible" or "all" radio buttons
// NOTE: list subtrack radio buttons are inside tracklist table header if
// there are no sort columns. The reason is to ensure spacing of lines
// column headers when the only column header is "Restricted Until"
printf("
", colspan);
int subCount = slCount(subtrackRefList);
printSubtrackListRadioButtons(parentTdb->track, subCount, settings->displayAll);
puts("
");
columnCount = colspan;
}
// Add column headers which are sort button links
if (sortOrder != NULL)
{
printf("
\n",
sortOrder->htmlId, sortOrder->sortOrder); // keeing track of sortOrder
columnCount++;
if (!tdbIsMultiTrack(parentTdb)) // An extra column for subVis/wrench so dragAndDrop works
{
printf("
\n");
columnCount++;
}
// Columns in tdb order (unchanging), sort in cart order (changed by user action)
int sIx=0;
for (sIx=0;sIxcount;sIx++)
{
if (sameString(SORT_ON_TRACK_NAME,sortOrder->column[sIx]))
break; // All wrangler requested sort orders have been done.
if (sameString(SORT_ON_RESTRICTED,sortOrder->column[sIx]))
break; // All wrangler requested sort orders have been done.
printf("
"); // schema column
columnCount++;
// Finally there may be a restricted until column
if (settings->restrictions)
{
if (sortOrder != NULL)
{
int sIx=sortOrder->count-1;
assert(sameString(SORT_ON_RESTRICTED,sortOrder->column[sIx]));
printf("
\n");
}
}
/********************/
/* Basic info for a controlled vocabulary term */
struct vocabBasic {
struct vocabBasic *next;
char *term;
char *description;
char *url;
};
boolean vocabSettingIsEncode(char *setting)
/* Distinguish ENCODE controlled vocab settings (first arg is cv.ra filename) from non-ENCODE
(table-based vocabs)
*/
{
if (setting && (strchr(cloneFirstWord(setting), '=') == NULL))
return TRUE;
return FALSE;
}
char *vocabLink(struct hash *vocabFieldHash, char *term, char *title)
/* Make an anchor with mouseover containing description and link if present */
{
struct vocabBasic *vocab = hashFindVal(vocabFieldHash, term);
if (vocab == NULL)
return NULL;
struct dyString *ds = dyStringNew(0);
if (vocab->url == NULL || strlen(vocab->url) == 0)
dyStringPrintf(ds, "%s",
vocab->description, term);
else
dyStringPrintf(ds, "%s\n",
vocab->description, vocab->url, term);
return dyStringCannibalize(&ds);
}
struct hash *vocabBasicFromSetting(struct trackDb *parentTdb, struct cart *cart)
/* Get description and URL for all vocabTables. Returns a hash of hashes */
{
char *spec = trackDbSetting(parentTdb, "controlledVocabulary");
if (!spec)
return NULL;
// Not yet implemented for ENCODE-style CV
if (vocabSettingIsEncode(spec))
return NULL;
struct slPair *vocabTables = slPairFromString(spec);
struct slPair *vocabTable = NULL;
struct hash *tableHash = hashNew(0);
struct sqlResult *sr;
char **row;
char query[256];
char *database = cartString(cart, "db");
for (vocabTable = vocabTables; vocabTable != NULL; vocabTable = vocabTable->next)
{
char *db = database;
char *tableSpec = (char *)vocabTable->val;
char *tableName = chopPrefix(tableSpec);
if (differentString(tableName, tableSpec))
{
chopSuffix(tableSpec);
db = tableSpec;
}
struct sqlConnection *conn = hAllocConn(db);
boolean hasUrl = FALSE;
struct hash *subgroupHash = hashNew(0);
hashAdd(tableHash, vocabTable->name, subgroupHash);
if (hHasField(db, tableName, "url"))
{
sqlSafef(query, sizeof(query), "select term, description, url from %s", tableName);
hasUrl = TRUE;
}
else
sqlSafef(query, sizeof(query), "select term, description from %s", tableName);
sr = sqlGetResult(conn, query);
while ((row = sqlNextRow(sr)) != NULL)
{
struct vocabBasic *vocab = NULL;
AllocVar(vocab);
vocab->term = cloneString(row[0]);
vocab->description = cloneString(row[1]);
if (hasUrl)
vocab->url = cloneString(row[2]);
hashAdd(subgroupHash, vocab->term, vocab);
}
sqlFreeResult(&sr);
hFreeConn(&conn);
}
return tableHash;
}
static void printSubtrackTableBody(struct trackDb *parentTdb, struct slRef *subtrackRefList,
struct subtrackConfigSettings *settings, struct cart *cart)
/* Print list of subtracks */
{
sortOrder_t *sortOrder = settings->sortOrder;
boolean useDragAndDrop = settings->useDragAndDrop;
boolean restrictions = settings->restrictions;
struct dyString *dyHtml = dyStringNew(SMALLBUF);
char buffer[SMALLBUF];
char id[SMALLBUF];
char *db = cartString(cart, "db");
// The subtracks need to be sorted by priority but only sortable and dragable will have
// non-default (cart) priorities to sort on
if (sortOrder != NULL || useDragAndDrop)
{
// preserves user's prev sort/drags, ignore returned value about where
// priorities come from
(void) tdbRefSortPrioritiesFromCart(cart, &subtrackRefList);
printf("\n", (sortOrder != NULL ? "sortable " : "") );
}
else
{
slSort(&subtrackRefList, trackDbRefCmp); // straight from trackDb.ra
puts("");
}
// Finally the big "for loop" to list each subtrack as a table row.
printf("\n\n");
membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,NULL);
struct hash *vocabHash = vocabBasicFromSetting(parentTdb, cart);
struct slRef *subtrackRef;
/* Color handling ?? */
//char *colors[2] = { COLOR_BG_DEFAULT,
// COLOR_BG_ALTDEFAULT };
char *colors[2] = { "bgLevel1",
"bgLevel1" };
int colorIx = settings->bgColorIx;
boolean noParentConfig = parentTdb && trackDbSetting(parentTdb, "noParentConfig");
for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
{
struct trackDb *subtrack = subtrackRef->val;
int ix;
// Determine whether subtrack is checked, visible, configurable, has group membership, etc.
int fourState = subtrackFourStateChecked(subtrack,cart);
boolean checkedCB = fourStateChecked(fourState);
boolean enabledCB = fourStateEnabled(fourState);
boolean visibleCB = fourStateVisible(fourState);
membership_t *membership = subgroupMembershipGet(subtrack);
eCfgType cType = cfgNone;
if (!tdbIsMultiTrack(parentTdb)) // MultiTracks never have configurable subtracks!
cType = cfgTypeFromTdb(subtrack,FALSE);
if (cType != cfgNone)
{
// Turn off configuring for certain track type or if explicitly turned off
int cfgSubtrack = configurableByAjax(subtrack,cType);
if (cfgSubtrack <= cfgNone)
cType = cfgNone;
else if (membersForAll->members[dimV] && membership != NULL)
{ // subtrack only configurable if more than one subtrack in view
// find "view" in subgroup membership: e.g. "signal"
if (-1 != (ix = stringArrayIx(membersForAll->members[dimV]->groupTag,
membership->subgroups, membership->count)))
{
int ix2; // find "signal" in set of all views
if (-1 != (ix2 = stringArrayIx(membership->membership[ix],
membersForAll->members[dimV]->tags,
membersForAll->members[dimV]->count)))
{
if (membersForAll->members[dimV]->subtrackCount[ix2] < 2)
cType = cfgNone;
}
}
}
else if (!noParentConfig && slCount(subtrackRefList) < 2 // don't bother if there is a single subtrack without noParentConfig in the composite header
&& cfgTypeFromTdb(parentTdb,FALSE) != cfgNone) // but the composite is configurable.
cType = cfgNone;
}
if (sortOrder == NULL && !useDragAndDrop)
{
char **lastDivide = NULL;
dividers_t *dividers = dividersSettingGet(parentTdb);
if (dividers)
lastDivide = needMem(sizeof(char*)*dividers->count);
if (membership && divisionIfNeeded(lastDivide,dividers,membership) )
colorIx = (colorIx == COLOR_BG_DEFAULT_IX ? COLOR_BG_ALTDEFAULT_IX
: COLOR_BG_DEFAULT_IX);
dividersFree(÷rs);
}
safef(id, sizeof(id), "%s_sel", subtrack->track);
printf("
\n",id,(!visibleCB && !settings->displayAll?" style='display:none'":""));
// Now the TD that holds the checkbox
printf("
",
(enabledCB?"":" title='view is hidden'"),
(useDragAndDrop?" class='dragHandle' title='Drag to reorder'":""));
// A hidden field to keep track of subtrack order if it could change
if (sortOrder != NULL || useDragAndDrop)
{
safef(buffer, sizeof(buffer), "%s.priority", subtrack->track);
float priority = (float)cartUsualDouble(cart, buffer, subtrack->priority);
printf("",
buffer, priority); // keeing track of priority
}
// The checkbox has identifying classes including subCB and the tag for each dimension
// (e.g. class='subCB GM12878 CTCF Peak')
dyStringClear(dyHtml);
dyStringAppend(dyHtml, "subCB"); // always first
int di;
if (membersForAll->dimensions && membership != NULL)
{
for (di=dimX;didimMax;di++)
{
if (membersForAll->members[di] && -1 !=
(ix = stringArrayIx(membersForAll->members[di]->groupTag,
membership->subgroups, membership->count)))
dyStringPrintf(dyHtml," %s",htmlEncode(membership->membership[ix]));
}
}
else if (membersForAll->abcCount && membership != NULL) // "dimensions" don't exist but may be subgroups anyway
{
for (di=dimA;didimMax;di++)
{
if (membersForAll->members[di] && -1 !=
(ix = stringArrayIx(membersForAll->members[di]->groupTag,
membership->subgroups, membership->count)))
dyStringPrintf(dyHtml," %s",htmlEncode(membership->membership[ix]));
}
}
if (membersForAll->members[dimV] && membership != NULL && -1 !=
(ix = stringArrayIx(membersForAll->members[dimV]->groupTag,
membership->subgroups, membership->count)))
dyStringPrintf(dyHtml, " %s",htmlEncode(membership->membership[ix])); // Saved view for last
// And finally the checkBox is made!
safef(buffer, sizeof(buffer), "%s_sel", subtrack->track);
if (!enabledCB)
{
dyStringAppend(dyHtml, " disabled");
cgiMakeCheckBoxFourWay(buffer,checkedCB,enabledCB,id,dyStringContents(dyHtml),
"style='cursor:pointer' title='view is hidden'");
jsOnEventById("click", id, "matSubCbClick(this);");
}
else
{
cgiMakeCheckBoxFourWay(buffer,checkedCB,enabledCB,id,dyStringContents(dyHtml),
"style='cursor:pointer'");
jsOnEventById("click", id, "matSubCbClick(this);");
}
if (useDragAndDrop)
printf(" ");
if (!tdbIsMultiTrack(parentTdb)) // MultiTracks never have independent vis
{
printf("
"); // An extra column for subVis/wrench so dragAndDrop works
enum trackVisibility vis = tdbVisLimitedByAncestors(cart,subtrack,FALSE,FALSE);
char *view = NULL;
if (membersForAll->members[dimV] && membership !=NULL
&& -1 != (ix = stringArrayIx(membersForAll->members[dimV]->groupTag, membership->subgroups,
membership->count)))
view = membership->membership[ix];
char classList[256];
if (view != NULL)
safef(classList,sizeof(classList),"clickable fauxInput%s subVisDD %s",
(visibleCB ? "":" disabled"),view); // view should be last!
else
safef(classList,sizeof(classList),"clickable fauxInput%s subVisDD",
(visibleCB ? "":" disabled"));
#define SUBTRACK_CFG_VIS "
");
// If sortable, then there must be a column per sortable dimension
if (sortOrder != NULL)
{
int sIx=0;
for (sIx=0; sIx count; sIx++)
{
ix = -1;
char *col = sortOrder->column[sIx];
if (membership)
ix = stringArrayIx(col, membership->subgroups, membership->count);
// TODO: Sort needs to expand from subGroups to labels as well
// only print the warning message for trackDb errors and not for the
// default sortable columns of trackName and dateUnrestricted
if ( (!membership || (membership && ix == -1) ) &&
!(sameString(col, "trackName") || sameString(col, "dateUnrestricted") || sameString(col, "subtrackColor")) )
{
printf("
Missing subgroup
");
}
else
{
if (ix >= 0)
{
char *term = membership->membership[ix];
char *title = membership->titles[ix];
char *titleRoot=NULL;
if (cvTermIsEmpty(col, title))
titleRoot = cloneString(" ");
else
titleRoot = labelRoot(title, NULL);
// Each sortable column requires hidden goop (in the "abbr" field currently)
// which is the actual sort on value
printf("
", subtrack->track, col, term);
printf(" ");
char *link = NULL;
if (vocabHash)
{
struct hash *colHash = hashFindVal(vocabHash, col);
if (colHash)
link = vocabLink(colHash, term, titleRoot);
}
printf("%s", link ? link : titleRoot);
puts("
",
subtrack->track, col, hue,
subtrack->colorR, subtrack->colorG, subtrack->colorB);
}
}
}
}
else // Non-sortable tables do not have sort by columns but will display a short label
{ // (which may be a configurable link)
if (settings->colorPatch)
{
printf("
");
}
// The long label column (note that it may have a metadata dropdown)
printf("
%s", subtrack->longLabel);
if (trackDbSetting(parentTdb, "wgEncode") && trackDbSetting(subtrack, "accession"))
printf(" [GEO:%s]", trackDbSetting(subtrack, "accession"));
compositeMetadataToggle(db,subtrack,NULL,TRUE,FALSE);
printf(" ");
// Embedded cfg dialogs are within the TD that contains the longLabel.
// This allows a wide item to be embedded in the table
if (cType != cfgNone)
{
// How to make this thing float to the left? Container is overflow:visible
// and contained (made in js) is position:relative; left: -{some pixels}
#define CFG_SUBTRACK_DIV ""
#define MAKE_CFG_SUBTRACK_DIV(table,view) \
printf(CFG_SUBTRACK_DIV,(table),(view)?(view):"noView")
char * view = NULL;
if (membersForAll->members[dimV] && membership != NULL && -1 !=
(ix = stringArrayIx(membersForAll->members[dimV]->groupTag,
membership->subgroups, membership->count)))
view = membership->membership[ix];
MAKE_CFG_SUBTRACK_DIV(subtrack->track,view);
}
// A schema link for each track
printf("
\n
");
makeSchemaLink(db,subtrack,"Data format");
printf(" ");
// Do we have a restricted until date?
if (restrictions)
{
char *dateDisplay = encodeRestrictionDate(db,subtrack,FALSE); // includes dates in the past
if (dateDisplay)
{
if (dateIsOld(dateDisplay, MDB_ENCODE_DATE_FORMAT))
printf("
\n
%s ",
dateDisplay);
else
printf("
\n
%s ", dateDisplay);
}
}
// End of row and free ourselves of this subtrack
puts("
\n");
boolean showCfg = trackDbSettingOn(subtrack, "showCfg");
if (showCfg)
jsInlineF(" subCfg.cfgToggle(document.getElementById(\"%s_toggle\"),\"%s\");", subtrack->track, subtrack->track);
}
// End of the table
puts("");
dyStringFree(&dyHtml);
membersForAllSubGroupsFree(parentTdb,&membersForAll);
}
static boolean membersHaveMatrix(membersForAll_t *membersForAll)
/* Check for matrix */
{
if (membersForAll->members[dimX] == NULL && membersForAll->members[dimY] == NULL)
return FALSE;
return TRUE;
}
static void printSubtrackTable(struct trackDb *parentTdb, struct slRef *subtrackRefList,
struct subtrackConfigSettings *settings, struct cart *cart)
/* Print table of subtracks */
{
// Print table tag
printf("\n
sortOrder != NULL)
dyStringPrintf(dyHtml, "sortable");
if (settings->useDragAndDrop)
{
if (dyStringLen(dyHtml) > 0)
dyStringAppendC(dyHtml,' ');
dyStringPrintf(dyHtml, "tableWithDragAndDrop");
}
printf(" class='subtracks");
if (dyStringLen(dyHtml) > 0)
{
printf(" bglevel1 %s'",dyStringContents(dyHtml));
settings->bgColorIx = COLOR_BG_ALTDEFAULT_IX;
}
else
settings->bgColorIx = COLOR_BG_DEFAULT_IX; // Start with non-default allows alternation
puts("'>");
dyStringFree(&dyHtml);
// save count of subtracks for use by footer code
int subCount = slCount(subtrackRefList);
printSubtrackTableHeader(parentTdb, subtrackRefList, settings);
printSubtrackTableBody(parentTdb, subtrackRefList, settings, cart);
printSubtrackTableFooter(subCount, settings);
puts("
");
}
boolean compositeHideEmptySubtracksSetting(struct trackDb *tdb, boolean *retDefault,
char **retMultiBedFile, char **retSubtrackIdFile)
/* Parse hideEmptySubtracks settings
* Format: hideEmptySubtracks on|off
* Optional index files for performance:
* hideEmptySubtracksMultiBedUrl multiBed.bigBed
* hideEmptySubtracksSourceUrl subtrackIds.tab
* MultiBed.bed is a bed3Sources bigBed, generated with UCSC tool trackDbIndexBb
* (for single view subtracks, can use bedtools multiinter
* post-processed by UCSC multiBed.pl tool)
* subtrackIds.tab is a tab-sep file: id subtrackName
*
* Return TRUE if setting is present. retDefault is TRUE if set to 'on', o/w FALSE
*/
{
if (!tdbIsComposite(tdb))
return FALSE;
char *hideEmpties = cloneString(trackDbSetting(tdb, SUBTRACK_HIDE_EMPTY));
if (!hideEmpties)
return FALSE;
boolean deflt = FALSE;
if (sameString(hideEmpties, "on"))
deflt = TRUE;
else if (differentString(hideEmpties, "off"))
{
warn("Track %s %s setting invalid: %s", tdb->track, SUBTRACK_HIDE_EMPTY, hideEmpties);
return FALSE;
}
if (retDefault)
*retDefault = deflt;
if (retMultiBedFile != NULL && retSubtrackIdFile != NULL)
{
char *file = cloneString(trackDbSetting(tdb, SUBTRACK_HIDE_EMPTY_MULTIBED_URL));
if (file != NULL)
{
// multi-bed specified to speed display
*retMultiBedFile = cloneString(hReplaceGbdb(file));
file = cloneString(trackDbSetting(tdb, SUBTRACK_HIDE_EMPTY_SOURCES_URL));
if (file == NULL)
{
warn("Track %s missing setting: %s", tdb->track, SUBTRACK_HIDE_EMPTY_SOURCES_URL);
return FALSE;
}
*retSubtrackIdFile = cloneString(hReplaceGbdb(file));
}
}
return TRUE;
}
boolean compositeHideEmptySubtracks(struct cart *cart, struct trackDb *tdb,
char **retMultiBedFile, char **retSubtrackIdFile)
/* Parse hideEmptySubtracks setting and check cart
* Return TRUE if we should hide empties
*/
{
boolean deflt = FALSE;
if (!compositeHideEmptySubtracksSetting(tdb, &deflt, retMultiBedFile, retSubtrackIdFile))
return FALSE;
char buf[128];
safef(buf, sizeof buf, "%s.%s", tdb->track, SUBTRACK_HIDE_EMPTY);
return cartUsualBoolean(cart, buf, deflt);
}
boolean compositeChildHideEmptySubtracks(struct cart *cart, struct trackDb *childTdb,
char **retMultiBedFile, char **retSubtrackIdFile)
/* Parse hideEmptySubtracks setting and check cart
* Return TRUE if we should hide empties
*/
{
struct trackDb *tdb = tdbGetComposite(childTdb);
if (!tdb)
return FALSE;
return compositeHideEmptySubtracks(cart, tdb, retMultiBedFile, retSubtrackIdFile);
}
static void compositeUiSubtracks(char *db, struct cart *cart, struct trackDb *parentTdb)
// Display list of subtracks and descriptions with checkboxes to control visibility and
// possibly other nice things including links to schema and metadata and a release date.
{
char buffer[SMALLBUF];
struct trackDb *subtrack;
// Get list of leaf subtracks to work with
struct slRef *subtrackRef, *subtrackRefList = trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,NULL);
sortOrder_t* sortOrder = sortOrderGet(cart, parentTdb);
char *displaySubs = NULL;
int subCount = slCount(subtrackRefList);
if (subCount > LARGE_COMPOSITE_CUTOFF && membersForAll->dimensions != NULL)
{
// ignore displaySubtracks setting for large composites with a matrix as
// matrix effectively shows all
safef(buffer, SMALLBUF,"%s.displaySubtracks", parentTdb->track);
displaySubs = cartUsualString(cart, buffer,"some"); // track specific defaults to only selected
}
else
{
displaySubs = cartUsualString(cart, "displaySubtracks", "all"); // browser wide defaults to all
}
boolean displayAll = sameString(displaySubs, "all");
// Table wraps around entire list so that "Top" link can float to the correct place.
cgiDown(0.7);
printf("
");
printf("");
if (sortOrder != NULL)
{
// First table row contains the display "selected/visible" or "all" radio buttons
// NOTE: list subtrack radio buttons are inside tracklist table header if
// there are no sort columns. The reason is to ensure spacing of lines
// column headers when the only column header is "Restricted Until"
printSubtrackListRadioButtons(parentTdb->track, subCount, displayAll);
if (membersHaveMatrix(membersForAll))
makeTopLink(parentTdb);
printf("
");
}
else
{
if (membersHaveMatrix(membersForAll))
makeTopLink(parentTdb);
}
// Get info for subtrack list
struct subtrackConfigSettings *settings = NULL;
AllocVar(settings);
// Determine whether there is a restricted until date column
settings->restrictions = FALSE;
for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
{
subtrack = subtrackRef->val;
(void)metadataForTable(db,subtrack,NULL);
if (NULL != metadataFindValue(subtrack,"dateUnrestricted"))
{
settings->restrictions = TRUE;
break;
}
}
settings->useDragAndDrop = sameOk("subTracks",trackDbSetting(parentTdb, "dragAndDrop"));
settings->sortOrder = sortOrder;
settings->displayAll = displayAll;
settings->colorPatch = (trackDbSetting(parentTdb, SUBTRACK_COLOR_PATCH) != NULL);
printSubtrackTable(parentTdb, subtrackRefList, settings, cart);
if (sortOrder == NULL)
printf("
");
membersForAllSubGroupsFree(parentTdb,&membersForAll);
sortOrderFree(&sortOrder);
}
static void compositeUiSubtracksMatchingPrimary(char *db, struct cart *cart,
struct trackDb *parentTdb,char *primarySubtrack)
// Display list of subtracks associated with a primary subtrack for the hgTables merge function
{
assert(primarySubtrack != NULL);
char *primaryType = getPrimaryType(primarySubtrack, parentTdb);
char htmlIdentifier[SMALLBUF];
// Get list of leaf subtracks to work with and sort them
struct slRef *subtrackRef, *subtrackRefList =
trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
if (NULL != trackDbSetting(parentTdb, "sortOrder")
|| NULL != trackDbSetting(parentTdb, "dragAndDrop"))
tdbRefSortPrioritiesFromCart(cart, &subtrackRefList); // preserves user's prev sort/drags
else
slSort(&subtrackRefList, trackDbRefCmp); // straight from trackDb.ra
// Now we can start in on the table of subtracks
printf("\n
");
if (slCount(subtrackRefList) > 5)
puts(" ");
puts("
");
if (!primarySubtrack)
jsInline("matInitializeMatrix();\n");
}
static void makeAddClearButtonPair(char *idPrefix, char *class,char *separator)
// Print an [Add][Clear] button pair that uses javascript to check subtracks
{
char buf[256];
if (class)
safef(buf, sizeof buf,"matSetMatrixCheckBoxes(true,CSS.escape('%s')); return false;", class);
else
safef(buf, sizeof buf,"matSetMatrixCheckBoxes(true); return false;");
char id[256];
safef(id, sizeof id, "%s_add", idPrefix);
cgiMakeOnClickButton(id, buf, ADD_BUTTON_LABEL);
if (separator)
printf("%s",separator);
if (class)
safef(buf, sizeof buf,"matSetMatrixCheckBoxes(false,CSS.escape('%s')); return false;", class);
else
safef(buf, sizeof buf,"matSetMatrixCheckBoxes(false); return false;");
safef(id, sizeof id, "%s_clr", idPrefix);
cgiMakeOnClickButton(id, buf, CLEAR_BUTTON_LABEL);
}
#define MANY_SUBTRACKS 8
#define WIGGLE_HELP_PAGE "../goldenPath/help/hgWiggleTrackHelp.html"
boolean cfgBeginBoxAndTitle(struct trackDb *tdb, boolean boxed, char *title)
// Handle start of box and title for individual track type settings
{
if (!boxed)
{
boxed = trackDbSettingOn(tdb,"boxedCfg");
if (boxed)
printf(" ");
}
if (boxed)
{
printf("
", COLOR_BG_ALTDEFAULT);
if (title)
printf("
%s Configuration
\n", title);
}
else if (title)
printf("
%s ", title );
-else
- printf("
");
+// When !boxed and title==NULL, emit nothing: the caller (e.g. supertrack
+// filter UI) already renders its own heading and doesn't want a stray
+// empty paragraph.
return boxed;
}
void cfgEndBox(boolean boxed)
// Handle end of box and title for individual track type settings
{
if (boxed)
puts("
");
}
void snakeOption(struct cart *cart, char *name, char *title, struct trackDb *tdb)
/* let the user choose to see the track in snake mode */
{
if (!cfgOptionBooleanDefault("canSnake", TRUE))
return;
printf(" Display data as a rearrangement graph: ");
boolean option = cartOrTdbBoolean(cart, tdb, "doSnake", FALSE);
char varName[1024];
safef(varName, sizeof(varName), "%s.doSnake", name);
cgiMakeCheckBox(varName, option);
printf(" \n");
//char *style = option ? "display:block" : "display:none";
//printf("
\n", style);
//printf("
\n\n");
jsInlineF("$(\"input[name='%s']\").click( function() { $('#snakeGraphOptions').toggle();} );\n"
, varName); // XSS FILTER?
}
void squishyPackOption(struct cart *cart, char *name, char *title, struct trackDb *tdb)
/* let the user choose to see the track in wiggle mode */
{
char option[256];
char buffer[4096];
char *field = trackDbSetting(tdb, "squishyPackField");
if (field == NULL)
return;
char *fieldLabel = trackDbSetting(tdb, "squishyPackLabel");
if (fieldLabel == NULL)
{
fieldLabel = buffer;
safef(buffer, sizeof buffer, "Reduce (squish) the height of items that have a %s value greater than", field);
}
double squishyPackPoint = cartOrTdbDouble(cart, tdb, "squishyPackPoint", 999);
printf(" %s ", fieldLabel);
safef(option, sizeof(option), "%s.%s", name, "squishyPackPoint" );
cgiMakeDoubleVarWithLimits(option, squishyPackPoint, "Range min", 0, NO_VALUE, NO_VALUE);
}
void wigOption(struct cart *cart, char *name, char *title, struct trackDb *tdb)
/* let the user choose to see the track in wiggle mode */
{
printf("
Display data as a density graph: ");
boolean option = cartOrTdbBoolean(cart, tdb, "doWiggle", FALSE);
char varName[1024];
safef(varName, sizeof(varName), "%s.doWiggle", name);
cgiMakeCheckBox(varName, option);
printf(" \n");
char *style = option ? "display:block" : "display:none";
printf("
\n", name, style);
// we need to fool the wiggle dialog into defaulting to autoscale and maximum
char *origType = tdb->type;
tdb->type = "bedGraph";
if (hashFindVal(tdb->settingsHash, AUTOSCALE) == NULL)
hashAdd(tdb->settingsHash, AUTOSCALE, "on");
if (hashFindVal(tdb->settingsHash, WINDOWINGFUNCTION) == NULL)
hashAdd(tdb->settingsHash, WINDOWINGFUNCTION, wiggleWindowingEnumToString( wiggleWindowingMean));
wigCfgUi(cart,tdb,name,title,TRUE);
tdb->type = origType;
printf("
Show only transcripts with these accessions: ");
char varName[1024];
safef(varName, sizeof(varName), "%s.nameFilter", name);
char *onlyTransStr = cartUsualString(cart, varName, "");
cgiMakeTextVar(varName, onlyTransStr, 60);
printInfoIcon("Enter the primary accession of the track, so RefSeq IDs for the RefSeq track, Gencode IDs for the Gencode track, etc. Separate multiple accessions with commas.");
puts("
\n\n");
}
boolean tdbSupportsColorOverride(struct trackDb *tdb)
/* Return TRUE if this track type supports the color override feature. */
{
if (!cfgOptionBooleanDefault("showColorPicker", FALSE))
return FALSE;
char *type = tdb->type;
char *track = tdb->track;
// Blacklist tracks that use custom rendering incompatible with color override
if (startsWith("gtexGene", track) || startsWith("gtexEqtlCluster", track)
|| startsWith("gtexEqtlTissue", track))
return FALSE;
return !tdbIsComposite(tdb)
&& (startsWithWord("bed", type) || startsWithWord("bigBed", type)
|| startsWithWord("genePred", type) || startsWithWord("bigGenePred", type)
|| startsWithWord("wig", type) || startsWithWord("bigWig", type)
|| startsWithWord("rmsk", type) || startsWithWord("interact", type)
|| startsWithWord("bigInteract", type) || startsWithWord("bigLolly", type)
|| startsWithWord("vcfTabix", type) || startsWithWord("vcf", type)
|| startsWithWord("bigDbSnp", type));
}
void colorTrackOption(struct cart *cart, char *name, struct trackDb *tdb)
/* color picker for overriding track color */
{
char varName[1024];
safef(varName, sizeof(varName), "%s.colorOverride", name);
char defaultColor[16];
safef(defaultColor, sizeof(defaultColor), "#%02x%02x%02x", tdb->colorR, tdb->colorG, tdb->colorB);
char *rawCartValue = cartOptionalString(cart, varName);
boolean hasOverride = (rawCartValue != NULL && rawCartValue[0] != '\0');
char *colorValue = hasOverride ? rawCartValue : defaultColor;
char checkVar[1024];
safef(checkVar, sizeof(checkVar), "%s.colorOverrideOn", name);
boolean isOn = cartUsualBoolean(cart, checkVar, hasOverride);
printf(" Override track color: ");
cgiMakeCheckBox(checkVar, isOn);
printf(" ",
varName, varName, colorValue);
printf(" \n", varName);
jsInlineF(
"(function() {\n"
" var textEl = document.getElementById('%s_text');\n"
" var pickerEl = document.getElementById('%s_picker');\n"
" var checkEl = document.querySelector('input[type=checkbox][name=\"%s\"]');\n"
" $(pickerEl).spectrum({\n"
" color: textEl.value,\n"
" showPalette: true,\n"
" showSelectionPalette: true,\n"
" showInitial:true,\n"
" showInput: true,\n"
" preferredFormat: 'hex',\n"
" hideAfterPaletteSelect: true,\n"
" change: function(color) { textEl.value = color.toHexString(); checkEl.checked = true; }\n"
" });\n"
" textEl.addEventListener('change', function() {\n"
" $(pickerEl).spectrum('set', textEl.value);\n"
" checkEl.checked = true;\n"
" });\n"
"})();\n",
varName, varName, checkVar);
puts("\n");
}
void wiggleScaleDropDownJavascript(char *name)
/* print some js that deactivates the min/max range if autoscaling is activated */
{
struct dyString *dy = dyStringNew(1024);
dyStringPrintf(dy, " $(\"[name='%s.autoScale']\").change(function()\n", name);
dyStringPrintf(dy, " {\n");
dyStringPrintf(dy, " val= $(this).find(':selected').val(); \n");
dyStringPrintf(dy, " if (val!=\"use vertical viewing range setting\")\n");
dyStringPrintf(dy, " {\n");
dyStringPrintf(dy, " $(\"[name='%s.minY']\")[0].disabled=true;\n", name);
dyStringPrintf(dy, " $(\"[name='%s.maxY']\")[0].disabled=true;\n", name);
dyStringPrintf(dy, " $(\".%sAutoScaleDesc\").attr('style', 'color:grey;');\n", name);
dyStringPrintf(dy, " }\n");
dyStringPrintf(dy, " else\n");
dyStringPrintf(dy, " {\n");
dyStringPrintf(dy, " $(\"[name='%s.minY']\")[0].disabled=false;\n", name);
dyStringPrintf(dy, " $(\"[name='%s.maxY']\")[0].disabled=false;\n", name);
dyStringPrintf(dy, " $(\".%sAutoScaleDesc\").attr('style', 'color:black;');\n", name);
dyStringPrintf(dy, " }\n");
dyStringPrintf(dy, " });\n");
dyStringPrintf(dy, "\n");
dyStringPrintf(dy, " $( document ).ready(function()\n");
dyStringPrintf(dy, " {\n");
dyStringPrintf(dy, " val= $(\"[name='%s.autoScale']\").find(':selected').val(); \n", name);
dyStringPrintf(dy, " if (val==\"auto-scale to data view\")\n");
dyStringPrintf(dy, " {\n");
dyStringPrintf(dy, " $(\"[name='%s.minY']\")[0].disabled=true;\n", name);
dyStringPrintf(dy, " $(\"[name='%s.maxY']\")[0].disabled=true;\n", name);
dyStringPrintf(dy, " $(\".%sAutoScaleDesc\").attr('style', 'color:grey;');\n", name);
dyStringPrintf(dy, " }\n");
dyStringPrintf(dy, " });\n");
jsInline(dy->string);
dyStringFree(&dy);
}
void wigCfgUi(struct cart *cart, struct trackDb *tdb, char *name, char *title, boolean boxed)
/* UI for the wiggle track */
{
char *typeLine = NULL; /* to parse the trackDb type line */
char *words[8]; /* to parse the trackDb type line */
int wordCount = 0; /* to parse the trackDb type line */
char option[256];
double minY; /* from trackDb or cart */
double maxY; /* from trackDb or cart */
double tDbMinY; /* data range limits from trackDb type line */
double tDbMaxY; /* data range limits from trackDb type line */
char *horizontalGrid = NULL; /* Grid lines, off by default */
char *transformFunc = NULL; /* function to transform data points */
char *alwaysZero = NULL; /* Always include 0 in range */
char *lineBar; /* Line or Bar graph */
char *autoScale; /* Auto scaling on or off */
char *windowingFunction; /* Maximum, Mean, or Minimum */
char *smoothingWindow; /* OFF or [2:16] */
char *yLineMarkOnOff; /* user defined Y marker line to draw */
double yLineMark; /* from trackDb or cart */
int maxHeightPixels = atoi(DEFAULT_HEIGHT_PER);
int minHeightPixels = MIN_HEIGHT_PER;
int defaultHeight = maxHeightPixels; /* pixels per item */
boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
wigFetchMinMaxPixelsWithCart(cart,tdb,name,&minHeightPixels, &maxHeightPixels, &defaultHeight);
typeLine = cloneString(tdb->type);
wordCount = chopLine(typeLine,words);
wigFetchMinMaxYWithCart(cart, tdb, name, &minY, &maxY, &tDbMinY, &tDbMaxY, wordCount, words);
freeMem(typeLine);
wigFetchTransformFuncWithCart(cart,tdb,name, &transformFunc);
wigFetchAlwaysZeroWithCart(cart,tdb,name, &alwaysZero);
wigFetchHorizontalGridWithCart(cart,tdb,name, &horizontalGrid);
wigFetchAutoScaleWithCart(cart,tdb,name, &autoScale);
wigFetchGraphTypeWithCart(cart,tdb,name, &lineBar);
wigFetchWindowingFunctionWithCart(cart,tdb,name, &windowingFunction);
wigFetchSmoothingWindowWithCart(cart,tdb,name, &smoothingWindow);
wigFetchYLineMarkWithCart(cart,tdb,name, &yLineMarkOnOff);
wigFetchYLineMarkValueWithCart(cart,tdb,name, &yLineMark);
boolean doNegative = wigFetchDoNegativeWithCart(cart,tdb,tdb->track, (char **) NULL);
boolean doSequenceLogo = wigFetchDoSequenceLogoWithCart(cart,tdb,tdb->track, (char **) NULL);
printf("
");
else
{
puts("");
if (!isLogo)
printf("Graph configuration help",WIGGLE_HELP_PAGE);
}
// add a little javascript call to make sure we don't get whiskers with stacks in multiwigs
if (didAggregate)
jsInlineF("$(function () { multiWigSetupOnChange('%s'); });\n", name);
cfgEndBox(boxed);
}
void filterButtons(char *filterTypeVar, char *filterTypeVal, boolean none)
/* Put up some filter buttons. */
{
printf("Filter: ");
radioButton(filterTypeVar, filterTypeVal, "red");
radioButton(filterTypeVar, filterTypeVal, "green");
radioButton(filterTypeVar, filterTypeVal, "blue");
radioButton(filterTypeVar, filterTypeVal, "exclude");
radioButton(filterTypeVar, filterTypeVal, "include");
if (none)
radioButton(filterTypeVar, filterTypeVal, "none");
}
void radioButton(char *var, char *val, char *ourVal)
/* Print one radio button */
{
cgiMakeRadioButton(var, ourVal, sameString(ourVal, val));
printf("%s ", ourVal);
}
void oneMrnaFilterUi(struct controlGrid *cg, struct trackDb *tdb, char *text, char *var,
char *suffix, struct cart *cart)
/* Print out user interface for one type of mrna filter. */
{
controlGridStartCell(cg);
printf("%s: ", text);
boolean parentLevel = isNameAtParentLevel(tdb,var);
cgiMakeTextVar(var, cartUsualStringClosestToHome(cart, tdb, parentLevel,suffix, ""), 19);
controlGridEndCell(cg);
}
void bedFiltCfgUi(struct cart *cart, struct trackDb *tdb, char *prefix, char *title, boolean boxed)
/* Put up UI for an "bedFilter" tracks. */
{
struct mrnaUiData *mud = newBedUiData(prefix);
struct mrnaFilter *fil;
struct controlGrid *cg = NULL;
boolean parentLevel = isNameAtParentLevel(tdb,prefix);
char *filterTypeVal =
cartUsualStringClosestToHome(cart, tdb, parentLevel, mud->filterTypeSuffix, "red");
boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
/* Define type of filter. */
printf("
\n");
char buffer[256];
safef(buffer, sizeof buffer,"%s.%s",prefix,mud->filterTypeSuffix);
filterButtons(buffer, filterTypeVal, FALSE);
printf("");
/* List various fields you can filter on. */
cg = startControlGrid(4, NULL);
for (fil = mud->filterList; fil != NULL; fil = fil->next)
{
safef(buffer, sizeof buffer,"%s.%s",prefix,fil->suffix);
oneMrnaFilterUi(cg, tdb, fil->label, buffer, fil->suffix, cart);
}
endControlGrid(&cg);
cfgEndBox(boxed);
}
void genbankShowPatentControl(struct cart *cart, struct trackDb *tdb, char *prefix)
/* controls for enabling display of GENBANK RNA patent sequences */
{
char name[256];
safef(name, sizeof(name), "%s.%s", prefix, SHOW_PATENT_SEQUENCES_SUFFIX);
printf("
Show patent sequences:");
cgiMakeCheckBox(name, cartUsualBoolean(cart, name, FALSE));
}
void mrnaCfgUi(struct cart *cart, struct trackDb *tdb, char *prefix, char *title, boolean boxed)
/* Put up UI for an mRNA (or EST) track. */
{
boolean isXeno = (sameString(tdb->track, "xenoMrna") || sameString(tdb->track, "xenoEst"));
struct mrnaUiData *mud = newMrnaUiData(prefix, isXeno);
struct mrnaFilter *fil;
struct controlGrid *cg = NULL;
boolean parentLevel = isNameAtParentLevel(tdb,prefix);
char *filterTypeVal =
cartUsualStringClosestToHome(cart, tdb, parentLevel, mud->filterTypeSuffix,"red");
char *logicTypeVal =
cartUsualStringClosestToHome(cart, tdb, parentLevel, mud->logicTypeSuffix, "and");
boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
/* Define type of filter. */
char buffer[256];
safef(buffer,sizeof buffer,"%s.%s",prefix,mud->filterTypeSuffix);
filterButtons(buffer, filterTypeVal, FALSE);
printf(" Combination Logic: ");
safef(buffer,sizeof buffer,"%s.%s",prefix,mud->logicTypeSuffix);
radioButton(buffer, logicTypeVal, "and");
radioButton(buffer, logicTypeVal, "or");
printf(" \n");
/* List various fields you can filter on. */
printf("
\n");
cg = startControlGrid(4, NULL);
for (fil = mud->filterList; fil != NULL; fil = fil->next)
{
safef(buffer,sizeof buffer,"%s.%s",prefix,fil->suffix);
oneMrnaFilterUi(cg, tdb, fil->label, buffer, fil->suffix, cart);
}
endControlGrid(&cg);
baseColorDrawOptDropDown(cart, tdb);
indelShowOptions(cart, tdb);
if (sameString(tdb->track, "mrna") || sameString(tdb->track, "xenoMrna"))
genbankShowPatentControl(cart, tdb, prefix);
wigOption(cart, prefix, title, tdb);
cfgEndBox(boxed);
}
void scoreGrayLevelCfgUi(struct cart *cart, struct trackDb *tdb, char *prefix, int scoreMax)
// If scoreMin has been set, let user select the shade of gray for that score, in case
// the default is too light to see or darker than necessary.
{
boolean parentLevel = isNameAtParentLevel(tdb,prefix);
char *scoreMinStr = trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN);
if (scoreMinStr != NULL)
{
int scoreMin = atoi(scoreMinStr);
// maxShade=9 taken from hgTracks/simpleTracks.c. Ignore the 10 in shadesOfGray[10+1] --
// maxShade is used to access the array.
int maxShade = 9;
int scoreMinGrayLevel = scoreMin * maxShade/scoreMax;
if (scoreMinGrayLevel <= 0) scoreMinGrayLevel = 1;
char *setting = trackDbSettingClosestToHome(tdb, MIN_GRAY_LEVEL);
int minGrayLevel = cartUsualIntClosestToHome(cart, tdb, parentLevel, MIN_GRAY_LEVEL,
setting ? atoi(setting) : scoreMinGrayLevel);
if (minGrayLevel <= 0) minGrayLevel = 1;
if (minGrayLevel > maxShade) minGrayLevel = maxShade;
puts("\nShade of lowest-scoring items: ");
// Add javascript to select so that its color is consistent with option colors:
int level = 255 - (255*minGrayLevel / maxShade);
printf("\n");
// Use class to set color of each option:
for (i = 1; i <= maxShade; i++)
{
level = 255 - (255*i / maxShade);
printf("\n");
else
printf("• gray (%d%%)\n", i * (100/maxShade));
}
printf("\n");
}
}
static boolean getScoreDefaultsFromTdb(struct trackDb *tdb, char *scoreName,char *defaults,
char**min,char**max)
// returns TRUE if defaults exist and sets the string pointer (because they may be float or int)
// if min or max are set, then they should be freed
{
if (min)
*min = NULL; // default these outs!
if (max)
*max = NULL;
char *setting = trackDbSettingClosestToHome(tdb, scoreName);
if (setting)
{
if (strchr(setting,':') != NULL)
return colonPairToStrings(setting,min,max);
else if (min)
*min = cloneString(setting);
return TRUE;
}
return FALSE;
}
static void setAsNewFilterType(struct trackDb *tdb, char *name, char *field)
/* put the full name of the trackDb variable in a hash of field names if it's specified in the "new" way */
{
struct hash *hash = tdb->isNewFilterHash;
if (hash == NULL)
hash = tdb->isNewFilterHash = newHash(5);
hashAdd(hash, field, name);
}
static char *isNewFilterType(struct trackDb *tdb, char *name)
/* check to see if a field name is in the "new" hash. If it is, return the full trackDb variable name */
{
if ((tdb == NULL) || (tdb->isNewFilterHash == NULL))
return NULL;
struct hashEl *hel = hashLookup(tdb->isNewFilterHash, name);
if (hel == NULL)
return NULL;
return hel->val;
}
char *getScoreNameAdd(struct trackDb *tdb, char *scoreName, char *add)
// Add a suffix to a filter for more information
{
char scoreLimitName[1024];
char *name = cloneString(scoreName);
char *dot = strchr(name, '.');
if ((dot != NULL) && (isNewFilterType(tdb, dot+1) != NULL))
{
*dot++ = 0;
safef(scoreLimitName, sizeof(scoreLimitName), "%s%s.%s", name, add, dot);
}
else
safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, add);
return cloneString(scoreLimitName);
}
static boolean getScoreLimitsFromTdb(struct trackDb *tdb, char *scoreName,char *defaults,
char**min,char**max)
// returns TRUE if limits exist and sets the string pointer (because they may be float or int)
// if min or max are set, then they should be freed
{
if (min)
*min = NULL; // default these outs!
if (max)
*max = NULL;
char *scoreLimitName = getScoreNameAdd(tdb, scoreName, _LIMITS);
char *setting = trackDbSettingClosestToHome(tdb, scoreLimitName);
if (setting)
{
return colonPairToStrings(setting,min,max);
}
else
{
if (min)
{
scoreLimitName = getScoreNameAdd(tdb, scoreName, _MIN);
setting = trackDbSettingClosestToHome(tdb, scoreLimitName);
if (setting)
*min = cloneString(setting);
}
if (max)
{
scoreLimitName = getScoreNameAdd(tdb, scoreName, _MAX);
setting = trackDbSettingClosestToHome(tdb, scoreLimitName);
if (setting)
*max = cloneString(setting);
}
return TRUE;
}
if (defaults != NULL && ((min && *min == NULL) || (max && *max == NULL)))
{
char *minLoc=NULL;
char *maxLoc=NULL;
if (colonPairToStrings(defaults,&minLoc,&maxLoc))
{
if (min && *min == NULL && minLoc != NULL)
*min=minLoc;
else
freeMem(minLoc);
if (max && *max == NULL && maxLoc != NULL)
*max=maxLoc;
else
freeMem(maxLoc);
return TRUE;
}
}
return FALSE;
}
void getScoreIntRangeFromCart(struct cart *cart, struct trackDb *tdb, boolean parentLevel,
char *scoreName, int *limitMin, int *limitMax,int *min,int *max)
// gets an integer score range from the cart, but the limits from trackDb
// for any of the pointers provided, will return a value found, if found, else it's contents
// are undisturbed (use NO_VALUE to recognize unavaliable values)
{
char scoreLimitName[128];
char *deMin=NULL,*deMax=NULL;
if ((limitMin || limitMax) && getScoreLimitsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
{
if (deMin != NULL && limitMin)
*limitMin = atoi(deMin);
if (deMax != NULL && limitMax)
*limitMax = atoi(deMax);
freeMem(deMin);
freeMem(deMax);
}
if ((min || max) && getScoreDefaultsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
{
if (deMin != NULL && min)
*min = atoi(deMin);
if (deMax != NULL && max)
*max =atoi(deMax);
freeMem(deMin);
freeMem(deMax);
}
if (max)
{
safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, _MAX);
deMax = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreLimitName);
if (deMax != NULL)
*max = atoi(deMax);
}
if (min)
{ // Warning: name changes if max!
safef(scoreLimitName, sizeof(scoreLimitName), "%s%s", scoreName, (max && deMax? _MIN:""));
deMin = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreLimitName);
if (deMin != NULL)
*min = atoi(deMin);
}
// Defaulting min and max within limits. Sorry for the horizontal ifs,
// but stacking the group makes them easier to follow
if (min && limitMin
&& *limitMin != NO_VALUE && (*min == NO_VALUE || *min < *limitMin)) *min = *limitMin;
if (min && limitMax
&& *limitMax != NO_VALUE && *min > *limitMax) *min = *limitMax;
if (max && limitMax
&& *limitMax != NO_VALUE && (*max == NO_VALUE || *max > *limitMax)) *max = *limitMax;
if (max && limitMin
&& *limitMin != NO_VALUE && *max < *limitMin) *max = *limitMin;
}
void getScoreFloatRangeFromCart(struct cart *cart, struct trackDb *tdb, boolean parentLevel,
char *scoreName, double *limitMin,double *limitMax,double*min,double*max)
// gets an double score range from the cart, but the limits from trackDb
// for any of the pointers provided, will return a value found, if found, else it's contents
// are undisturbed (use NO_VALUE to recognize unavaliable values)
{
char *deMin=NULL,*deMax=NULL;
if ((limitMin || limitMax) && getScoreLimitsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
{
if (deMin != NULL && limitMin)
*limitMin = strtod(deMin,NULL);
if (deMax != NULL && limitMax)
*limitMax =strtod(deMax,NULL);
freeMem(deMin);
freeMem(deMax);
}
if ((min || max) && getScoreDefaultsFromTdb(tdb,scoreName,NULL,&deMin,&deMax))
{
if (deMin != NULL && min)
*min = strtod(deMin,NULL);
if (deMax != NULL && max)
*max =strtod(deMax,NULL);
freeMem(deMin);
freeMem(deMax);
}
if (max)
{
char *scoreLimitName = getScoreNameAdd(tdb, scoreName, _MAX);
deMax = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreLimitName);
if (deMax != NULL)
*max = strtod(deMax,NULL);
}
if (min)
{ // name is always {filterName}Min
char *scoreLimitName = getScoreNameAdd(tdb, scoreName, _MIN);
deMin = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreLimitName);
if (deMin == NULL)
deMin = cartOptionalStringClosestToHome(cart, tdb,parentLevel,scoreName);
if (deMin != NULL)
*min = strtod(deMin,NULL);
}
// 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, 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("
");
return TRUE;
}
return FALSE;
}
struct trackDbFilter *tdbGetTrackFilters( struct trackDb *tdb, char * lowWild, char * lowName, char * capWild, char * capName)
// figure out which of the ways to specify trackDb filter variables we're using
// and return the setting
{
struct trackDbFilter *trackDbFilterList = NULL;
struct slName *filterSettings = trackDbSettingsWildMatch(tdb, lowWild);
if (filterSettings)
{
struct trackDbFilter *tdbFilter;
struct slName *filter = NULL;
while ((filter = slPopHead(&filterSettings)) != NULL)
{
AllocVar(tdbFilter);
slAddHead(&trackDbFilterList, tdbFilter);
tdbFilter->name = cloneString(filter->name);
tdbFilter->setting = trackDbSetting(tdb, filter->name);
tdbFilter->fieldName = extractFieldNameNew(filter->name, lowName);
setAsNewFilterType(tdb, tdbFilter->name, tdbFilter->fieldName);
}
}
filterSettings = trackDbSettingsWildMatch(tdb, capWild);
if (filterSettings)
{
struct trackDbFilter *tdbFilter;
struct slName *filter = NULL;
while ((filter = slPopHead(&filterSettings)) != NULL)
{
if (differentString(filter->name,NO_SCORE_FILTER))
{
AllocVar(tdbFilter);
slAddHead(&trackDbFilterList, tdbFilter);
tdbFilter->name = cloneString(filter->name);
tdbFilter->setting = trackDbSetting(tdb, filter->name);
tdbFilter->fieldName = extractFieldNameOld(filter->name, capName);
char *name;
if ((name = isNewFilterType(tdb, tdbFilter->fieldName) ) != NULL)
errAbort("error specifying a field's filters in both old (%s) and new format (%s).", tdbFilter->name, name);
}
}
}
return trackDbFilterList;
}
struct trackDbFilter *tdbGetTrackNumFilters( struct trackDb *tdb)
// get the number filters out of trackDb
{
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);
}
static boolean didHighlightSelector = FALSE;
void printHighlightColorPicker(struct cart *cart, struct trackDb *tdb)
{
if (didHighlightSelector)
return;
jsIncludeFile("ajax.js", NULL);
jsIncludeFile("hui.js", NULL);
puts("
");
char *text = "Note that multiple highlight selections use the same color, and are applied successively. So any item that meets at least one criteria will be highlighted.";
printInfoIcon(text);
puts("
");
puts("");
jsInlineF("var cartHighlightColor = \"%s\"\n;", prevHighlightColor(cart, tdb));
jsInlineF("makeHighlightPicker(\"%s.highlightColor\", document.getElementById(\"hgTrackUiColorPicker\"), \"%s\");\n", tdb->track, 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 isHighlight)
// Shows all *Filter style filters. Note that these are in random order and have no graceful title
{
int count = 0;
struct trackDbFilter *trackDbFilters = NULL;
if (isHighlight)
trackDbFilters = tdbGetTrackNumHighlights(tdb);
else
trackDbFilters = tdbGetTrackNumFilters(tdb);
if (trackDbFilters)
{
+ // The is a separator under the track's "Configuration" block title.
+ // Callers that don't emit a title (e.g. the supertrack filter UI that
+ // owns its own heading) pass title==NULL and don't want the extra break.
+ if (title != NULL)
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);
if (as != NULL)
{
struct asColumn *asCol = asColumnFind(as, field);
if (asCol != NULL)
{ // Found label so replace field; strip "|..." suffix used for detail page
field = asCol->comment;
char *pipe = strchr(field, '|');
if (pipe != NULL)
field = cloneStringZ(field, pipe - field);
}
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
{
if (isHighlight)
safef(labelBuf, sizeof(labelBuf),"%s%s", filterByRange ? "": "Highlight items with Minimum ", field );
else
safef(labelBuf, sizeof(labelBuf),"%s%s", filterByRange ? "": "Minimum ", field);
}
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))
return TRUE;
if (trackDbSettingClosestToHome(tdb, GRAY_LEVEL_SCORE_MIN))
return TRUE;
struct trackDbFilter *filterSettings = tdbGetTrackNumFilters( tdb);
if (filterSettings != NULL)
return TRUE;
filterSettings = tdbGetTrackTextFilters( tdb);
if (filterSettings != NULL)
return TRUE;
filterSettings = tdbGetTrackFilterByFilters( tdb);
if (filterSettings != NULL)
return TRUE;
return FALSE;
}
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 trackDbFilter *filterSettings = tdbGetTrackNumFilters( tdb);
if (filterSettings != NULL)
{
boolean one = FALSE;
struct trackDbFilter *oneFilter = filterSettings;
char *noScoreFilter = trackDbSetting(tdb, NO_SCORE_FILTER);
if (noScoreFilter)
blocked = TRUE;
for (;oneFilter != NULL;oneFilter=oneFilter->next)
{
if (differentString(oneFilter->fieldName,"score")) // scoreFilter is implicit
{ // but could be blocked
one = TRUE;
break;
}
}
if (one)
return TRUE;
}
if (!blocked) // scoreFilter is implicit unless NO_SCORE_FILTER
return TRUE;
return FALSE;
}
char *getFilterType(struct cart *cart, struct trackDb *tdb, char *field, char *def)
// figure out how the trackDb is specifying the FILTER_TYPE variable and return its setting
{
char settingString[4096];
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, boolean isHighlight)
/* Show all the text filters for this track. */
{
int count = 0;
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("
");
cgiMakeCheckBox(option, doScoreCtFilter);
safef(option, sizeof(option), "%s.filterTopScorersCt", name);
scoreFilterCt = cartUsualStringClosestToHome(cart, tdb, parentLevel, "filterTopScorersCt",
words[1]);
puts(" Show only items in top-scoring ");
cgiMakeIntVarWithLimits(option,atoi(scoreFilterCt),"Top-scoring count",0,1,100000);
//* Only check size of table if track does not have subtracks */
if ( !parentLevel && hTableExists(db, tdb->table))
printf(" (range: 1 to 100,000 total items: %d)\n",getTableSize(db, tdb->table));
else
printf(" (range: 1 to 100,000)\n");
}
cfgEndBox(boxed);
}
// Moved from hgTrackUi for consistency
static void filterByChromCfgUi(struct cart *cart, struct trackDb *tdb)
{
char filterVar[256];
char *filterVal = "";
printf("
Filter by chromosome (e.g. chr10): ");
safef(filterVar, sizeof(filterVar), "%s.chromFilter", tdb->track);
(void) cartUsualString(cart, filterVar, filterVal); // ignore returned setting
cgiMakeTextVar(filterVar, cartUsualString(cart, filterVar, ""), 15);
}
// Moved from hgTrackUi for consistency
void crossSpeciesCfgUi(struct cart *cart, struct trackDb *tdb)
// Put up UI for selecting rainbow chromosome color or intensity score.
{
char colorVar[256];
char *colorSetting;
// initial value of chromosome coloring option is "on", unless
// overridden by the colorChromDefault setting in the track
char *colorDefault = trackDbSettingOrDefault(tdb, "colorChromDefault", "on");
printf("
Color track based on chromosome: ");
safef(colorVar, sizeof(colorVar), "%s.color", tdb->track);
colorSetting = cartUsualString(cart, colorVar, colorDefault);
cgiMakeRadioButton(colorVar, "on", sameString(colorSetting, "on"));
printf(" on ");
cgiMakeRadioButton(colorVar, "off", sameString(colorSetting, "off"));
printf(" off ");
printf("
");
filterByChromCfgUi(cart,tdb);
}
struct slPair *buildFieldList(struct trackDb *tdb, char *trackDbVar, struct asObject *as)
/* Build up a hash of a list of fields in an AS file. */
{
char *fields = trackDbSettingClosestToHome(tdb, trackDbVar);
if (fields == NULL)
return NULL;
if (sameString(fields, "none"))
return slPairNew("none", NULL);
struct slPair *list = NULL;
struct slName *thisField, *fieldList = slNameListFromComma(fields);
for(thisField = fieldList; thisField; thisField = thisField->next)
{
char *trimLabel = trimSpaces(thisField->name);
unsigned colNum = asColumnFindIx(as->columnList, trimLabel);
if (colNum == -1)
errAbort("cannot find field named '%s' in AS file '%s'",
trimLabel, as->name);
slAddHead(&list, slPairNew(trimLabel, NULL + colNum));
}
slReverse(&list);
return list;
}
void labelCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *prefix)
/* If there is a labelFields for a bigBed, this routine is called to put up the label options. */
{
// composites can't label because they don't have an autoSql
if (tdbIsComposite(tdb))
return;
if (trackDbSettingClosestToHomeOn(tdb, "linkIdInName"))
return;
struct asObject *as = asForDb(tdb, db);
if (as == NULL)
return;
struct slPair *labelList = buildFieldList(tdb, "labelFields", as);
struct slPair *defaultLabelList = buildFieldList(tdb, "defaultLabelFields", as);
char varName[1024];
if ((labelList == NULL) || sameString(labelList->name, "none"))
return;
struct slPair *thisLabel = labelList;
if (thisLabel->next == NULL) // If there's only one option we either show the label or not.
{
printf("Show Label: ");
safef(varName, sizeof(varName), "%s.label.%s", prefix, thisLabel->name);
boolean option = cartUsualBoolean(cart, varName, TRUE);
cgiMakeCheckBox(varName, option);
}
else
{
printf("Label: ");
for(; thisLabel; thisLabel = thisLabel->next)
{
safef(varName, sizeof(varName), "%s.label.%s", prefix, thisLabel->name);
boolean isDefault = FALSE;
if (defaultLabelList == NULL)
isDefault = (thisLabel == labelList);
else if (sameString(defaultLabelList->name, "none"))
isDefault = FALSE;
else
isDefault = (slPairFind(defaultLabelList, thisLabel->name) != NULL);
boolean option = cartUsualBoolean(cart, varName, isDefault);
cgiMakeCheckBox(varName, option);
// find comment for the column listed
struct asColumn *col = as->columnList;
unsigned num = ptToInt(thisLabel->val);
for(; col && num--; col = col->next)
;
assert(col);
printf(" %s ", col->comment);
}
}
}
void mergeSpanCfgUi(struct cart *cart, struct trackDb *tdb, char *prefix)
/* If this track offers a merge spanned items option, put up the cfg for it, which
* is just a checkbox with a small explanation. Comparing tdb->track to prefix
* ensures we don't offer this control at the composite level, as this is a
* subtrack only config */
{
if (trackDbSettingOn(tdb, MERGESPAN_TDB_SETTING) && sameString(tdb->track, prefix))
{
boolean curOpt = trackDbSettingOn(tdb, "mergeSpannedItems");
char mergeSetting[256];
safef(mergeSetting, sizeof(mergeSetting), "%s.%s", tdb->track, MERGESPAN_CART_SETTING);
if (cartVarExists(cart, mergeSetting))
curOpt = cartBoolean(cart, mergeSetting);
printf("Merge items that span the current region:");
cgiMakeCheckBox(mergeSetting, curOpt);
}
}
void pslCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title,
boolean boxed)
/* Put up UI for psl tracks */
{
boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
char *typeLine = cloneString(tdb->type);
char *words[8];
int wordCount = wordCount = chopLine(typeLine, words);
if (sameString(tdb->type, "bigPsl"))
labelCfgUi(db, cart, tdb, name);
if (wordCount == 3 && sameWord(words[1], "xeno"))
crossSpeciesCfgUi(cart,tdb);
baseColorDropLists(cart, tdb, name);
indelShowOptionsWithName(cart, tdb, name);
wigOption(cart, name, title, tdb);
snakeOption(cart, name, title, tdb);
cfgEndBox(boxed);
}
void netAlignCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *prefix, char *title,
boolean boxed)
/* Put up UI for net tracks */
{
boxed = cfgBeginBoxAndTitle(tdb, boxed, title);
boolean parentLevel = isNameAtParentLevel(tdb,prefix);
enum netColorEnum netColor = netFetchColorOption(cart, tdb, parentLevel);
char optString[256]; /* our option strings here */
safef(optString, ArraySize(optString), "%s.%s", prefix, NET_COLOR );
printf("
");
}
numberPerRow = 5;
/* new logic to decide if line break should be displayed here */
if ((j != 0 && (j % numberPerRow) == 0) && (lineBreakJustPrinted == FALSE))
{
puts("
",boxed?" width='100%'":"");
baseColorDrawOptDropDown(cart, tdb);
indelShowOptionsWithNameExt(cart, tdb, name, "LRG transcript sequence", FALSE, FALSE);
cfgEndBox(boxed);
}
struct trackDb *rFindView(struct trackDb *forest, char *view)
// Return the trackDb on the list that matches the view tag. Prefers ancestors before decendents
{
struct trackDb *tdb;
for (tdb = forest; tdb != NULL; tdb = tdb->next)
{
char *viewSetting = trackDbSetting(tdb, "view");
if (sameOk(viewSetting, view) || sameOk(tagEncode(viewSetting), view))
return tdb;
}
for (tdb = forest; tdb != NULL; tdb = tdb->next)
{
struct trackDb *viewTdb = rFindView(tdb->subtracks, view);
if (viewTdb != NULL)
return viewTdb;
}
return NULL;
}
static boolean compositeViewCfgExpandedByDefault(struct trackDb *parentTdb,char *view,
char **retVisibility)
// returns true if the view cfg is expanded by default. Optionally allocates string of view
// setting (eg 'dense')
{
boolean expanded = FALSE;
if ( retVisibility != NULL )
*retVisibility = cloneString(hStringFromTv(parentTdb->visibility));
struct trackDb *viewTdb = rFindView(parentTdb->subtracks, view);
if (viewTdb == NULL)
return FALSE;
if (retVisibility != NULL)
*retVisibility = cloneString(hStringFromTv(viewTdb->visibility));
if (trackDbSetting(viewTdb, "viewUi"))
expanded = TRUE;
return expanded;
}
enum trackVisibility visCompositeViewDefault(struct trackDb *parentTdb,char *view)
// returns the default track visibility of particular view within a composite track
{
char *visibility = NULL;
compositeViewCfgExpandedByDefault(parentTdb,view,&visibility);
enum trackVisibility vis = hTvFromString(visibility);
freeMem(visibility);
return vis;
}
static boolean hCompositeDisplayViewDropDowns(char *db, struct cart *cart, struct trackDb *parentTdb)
// UI for composite view drop down selections.
{
int ix;
char classes[SMALLBUF];
char javascript[JBUFSIZE];
char id[256];
#define CFG_LINK "%s ▾"
#define CFG_LINK_JS "return (showConfigControls('%s') == false);"
#define MAKE_CFG_LINK(name,title,viewTrack,open) \
safef(id, sizeof id, "%s_link", (name)); \
printf(CFG_LINK, (name),id,(title),(title),(viewTrack),((open)?"on":"off")); \
safef(javascript, sizeof javascript, CFG_LINK_JS, (name)); \
jsOnEventById("click", id, javascript);
// membersForAll is generated once per track, then cached
membersForAll_t *membersForAll = membersForAllSubGroupsGet(parentTdb, cart);
members_t *membersOfView = membersForAll->members[dimV];
if (membersOfView == NULL)
return FALSE;
char configurable[membersOfView->count];
memset(configurable,cfgNone,sizeof(configurable));
int firstOpened = -1;
boolean makeCfgRows = FALSE;
struct trackDb **matchedViewTracks = needMem(sizeof(struct trackDb *) * membersOfView->count);
for (ix = 0; ix < membersOfView->count; ix++)
{
if (membersOfView->subtrackList != NULL
&& membersOfView->subtrackList[ix] != NULL)
{
struct trackDb *subtrack = membersOfView->subtrackList[ix]->val;
matchedViewTracks[ix] = subtrack->parent;
configurable[ix] = (char)cfgTypeFromTdb(subtrack, TRUE);
if (configurable[ix] != cfgNone && trackDbSettingBlocksConfiguration(subtrack,FALSE))
configurable[ix] = cfgNone;
if (configurable[ix] != cfgNone)
{
if (firstOpened == -1)
{
if (cartOrTdbBoolean(cart, matchedViewTracks[ix], "showCfg", FALSE))
firstOpened = ix;
}
makeCfgRows = TRUE;
}
}
}
toLowerN(membersOfView->groupTitle, 1);
printf("Select %s (Help):\n", membersOfView->groupTitle);
printf("
\n");
// Make row of vis drop downs
for (ix = 0; ix < membersOfView->count; ix++)
{
char *viewName = membersOfView->tags[ix];
if (matchedViewTracks[ix] != NULL)
{
printf("
",membersOfView->count+1);
if (configurable[ix] != cfgNone)
{ // Hint: subtrack is model but named for view
cfgByCfgType(configurable[ix],db,cart,view->subtracks,view->track,
membersOfView->titles[ix],TRUE);
}
printf("
");
}
}
}
puts("
");
freeMem(matchedViewTracks);
return TRUE;
}
char *compositeLabelWithVocabLink(char *db,struct trackDb *parentTdb, struct trackDb *childTdb,
char *vocabType, char *label)
// If the parentTdb has a controlledVocabulary setting and the vocabType is found,
// then label will be wrapped with the link to display it. Return string is cloned.
{
char *vocab = trackDbSetting(parentTdb, "controlledVocabulary");
// WARNING: this is needed to cache metadata in trackDb object (accessed by metadataFindValue)
(void)metadataForTable(db,childTdb,NULL);
if (vocab == NULL)
return cloneString(label); // No wrapping!
// Currently implemented just for ENCODE style vocab
if (!vocabSettingIsEncode(vocab))
return cloneString(label);
char *words[SMALLBUF];
int count;
if ((count = chopByWhite(cloneString(vocab), words, SMALLBUF)) <= 1)
return cloneString(label);
char *suffix = NULL;
char *rootLabel = labelRoot(label, &suffix);
boolean found = FALSE;
int ix;
for (ix=1;ix"
#define PM_BUTTON_UC_JS "return (matSetMatrixCheckBoxes(%s%sCSS.escape(\"%s\")%s%s%s) == false);"
#define PM_MAKE_BUTTON_UC(s1,s2,s3,s4,s5,s6,name,img) \
safef(id, sizeof id, "btn_%s", (name)); \
printf(PM_BUTTON_UC, htmlEncode(id), (img)); \
safef(javascript, sizeof javascript, PM_BUTTON_UC_JS, (s1),(s2),(s3),(s4),(s5),(s6)); \
jsOnEventById("click", id, javascript);
#define MATRIX_RIGHT_BUTTONS_AFTER 8
#define MATRIX_BOTTOM_BUTTONS_AFTER 20
static void buttonsForAll(boolean left, boolean top)
{
char id[256];
char javascript[1024];
char fullname[256];
safef(fullname, sizeof fullname, "plus_all_%s_%s", left ? "left" : "right", top ? "top" : "bottom");
PM_MAKE_BUTTON_UC("true", ",", "", "", "", "", fullname, "add_sm.gif")
safef(fullname, sizeof fullname, "minus_all_%s_%s", left ? "left" : "right", top ? "top" : "bottom");
PM_MAKE_BUTTON_UC("false",",", "", "", "", "", fullname, "remove_sm.gif")
}
static void buttonsForOne(char *class, boolean vertical, boolean left, boolean top)
{
char id[256];
char javascript[1024];
char fullname[256];
safef(fullname, sizeof fullname, "plus_%s_all_%s_%s" , class, left ? "left" : "right",
top ? "top" : "bottom");
PM_MAKE_BUTTON_UC("true", ",", class, "", "", "", fullname, "add_sm.gif")
if (vertical)
puts(" ");
safef(fullname, sizeof fullname, "minus_%s_all_%s_%s", class, left ? "left" : "right",
top ? "top" : "bottom");
PM_MAKE_BUTTON_UC("false", ",", class, "", "", "", fullname, "remove_sm.gif")
}
#define MATRIX_SQUEEZE 10
static boolean matrixSqueeze(membersForAll_t* membersForAll)
// Returns non-zero if the matrix will be squeezed. Non-zero is actually squeezedLabelHeight
{
char *browserVersion;
if (btIE == cgiClientBrowser(&browserVersion, NULL, NULL) && *browserVersion < '9')
return 0;
members_t *dimensionX = membersForAll->members[dimX];
members_t *dimensionY = membersForAll->members[dimY];
if (dimensionX && dimensionY)
{
if (dimensionX->count>MATRIX_SQUEEZE)
{
int ixX,cntX=0;
for (ixX = 0; ixX < dimensionX->count; ixX++)
{
if (dimensionX->subtrackList
&& dimensionX->subtrackList[ixX]
&& dimensionX->subtrackList[ixX]->val)
cntX++;
}
if (cntX>MATRIX_SQUEEZE)
return TRUE;
}
}
return FALSE;
}
static void matrixXheadingsRow1(char *db, struct trackDb *parentTdb, boolean squeeze,
membersForAll_t* membersForAll, boolean top)
// prints the top row of a matrix: 'All' buttons; X titles; buttons 'All'
{
members_t *dimensionX = membersForAll->members[dimX];
members_t *dimensionY = membersForAll->members[dimY];
printf("
\n",top?"BOTTOM":"TOP");
if (dimensionX && dimensionY)
{
printf("
",top?"TOP":"BOTTOM");
//printf("
",(top == squeeze)?"BOTTOM":"TOP");//"TOP":"BOTTOM");
buttonsForAll(TRUE, top);
puts(" All
");
}
// If there is an X dimension, then titles go across the top
if (dimensionX)
{
int ixX,cntX=0;
if (dimensionY)
{
if (squeeze)
printf("
",
dimensionX->tags[ixX],
compositeLabelWithVocabLink(db,parentTdb,dimensionX->subtrackList[ixX]->val,
dimensionX->groupTag,label));
freeMem(label);
}
cntX++;
}
}
// If dimension is big enough, then add Y buttons to right as well
if (cntX>MATRIX_RIGHT_BUTTONS_AFTER)
{
if (dimensionY)
{
if (squeeze)
printf("
\n");
}
static void matrixXheadingsRow2(struct trackDb *parentTdb, boolean squeeze,
membersForAll_t* membersForAll, boolean top)
// prints the 2nd row of a matrix: Y title; X buttons; title Y
{
members_t *dimensionX = membersForAll->members[dimX];
members_t *dimensionY = membersForAll->members[dimY];
// If there are both X and Y dimensions, then there is a row of buttons in X
if (dimensionX && dimensionY)
{
int ixX,cntX=0;
printf("
%s
",
dimensionY->groupTitle);
for (ixX = 0; ixX < dimensionX->count; ixX++) // Special row of +- +- +-
{
if (dimensionX->subtrackList
&& dimensionX->subtrackList[ixX]
&& dimensionX->subtrackList[ixX]->val)
{
printf("
\n",htmlEncode(dimensionY->tags[ixY]),
compositeLabelWithVocabLink(db,parentTdb,childTdb,dimensionY->groupTag,
dimensionY->titles[ixY]));
}
static int displayABCdimensions(char *db,struct cart *cart, struct trackDb *parentTdb,
struct slRef *subtrackRefList, membersForAll_t* membersForAll)
// This will walk through all declared nonX&Y dimensions (X and Y is the 2D matrix of CBs.
// NOTE: ABC dims are only supported if there are X & Y both.
// Also expected number should be passed in
{
int count=0,ix;
for (ix=dimA;ixdimMax;ix++)
{
if (membersForAll->members[ix]==NULL)
continue;
if (membersForAll->members[ix]->count<1)
continue;
count++;
if (count==1) // First time set up a table
puts("
");
printf("
%s:
",
membersForAll->members[ix]->groupTitle);
int aIx;
for (aIx=0;aIxmembers[ix]->count;aIx++)
{
if (membersForAll->members[ix]->tags[aIx] != NULL)
{
assert(membersForAll->members[ix]->subtrackList[aIx]->val != NULL);
printf("
");
return count;
}
#ifdef DEBUG
static void dumpDimension(members_t *dimension, char *name, FILE *f)
/* Dump out information on dimension. */
{
int count = dimension->count;
fprintf(f, "%s: count=%d tag=%s title=%s setting=%s \n", name, count, dimension->tag, dimension->title, dimension->setting);
int i;
for (i=0; inames[i], dimension->values[i]);
fprintf(f, " \n");
}
#endif /* DEBUG */
static char *labelWithVocabLinkForMultiples(char *db,struct trackDb *parentTdb, members_t* members)
// If the parentTdb has a controlledVocabulary setting and the vocabType is found,
// then label will be wrapped with the link to all relevent terms. Return string is cloned.
{
assert(members->subtrackList != NULL);
char *vocab = cloneString(trackDbSetting(parentTdb, "controlledVocabulary"));
if (vocab == NULL)
return cloneString(members->groupTitle); // No link wrapping!
char *words[15];
int count,ix;
boolean found=FALSE;
if ((count = chopByWhite(vocab, words,15)) <= 1) // vocab now contains just the file name
return cloneString(members->groupTitle);
char *mdbVar = NULL;
// Find mdb var to look up based upon the groupTag and cv setting
for (ix=1;ixgroupTag,words[ix])) // controlledVocabulary setting matches tag
{ // so all labels are linked
mdbVar = members->groupTag;
break;
}
else if (startsWithWordByDelimiter(members->groupTag,'=',words[ix]))
{
mdbVar = words[ix] + strlen(members->groupTag) + 1;
break;
}
}
if (mdbVar == NULL)
{
freeMem(vocab);
return cloneString(members->groupTitle);
}
#define VOCAB_MULTILINK_BEG "%s"
struct dyString *dyLink = dyStringCreate(VOCAB_MULTILINK_BEG,
(sameWord(mdbVar,"antibody")?"target":"term"));
// Now build the comma delimited string of mdb vals (all have same mdb var)
boolean first = TRUE;
for (ix=0;ixcount;ix++)
{
if (members->subtrackList[ix] != NULL && members->subtrackList[ix]->val != NULL)
{
struct trackDb *childTdb = members->subtrackList[ix]->val;
(void)metadataForTable(db,childTdb,NULL); // Makes sure this has been populated
const char * mdbVal = metadataFindValue(childTdb,mdbVar); // one for each is enough
if (mdbVal != NULL)
{
if (!first)
dyStringAppendC(dyLink,',');
dyStringAppend(dyLink,(char *)mdbVal);
first = FALSE;
}
}
}
dyStringPrintf(dyLink,VOCAB_MULTILINK_END,members->groupTitle,members->groupTitle);
freeMem(vocab);
return dyStringCannibalize(&dyLink);
}
static boolean compositeUiByFilter(char *db, struct cart *cart, struct trackDb *parentTdb,
char *formName)
// UI for composite tracks: filter subgroups by multiselects to select subtracks.
{
membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,cart);
if (membersForAll == NULL || membersForAll->filters == FALSE) // Not Matrix or filters
return FALSE;
if (cartOptionalString(cart, "ajax") == NULL)
{
webIncludeResourceFile("ui.dropdownchecklist.css");
jsIncludeFile("ui.dropdownchecklist.js",NULL);
jsIncludeFile("ddcl.js",NULL);
}
cgiDown(0.7);
printf("Select subtracks %sby: (select multiple %sitems - %s) \n",
(membersForAll->members[dimX] != NULL || membersForAll->members[dimY] != NULL ? "further ":""),
(membersForAll->dimMax == dimA?"":"categories and "),FILTERBY_HELP_LINK);
printf("
\n");
char id[256];
char javascript[1024];
// Do All [+][-] buttons
if (membersForAll->members[dimX] == NULL && membersForAll->members[dimY] == NULL) // No matrix
{
printf("
All: ");
// TODO: Test when a real world case actually calls this. Currently no trackDb.ra cases exist
#define PM_BUTTON_FILTER_COMP ""
#define PM_BUTTON_FILTER_COMP_JS "waitOnFunction(filterCompositeSet,this,%s);return false;"
#define MAKE_PM_BUTTON_FILTER_COMP(tf,fc,plmi) \
safef(id, sizeof id, "btn_%s", (fc)); \
printf(PM_BUTTON_FILTER_COMP, id, (plmi)); \
safef(javascript, sizeof javascript, PM_BUTTON_FILTER_COMP_JS, (tf)); \
jsOnEventById("click", id, javascript);
MAKE_PM_BUTTON_FILTER_COMP("true", "plus_fc",'+')
MAKE_PM_BUTTON_FILTER_COMP("false","minus_fc",'-')
printf("
\n");
}
// Now make a filterComp box for each ABC dimension
int dimIx=dimA;
for (dimIx=dimA;dimIxdimMax;dimIx++)
{
printf("
%s: \n",
labelWithVocabLinkForMultiples(db,parentTdb,membersForAll->members[dimIx]));
safef(id, sizeof id, "fc%d",dimIx);
printf(
"\n"
,id,parentTdb->track,membersForAll->members[dimIx]->groupTag,
"multiple");
jsOnEventById("change", id, "filterCompositeSelectionChanged(this);");
// DO we support anything besides multi?
// (membersForAll->members[dimIx]->fcType == fctMulti?"multiple ":""));
if (membersForAll->members[dimIx]->fcType != fctOneOnly)
printf("\n",
(sameWord("All",membersForAll->checkedTags[dimIx])?" SELECTED":"") );
int ix=0;
for (ix=0;ixmembers[dimIx]->count; ix++)
{
boolean alreadySet = membersForAll->members[dimIx]->selected[ix];
printf("\n",(alreadySet?" SELECTED":""),
membersForAll->members[dimIx]->tags[ix],membersForAll->members[dimIx]->titles[ix]);
}
printf("");
if (membersForAll->members[dimIx]->fcType == fctOneOnly)
printf(" (select only one)");
printf("
\n");
}
printf("
\n");
puts(" \n");
return TRUE;
}
void fastMatixToSubtrackMap()
// prints out the "common" globals json hash
// This hash is the one utils.js and therefore all CGIs know about
{
struct dyString *dy = dyStringNew(1024);
dyStringPrintf(dy,
"var mtxSubMap = {};\n"
"$( document ).ready(function()\n"
"{\n"
"matCB = $('input.matCB:first');\n"
"if (!matCB)\n"
" return;\n"
"var matClassList = $( matCB ).attr('class').split(' ');\n"
"matClassList = aryRemove(matClassList,['matCB','changed','disabled','abc']);\n"
"if (matClassList.length === 0 )\n"
" return;\n"
"subCBs = $('input.subCB');\n"
"$( subCBs ).each( function (i) { \n"
" // class='subCB BS-Seq Mantle_Cell_Lymphoma venous_blood A007MCL CNAG CPG_methylation_cov signal' \n"
" var classList = $( this ).attr('class').split(' ');\n"
" if (matClassList.length === 1) {\n"
" var classes = '.' + classList[1]; // dimX or dimY \n"
" } else {\n"
" var classes = '.' + classList[1] + '.' + classList[2]; // dimX and dimY \n"
" }\n"
" if (mtxSubMap[classes] === undefined) {\n"
" mtxSubMap[classes] = [this];\n"
" } else {\n"
" mtxSubMap[classes].push(this);\n"
" }\n"
"});\n"
"});\n"
);
jsInline(dy->string);
dyStringFree(&dy);
}
static boolean compositeUiByMatrix(char *db, struct cart *cart, struct trackDb *parentTdb,
char *formName)
// UI for composite tracks: matrix of checkboxes.
{
//int ix;
char objName[SMALLBUF];
membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,cart);
if (membersForAll == NULL || membersForAll->dimensions == NULL) // Not Matrix!
return FALSE;
int ixX,ixY;
members_t *dimensionX = membersForAll->members[dimX];
members_t *dimensionY = membersForAll->members[dimY];
// use array of char determine all the cells (in X,Y,Z dimensions) that are actually populated
char *value;
int sizeOfX = dimensionX?dimensionX->count:1;
int sizeOfY = dimensionY?dimensionY->count:1;
int cells[sizeOfX][sizeOfY]; // There needs to be atleast one element in dimension
int chked[sizeOfX][sizeOfY]; // How many subCBs are checked per matCB?
int enabd[sizeOfX][sizeOfY]; // How many subCBs are enabled per matCB?
memset(cells, 0, sizeof(cells));
memset(chked, 0, sizeof(chked));
memset(enabd, 0, sizeof(chked));
struct slRef *subtrackRef, *subtrackRefList =
trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
struct trackDb *subtrack;
if (dimensionX || dimensionY) // Must be an X or Y dimension
{
// Fill the cells based upon subtrack membership
for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
{
subtrack = subtrackRef->val;
ixX = (dimensionX ? -1 : 0 );
ixY = (dimensionY ? -1 : 0 );
if (dimensionX && subgroupFind(subtrack,dimensionX->groupTag,&value))
{
ixX = stringArrayIx(value,dimensionX->tags,dimensionX->count);
subgroupFree(&value);
}
if (dimensionY && subgroupFind(subtrack,dimensionY->groupTag,&value))
{
ixY = stringArrayIx(value,dimensionY->tags,dimensionY->count);
subgroupFree(&value);
}
if (ixX > -1 && ixY > -1)
{
cells[ixX][ixY]++;
int fourState = subtrackFourStateChecked(subtrack,cart);
// hidden views are handled by 4-way CBs: only count enabled
if (fourStateEnabled(fourState))
{
// Only bother if the subtrack is found in all ABC dims checked
if (subtrackInAllCurrentABCs(subtrack,membersForAll))
{
enabd[ixX][ixY]++;
if (fourStateChecked(fourState) == 1)
chked[ixX][ixY]++;
}
}
}
}
}
// If there is no matrix and if there is a filterComposite, then were are done.
if (dimensionX == NULL && dimensionY == NULL)
{
if (compositeUiByFilter(db, cart, parentTdb, formName))
return FALSE;
}
// Tell the user what to do:
char javascript[JBUFSIZE];
//puts("Select subtracks by characterization: ");
printf("Select subtracks by ");
if (dimensionX && !dimensionY)
safef(javascript, sizeof(javascript), "%s:",dimensionX->groupTitle);
else if (!dimensionX && dimensionY)
safef(javascript, sizeof(javascript), "%s:",dimensionY->groupTitle);
else if (dimensionX && dimensionY)
safef(javascript, sizeof(javascript), "%s and %s:",
dimensionX->groupTitle,dimensionY->groupTitle);
else
safef(javascript, sizeof(javascript), "multiple variables:");
puts(strLower(javascript));
if (!subgroupingExists(parentTdb,"view"))
puts("(help)\n");
puts(" \n");
if (membersForAll->abcCount > 0 && membersForAll->filters == FALSE)
{
displayABCdimensions(db,cart,parentTdb,subtrackRefList,membersForAll);
}
// Could have been just filterComposite. Must be an X or Y dimension
if (dimensionX == NULL && dimensionY == NULL)
return FALSE;
// if there is a treeimage, put it beside the matrix in the green box
char *treeImage = trackDbSetting(parentTdb, "treeImage");
if (treeImage != NULL)
{
printf("
");
printf("
\n",
COLOR_BG_ALTDEFAULT);
}
else
printf("
\n",
COLOR_BG_ALTDEFAULT);
(void)matrixXheadings(db,parentTdb,membersForAll,TRUE);
// Now the Y by X matrix
int cntX=0,cntY=0;
for (ixY = 0; ixY < sizeOfY; ixY++)
{
if (dimensionY == NULL || (dimensionY->tags[ixY]))
{
cntY++;
assert(!dimensionY || ixY < dimensionY->count);
printf("
");
matrixYheadings(db,parentTdb, membersForAll,ixY,TRUE);
#define MAT_CB_SETUP ""
#define MAT_CB(name,js) printf(MAT_CB_SETUP,(htmlEncode(name)),(htmlEncode(name)),(js));
for (ixX = 0; ixX < sizeOfX; ixX++)
{
if (dimensionX == NULL || (dimensionX->tags[ixX]))
{
assert(!dimensionX || ixX < dimensionX->count);
if (cntY==1) // Only do this on the first good Y
cntX++;
if (dimensionX && ixX == dimensionX->count)
break;
char *ttlX = NULL;
char *ttlY = NULL;
if (dimensionX)
{
ttlX = cloneString(dimensionX->titles[ixX]);
stripString(ttlX,"");
stripString(ttlX,"");
}
if (dimensionY != NULL)
{
ttlY = cloneString(dimensionY->titles[ixY]);
stripString(ttlY,"");
stripString(ttlY,"");
}
if (cells[ixX][ixY] > 0)
{
boolean halfChecked = ( chked[ixX][ixY] > 0
&& chked[ixX][ixY] < enabd[ixX][ixY]);
struct dyString *dySettings = dyStringNew(256);
if (dimensionX && dimensionY)
{
safef(objName, sizeof(objName), "mat_%s_%s_cb",
dimensionX->tags[ixX],dimensionY->tags[ixY]);
}
else
{
safef(objName, sizeof(objName), "mat_%s_cb",
(dimensionX ? dimensionX->tags[ixX] : dimensionY->tags[ixY]));
}
if (ttlX && ttlY)
printf("
\n",
htmlEncode(dimensionX ? dimensionX->tags[ixX] : dimensionY->tags[ixY]));
dyStringPrintf(dySettings, " class=\"matCB");
if (halfChecked)
dyStringPrintf(dySettings, " disabled"); // appears disabled but still clickable!
if (dimensionX)
dyStringPrintf(dySettings, " %s",dimensionX->tags[ixX]);
if (dimensionY)
dyStringPrintf(dySettings, " %s",dimensionY->tags[ixY]);
dyStringAppendC(dySettings,'"');
if (chked[ixX][ixY] > 0)
dyStringAppend(dySettings," CHECKED");
if (halfChecked)
dyStringAppend(dySettings," title='Not all associated subtracks have been selected'");
MAT_CB(objName,dyStringCannibalize(&dySettings));
jsOnEventById("click", objName, "matCbClick(this);"); // X&Y are set by javascript
puts("
");
// generate set & clear buttons for subgroups
for (i = 0; i < MAX_SUBGROUP; i++)
{
char *subGroup;
safef(setting, sizeof setting, "subGroup%d", i+1);
if (trackDbSetting(parentTdb, setting) == NULL)
break;
wordCnt = chopLine(cloneString(trackDbSetting(parentTdb, setting)), words);
if (wordCnt < 2)
continue;
subGroup = cloneString(words[0]);
if (sameWord(subGroup,"view"))
continue; // Multi-view should have taken care of "view" subgroup already
puts("
");
printf("
%s
", words[1]);
for (j = 2; j < wordCnt; j++)
{
if (!parseAssignment(words[j], &name, &value))
continue;
printf("
");
button = cgiOptionalString(buttonVar);
if (isEmpty(button))
continue;
struct slRef *tdbRefList = trackDbListGetRefsToDescendantLeaves(parentTdb->subtracks);
struct slRef *tdbRef;
for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
{
subtrack = tdbRef->val;
char *p;
int n;
if ((p = trackDbSetting(subtrack, "subGroups")) == NULL)
continue;
n = chopLine(cloneString(p), words);
for (k = 0; k < n; k++)
{
char *subName, *subValue;
if (!parseAssignment(words[k], &subName, &subValue))
continue;
if (sameString(subName, subGroup) && sameString(subValue, name))
{
boolean newVal = FALSE;
safef(option, sizeof(option),"%s_sel", subtrack->track);
newVal = sameString(button, ADD_BUTTON_LABEL);
cartSetBoolean(cart, option, newVal);
}
}
}
}
puts("
");
}
return TRUE;
}
static bool mouseOverJsDone = FALSE;
void printInfoIconSvg(char *color)
/* Print just info icon (i) as svg tag to stdout, default color is #1C274C */
{
puts("");
}
void printInfoIconColor(char *mouseover, char *color)
/* Print info icon (i) with explanatory text on mouseover, with color */
{
printf("", mouseover);
printInfoIconSvg(color);
puts("");
if (!mouseOverJsDone)
{
jsInline("document.addEventListener('DOMContentLoaded', function() {\n"
" convertTitleTagsToMouseovers();\n"
" });\n");
mouseOverJsDone = TRUE;
}
}
void printInfoIcon(char *mouseover)
/* Print info icon (i) with explanatory text on mouseover */
{
// see https://www.svgrepo.com/svg/524660/info-circle
printf("", mouseover);
printInfoIconSvg("#1C274C");
puts("");
if (!mouseOverJsDone)
{
jsInline("document.addEventListener('DOMContentLoaded', function() {\n"
" convertTitleTagsToMouseovers();\n"
" });\n");
mouseOverJsDone = TRUE;
}
}
void hCompositeUi(char *db, struct cart *cart, struct trackDb *tdb,
char *primarySubtrack, char *fakeSubmit, char *formName)
// UI for composite tracks: subtrack selection. If primarySubtrack is
// non-NULL, don't allow it to be cleared and only offer subtracks
// that have the same type. If fakeSubmit is non-NULL, add a hidden
// var with that name so it looks like it was pressed.
{
bool hasSubgroups = (trackDbSetting(tdb, "subGroup1") != NULL);
boolean isMatrix = dimensionsExist(tdb);
boolean viewsOnly = FALSE;
if (primarySubtrack == NULL && !cartVarExists(cart, "ajax"))
{
if (trackDbSetting(tdb, "dragAndDrop") != NULL)
jsIncludeFile("jquery.tablednd.js", NULL);
jsIncludeFile("ajax.js",NULL);
jsIncludeFile("hui.js",NULL);
jsIncludeFile("subCfg.js",NULL);
jsIncludeFile("ddcl.js", NULL);
webIncludeResourceFile("ui.dropdownchecklist.css");
jsIncludeFile("ui.dropdownchecklist.js",NULL);
}
cgiDown(0.3);
boolean hideSubtracksDefault;
// TODO: Gray out or otherwise suppress when in multi-region mode
if (compositeHideEmptySubtracksSetting(tdb, &hideSubtracksDefault, NULL, NULL))
{
char *hideLabel = "Hide empty subtracks";
hideLabel = trackDbSettingOrDefault(tdb, SUBTRACK_HIDE_EMPTY_LABEL, hideLabel);
printf("
%s: ", hideLabel);
char buf[128];
safef(buf, sizeof buf, "%s.%s", tdb->track, SUBTRACK_HIDE_EMPTY);
boolean doHideEmpties = compositeHideEmptySubtracks(cart, tdb, NULL, NULL);
cgiMakeCheckBox(buf, doHideEmpties);
// info icon with explanatory text on mouseover
char *info =
"Subtracks with no data in the browser window are hidden. Changing the browser window"
" by zooming or scrolling may result in display of a different selection of tracks.";
printInfoIcon(info);
printf("
");
}
if (trackDbCountDescendantLeaves(tdb) < MANY_SUBTRACKS && !hasSubgroups)
{
if (primarySubtrack)
compositeUiSubtracksMatchingPrimary(db, cart, tdb,primarySubtrack);
else
compositeUiSubtracks(db, cart, tdb);
return;
}
if (fakeSubmit)
cgiMakeHiddenVar(fakeSubmit, "submit");
if (primarySubtrack == NULL)
{
if (subgroupingExists(tdb,"view"))
{
hCompositeDisplayViewDropDowns(db, cart,tdb);
if (subgroupCount(tdb) <= 1)
viewsOnly = TRUE;
}
if (!viewsOnly)
{
cgiDown(0.7);
if (trackDbSettingOn(tdb, "allButtonPair"))
{
compositeUiAllButtons(db, cart, tdb, formName);
}
else if (!hasSubgroups || !isMatrix)
{
compositeUiNoMatrix(db, cart, tdb, formName);
}
else
{
compositeUiByMatrix(db, cart, tdb, formName);
}
}
}
cartSaveSession(cart);
cgiContinueHiddenVar("g");
if (primarySubtrack)
compositeUiSubtracksMatchingPrimary(db, cart, tdb,primarySubtrack);
else
compositeUiSubtracks(db, cart, tdb);
if (primarySubtrack == NULL) // primarySubtrack is set for tableBrowser but not hgTrackUi
{
if (trackDbCountDescendantLeaves(tdb) > 5)
{
cgiDown(0.7);
cgiMakeButton("Submit", "Submit");
}
}
}
boolean superTrackDropDownWithExtra(struct cart *cart, struct trackDb *tdb,
int visibleChild, struct slPair *events)
// Displays hide/show dropdown for supertrack.
// Set visibleChild to indicate whether 'show' should be grayed
// out to indicate that no supertrack members are visible:
// 0 to gray out (no visible children)
// 1 don't gray out (there are visible children)
// -1 don't know (this function should determine)
// If -1,i the subtracks field must be populated with the child trackDbs.
// Returns false if not a supertrack
{
if (!tdbIsSuperTrack(tdb))
return FALSE;
// determine if supertrack is show/hide
boolean show = FALSE;
char *setting =
cartUsualString(cart, tdb->track, tdb->isShow ? "show" : "hide");
if (sameString("show", setting))
show = TRUE;
// Determine if any tracks in supertrack are visible; if not, the 'show' is grayed out
if (show && (visibleChild == -1))
{
visibleChild = 0;
struct slRef *childRef;
for ( childRef = tdb->children; childRef != NULL; childRef = childRef->next)
{
struct trackDb *cTdb = childRef->val;
cTdb->visibility =
hTvFromString(cartUsualString(cart, cTdb->track,
hStringFromTv(cTdb->visibility)));
if (cTdb->visibility != tvHide)
visibleChild = 1;
}
}
hideShowDropDownWithClassExtraAndLabel(tdb->track, NULL, show, (show && visibleChild) ?
"superDropdown normalText visDD" : "superDropdown hiddenText visDD",
events, tdb->shortLabel);
return TRUE;
}
int tvConvertToNumericOrder(enum trackVisibility v)
{
return ((v) == tvShow ? 5 : \
(v) == tvFull ? 4 : \
(v) == tvPack ? 3 : \
(v) == tvSquish ? 2 : \
(v) == tvDense ? 1 : 0);
}
int tvCompare(enum trackVisibility a, enum trackVisibility b)
/* enum trackVis isn't in numeric order by visibility, so compare
* symbolically: */
{
return (tvConvertToNumericOrder(b) - tvConvertToNumericOrder(a));
}
enum trackVisibility tvMin(enum trackVisibility a, enum trackVisibility b)
/* Return the less visible of a and b. */
{
if (tvCompare(a, b) >= 0)
return a;
else
return b;
}
enum trackVisibility tdbLocalVisibility(struct cart *cart, struct trackDb *tdb,
boolean *subtrackOverride)
// returns visibility NOT limited by ancestry.
// Fills optional boolean if subtrack specific vis is found
// If not NULL cart will be examined without ClosestToHome.
// Folders/supertracks resolve to hide/full
{
if (subtrackOverride != NULL)
*subtrackOverride = FALSE; // default
// tdb->visibility should reflect local trackDb setting
enum trackVisibility vis = tdb->visibility;
if (tdbIsSuperTrack(tdb))
vis = (tdb->isShow ? tvFull : tvHide);
if (cart != NULL) // cart is optional
{
char *cartVis = cartOptionalString(cart, tdb->track);
boolean cgiVar = FALSE;
// check hub tracks for visibility settings without the hub prefix
if (startsWith("hub_", tdb->track) && (cartVis == NULL))
{
cartVis = cgiOptionalString( trackHubSkipHubName(tdb->track));
cgiVar = TRUE;
}
if (cartVis != NULL)
{
vis = hTvFromString(cartVis);
if (subtrackOverride != NULL && tdbIsContainerChild(tdb))
*subtrackOverride = TRUE;
if (cgiVar)
{
cartSetString(cart, tdb->track, cartVis); // add the decorated visibility to the cart
cartRemove(cart, trackHubSkipHubName(tdb->track)); // remove the undecorated version
}
}
}
return vis;
}
enum trackVisibility tdbVisLimitedByAncestors(struct cart *cart, struct trackDb *tdb,
boolean checkBoxToo, boolean foldersToo)
// returns visibility limited by ancestry.
// This includes subtrack vis override and parents limit maximum.
// cart may be null, in which case, only trackDb settings (default state) are examined
// checkBoxToo means ensure subtrack checkbox state is visible
// foldersToo means limit by folders (aka superTracks) as well.
{
boolean subtrackOverride = FALSE;
enum trackVisibility vis = tdbLocalVisibility(cart,tdb,&subtrackOverride);
if (tdbIsContainerChild(tdb))
{
// subtracks without explicit (cart) vis but are selected, should get inherited vis
if (!subtrackOverride)
vis = tvFull;
// subtracks with checkbox that says no, are stopped cold
if (checkBoxToo && !fourStateVisible(subtrackFourStateChecked(tdb,cart)))
vis = tvHide; // Checkbox says no
}
if (subtrackOverride)
return vis;
// aka superTrack
if (vis == tvHide || tdb->parent == NULL || (!foldersToo && tdbIsFolder(tdb->parent)))
return vis; // end of line
return tvMin(vis,tdbVisLimitedByAncestors(cart,tdb->parent,checkBoxToo,foldersToo));
}
char *compositeViewControlNameFromTdb(struct trackDb *tdb)
// Returns a string with the composite view control name if one exists
{
char *stView = NULL;
char *name = NULL;
char *rootName = NULL;
// This routine should give these results: compositeName.viewName or else subtrackName.viewName
// or else compositeName or else subtrackName
if (tdbIsCompositeChild(tdb) == TRUE && trackDbLocalSetting(tdb, "parent") != NULL)
{
if (trackDbSettingClosestToHomeOn(tdb, "configurable"))
rootName = tdb->track; // subtrackName
else
rootName = firstWordInLine(cloneString(trackDbLocalSetting(tdb, "parent")));
}
if (rootName != NULL)
{
if (subgroupFind(tdb,"view",&stView))
{
int len = strlen(rootName) + strlen(stView) + 3;
name = needMem(len);
safef(name,len,"%s.%s",rootName,stView);
subgroupFree(&stView);
}
else
name = cloneString(rootName);
}
else
name = cloneString(tdb->track);
return name;
}
void compositeViewControlNameFree(char **name)
// frees a string allocated by compositeViewControlNameFromTdb
{
if (name && *name)
freez(name);
}
boolean isNameAtParentLevel(struct trackDb *tdb,char *name)
// cfgUi controls are passed a prefix name that may be at the composite, view or subtrack level
// returns TRUE if name at view or composite level
{
struct trackDb *parent;
for (parent = tdb->parent; parent != NULL; parent = parent->parent)
if (startsWithWordByDelimiter(parent->track, '.', name))
return TRUE;
return FALSE;
}
boolean chainDbNormScoreAvailable(struct trackDb *tdb)
/* check if normScore column is specified in trackDb as available */
{
boolean normScoreAvailable = FALSE;
char * normScoreTest =
trackDbSettingClosestToHomeOrDefault(tdb, "chainNormScoreAvailable", "no");
if (differentWord(normScoreTest, "no"))
normScoreAvailable = TRUE;
return normScoreAvailable;
}
void hPrintAbbreviationTable(struct sqlConnection *conn, char *sourceTable, char *label)
/* Print out table of abbreviations. */
{
char query[256];
sqlSafef(query, sizeof(query), "select name,description from %s order by name", sourceTable);
struct sqlResult *sr = sqlGetResult(conn, query);
webPrintLinkTableStart();
webPrintLabelCell("Symbol");
webPrintLabelCell(label);
char **row;
while ((row = sqlNextRow(sr)) != NULL)
{
printf("
\n");
char *name = row[0];
char *description = row[1];
webPrintLinkCell(name);
webPrintLinkCell(description);
}
sqlFreeResult(&sr);
webPrintLinkTableEnd();
}
/* Special info (cell type abbreviations) for factorSource tracks */
struct factorSourceInfo
/* Cell type and description */
{
struct factorSourceInfo *next;
char *name;
char *description;
};
static int factorSourceInfoCmp(const void *va, const void *vb)
/* Compare two factorSourceInfo's, sorting on name and then description fields */
{
static char bufA[64], bufB[64];
const struct factorSourceInfo *a = *((struct factorSourceInfo **)va);
const struct factorSourceInfo *b = *((struct factorSourceInfo **)vb);
safef(bufA, 64, "%s+%s", a->name, a->description);
safef(bufB, 64, "%s+%s", b->name, b->description);
return strcmp(bufA, bufB);
}
void hPrintFactorSourceAbbrevTable(struct sqlConnection *conn, struct trackDb *tdb)
/* Print out table of abbreviations. With 'pack' setting,
* show cell name only (before '+') and uniqify */
{
char *label = "Cell Type";
char *sourceTable = trackDbRequiredSetting(tdb, SOURCE_TABLE);
char query[256];
sqlSafef(query, sizeof(query), "select name,description from %s order by name", sourceTable);
struct sqlResult *sr = sqlGetResult(conn, query);
webPrintLinkTableStart();
webPrintLabelCell("Symbol");
webPrintLabelCell(label);
char **row;
char *plus;
struct factorSourceInfo *source = NULL, *sources = NULL;
while ((row = sqlNextRow(sr)) != NULL)
{
char *name = row[0];
char *description = row[1];
// truncate description to just the cell type
if ((plus = strchr(description, '+')) != NULL)
*plus = 0;
AllocVar(source);
source->name = cloneString(name);
source->description = cloneString(description);
slAddHead(&sources, source);
}
slUniqify(&sources, factorSourceInfoCmp, NULL);
int count = 0;
while ((source = slPopHead(&sources)) != NULL)
{
printf("
\n");
webPrintLinkCell(source->name);
webPrintLinkCellStart();
fputs(source->description, stdout);
count++;
while (sources && sameString(sources->name, source->name))
{
source = slPopHead(&sources);
fputs(", ", stdout);
fputs(source->description, stdout);
count++;
}
webPrintLinkCellEnd();
}
sqlFreeResult(&sr);
webPrintLinkTableEnd();
printf("Total: %d\n", count);
}
static char *makeOnePennantIcon(char *setting, char **hintRet)
// Builds a string with pennantIcon HTML and returns it. Also returns hint. */
{
setting = cloneString(setting);
char *icon = nextWord(&setting);
char buffer[4096];
char *src = NULL;
char *url = NULL, *hint = NULL, *color = NULL;
boolean isTextIcon = FALSE;
if (!(endsWith(icon, ".jpg") || endsWith(icon, ".png")))
{
isTextIcon = TRUE;
color = nextWord(&setting);
src = strLower(icon);
}
else if (startsWith("http://", icon) || startsWith("https://", icon) ||
startsWith("ftp://", icon))
src = htmlEncode(icon);
else
{
safef(buffer, sizeof buffer, "../images/%s", icon);
src = htmlEncode(buffer);
}
if (setting)
{
url = nextWord(&setting);
if (setting)
{
hint = htmlEncode(stripEnclosingDoubleQuotes(setting));
}
}
struct dyString *ds = dyStringNew(0);
// generate markup
if (url)
dyStringPrintf(ds, "");
// add text or image
if (isTextIcon)
dyStringPrintf(ds, "%s", src);
else
dyStringPrintf(ds, "", src);
// close tags
if (url)
dyStringAppend(ds, "");
else if (isTextIcon)
dyStringAppend(ds, "");
dyStringAppend(ds, "\n");
if (hint && hintRet)
*hintRet = cloneString(hint);
return dyStringCannibalize(&ds);
}
static struct slPair *makePennantIcons(struct trackDb *tdb)
/* Return a list of pairs of pennantIcon HTML and note strings. */
{
char *setting = trackDbSetting(tdb, "pennantIcon");
if (setting == NULL || sameString(setting, "none"))
return NULL;
struct slPair *list = NULL;
int maxPennants = 3;
char *pennants[maxPennants];
int numPennants = chopByChar(setting, ';', pennants, ArraySize(pennants));
int i;
for (i = 0; i < numPennants; i++)
{
char *hint = NULL;
char *html = makeOnePennantIcon(pennants[i], &hint);
slPairAdd(&list, html, hint);
freeMem(html);
}
slReverse(&list);
return list;
}
void hPrintIcons(struct trackDb *tdb)
/* prints optional folder and pennants icons and a space, if any icons were printed */
{
bool hasIcon = hPrintPennantIcon(tdb);
if (tdbIsSuper(tdb) || tdbIsComposite(tdb))
{
// this is the folder.svg icon from the font-awesome collection.
// the icon collection also contains a "fa fa-folder-o" icon, which is the outlined version
// It was decided to use only the filled out icon for now and use the same icon for super
// and composite tracks. Adding the SVG removes a dependency and makes the icons show up instantly,
// instead of the short delay when using fonts. Github uses icons like this.
hPrintf(""
"");
hasIcon = TRUE;
}
if (hasIcon)
hPrintf(" ");
}
boolean hPrintPennantIcon(struct trackDb *tdb)
// Returns TRUE and prints out the "pennantIcon" when found.
// Example: ENCODE tracks in hgTracks config list.
{
if (trackDbSetting(tdb, "wgEncode") != NULL)
{
hPrintf("\n");
}
struct slPair *list = makePennantIcons(tdb), *el;
boolean gotPennant = (list != NULL);
for (el = list; el != NULL; el = el->next)
hPrintf("%s\n", el->name);
slPairFreeValsAndList(&list);
return gotPennant;
}
boolean printPennantIconNote(struct trackDb *tdb)
// Returns TRUE and prints out the "pennantIcon" and note when found.
//This is used by hgTrackUi and hgc before printing out trackDb "html"
{
struct slPair *list = makePennantIcons(tdb), *el;
boolean gotPennant = (list != NULL);
for (el = list; el != NULL; el = el->next)
{
printf(" %s\n", el->name);
char *hint = el->val;
if (hint)
printf("Note: %s\n", hint);
}
slPairFreeValsAndList(&list);
return gotPennant;
}
void printUpdateTime(char *database, struct trackDb *tdb,
struct customTrack *ct)
/* display table update time */
{
if (trackHubDatabase(database))
return;
/* synthetic tracks have no data file or table to check */
if (trackDbSetting(tdb, "syntheticTrack") != NULL)
return;
/* have not decided what to do for a composite container */
if (tdbIsComposite(tdb) || tdbIsSuper(tdb))
return;
struct sqlConnection *conn = NULL;
char *tableName = NULL;
if (isCustomTrack(tdb->track))
{
if (ct)
{
conn = hAllocConn(CUSTOM_TRASH);
tableName = ct->dbTableName;
}
}
else if (startsWith("big", tdb->type))
{
char *tableName = hTableForTrack(database, tdb->table);
struct sqlConnection *conn = hAllocConnTrack(database, tdb);
char *bbiFileName = bbiNameFromSettingOrTable(tdb, conn, tableName);
hFreeConn(&conn);
struct bbiFile *bbi = NULL;
if (startsWith("bigWig", tdb->type))
bbi = bigWigFileOpen(bbiFileName);
else
bbi = bigBedFileOpen(bbiFileName);
time_t timep = 0;
if (bbi)
{
timep = bbiUpdateTime(bbi);
bbiFileClose(&bbi);
}
printBbiUpdateTime(&timep);
}
else
{
tableName = hTableForTrack(database, tdb->table);
conn = hAllocConnTrack(database, tdb);
}
if (tableName)
{
char *date = firstWordInLine(sqlTableUpdate(conn, tableName));
if (date != NULL)
printf("Data last updated at UCSC: %s \n", date);
}
hFreeConn(&conn);
}
static struct trackDb *findTdbByBareName(struct trackDb *tdbList, char *bareName)
/* Recursively search tdbList (and subtracks) for a tdb whose bare track name matches. */
{
struct trackDb *tdb;
for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
{
if (sameString(trackHubSkipHubName(tdb->track), bareName))
return tdb;
struct trackDb *found = findTdbByBareName(tdb->subtracks, bareName);
if (found != NULL)
return found;
}
return NULL;
}
char *getTrackHtml(char *db, char *trackName)
/* Grab HTML from trackDb in native database for quickLift tracks. */
{
char *html = NULL;
if (trackHubDatabase(db) || isGenArk(db))
{
struct trackHub *hub = NULL;
struct trackHubGenome *hubGenome = trackHubGetGenome(db);
if (hubGenome != NULL)
hub = hubGenome->trackHub;
else if (isGenArk(db))
{
char *hubUrl = genarkUrl(db);
if (hubUrl != NULL)
{
hub = trackHubOpen(hubUrl, "");
if (hub != NULL)
hubGenome = trackHubFindGenome(hub, db);
}
}
if (hubGenome != NULL)
{
struct trackDb *tdbList = trackHubAddTracksGenome(hubGenome);
struct trackDb *tdb = findTdbByBareName(tdbList, trackHubSkipHubName(trackName));
if (tdb != NULL)
{
trackHubAddDescription(hubGenome->trackDbFile, tdb);
html = tdb->html;
}
}
}
else
{
char query[4096];
sqlSafef(query, sizeof query, "tableName = '%s'", trackHubSkipHubName(trackName));
struct trackDb *loadTrackDb(char *db, char *where);
struct trackDb *tdb = loadTrackDb(db, query);
html = tdb->html;
//char *html = tdb->html;
if (isEmpty(tdb->html))
{
char *parent = trackDbSetting(tdb, "parent");
char *words[10];
chopLine(parent,words);
sqlSafef(query, sizeof query, "tableName = '%s'", trackHubSkipHubName(words[0]));
struct trackDb *tdb = loadTrackDb(db, query);
html = tdb->html;
}
}
return html;
}
void printBbiUpdateTime(time_t *timep)
/* for bbi files, print out the timep value */
{
printf("Data last updated at UCSC: %s \n", sqlUnixTimeToDate(timep, FALSE));
}
static boolean tableDescriptionsExists(struct sqlConnection *conn)
/* Cache flag for whether tableDescriptions exists in conn, in case we will need to
* fetch a lot of descriptions from tableDescriptions. */
{
static struct hash *hash = NULL;
if (hash == NULL)
hash = hashNew(0);
char *db = sqlGetDatabase(conn);
int exists = hashIntValDefault(hash, db, -1);
if (exists < 0)
{
exists = sqlTableExists(conn, "tableDescriptions");
hashAddInt(hash, db, exists);
}
return (boolean)exists;
}
struct asObject *asFromTableDescriptions(struct sqlConnection *conn, char *table)
// If there is a tableDescriptions table and it has an entry for table, return
// a parsed autoSql object; otherwise return NULL.
{
struct asObject *asObj = NULL;
+// Callers occasionally invoke asForTdb with conn=NULL (e.g. superTrack filter
+// rendering that isn't tied to a data table). Nothing to look up in that case.
+if (conn == NULL)
+ return NULL;
if (tableDescriptionsExists(conn))
{
char query[PATH_LEN*2];
// Try unsplit table first.
sqlSafef(query, sizeof(query),
"select autoSqlDef from tableDescriptions where tableName='%s'", table);
char *asText = sqlQuickString(conn, query);
// If no result try split table.
if (asText == NULL)
{
sqlSafef(query, sizeof(query),
"select autoSqlDef from tableDescriptions where tableName='chrN_%s'", table);
asText = sqlQuickString(conn, query);
}
if (isNotEmpty(asText))
asObj = asParseText(asText);
freez(&asText);
}
return asObj;
}
static struct asObject *asForTdbOrDie(struct sqlConnection *conn, struct trackDb *tdb)
// Get autoSQL description if any associated with tdb.
// Abort if there's a problem
{
struct asObject *asObj = NULL;
if (tdbIsBigBed(tdb))
{
char *fileName = hReplaceGbdb(tdbBigFileName(conn, tdb));
if (fileName == NULL)
return NULL;
asObj = bigBedFileAsObjOrDefault(fileName);
freeMem(fileName);
}
// TODO: standardize to a wig as
//else if (tdbIsBigWig(tdb))
// asObj = asObjFrombigBed(conn,tdb);
else if (tdbIsLongTabix(tdb))
asObj = longTabixAsObj();
else if (tdbIsBam(tdb))
asObj = bamAsObj();
else if (tdbIsVcf(tdb))
asObj = vcfAsObj();
else if (startsWithWord("makeItems", tdb->type))
asObj = makeItemsItemAsObj();
else if (sameWord("bedDetail", tdb->type))
asObj = bedDetailAsObj();
else if (sameWord("pgSnp", tdb->type))
asObj = pgSnpAsObj();
else if (sameWord("barChart", tdb->type))
asObj = asParseText(barChartAutoSqlString);
else if (sameWord("interact", tdb->type))
asObj = interactAsObj();
else if (sameWord("hic", tdb->type))
// HI-C data are stored in .hic files, but parsed into interact objects
asObj = interactAsObj();
else if (sameWord("bedMethyl", tdb->type))
asObj = bedMethylAsObj();
else
asObj = asFromTableDescriptions(conn, tdb->table);
return asObj;
}
struct asObject *asForTdb(struct sqlConnection *conn, struct trackDb *tdb)
// Get autoSQL description if any associated with table, ignoring errAborts if any.
{
struct errCatch *errCatch = errCatchNew();
struct asObject *asObj = NULL;
// Wrap some error catching around asForTdbOrDie.
if (errCatchStart(errCatch))
{
asObj = asForTdbOrDie(conn, tdb);
}
errCatchEnd(errCatch);
errCatchFree(&errCatch);
return asObj;
}
struct asObject *asForDb(struct trackDb *tdb, char* database)
/* return asObject given the database. NULL if not found */
{
struct sqlConnection *conn = NULL ;
if (!trackHubDatabase(database))
conn = hAllocConnTrack(database, tdb);
struct asObject *as = asForTdb(conn, tdb);
hFreeConn(&conn);
return as;
}
#ifdef OLD /* This got moved to main library . */
struct asColumn *asColumnFind(struct asObject *asObj, char *name)
// Return named column.
{
struct asColumn *asCol = NULL;
if (asObj!= NULL)
{
for (asCol = asObj->columnList; asCol != NULL; asCol = asCol->next)
if (sameString(asCol->name, name))
break;
}
return asCol;
}
#endif /* OLD */
struct slName *asColNames(struct asObject *as)
// Get list of column names.
{
struct slName *list = NULL, *el;
struct asColumn *col;
for (col = as->columnList; col != NULL; col = col->next)
{
el = slNameNew(col->name);
slAddHead(&list, el);
}
slReverse(&list);
return list;
}
static struct dyString *subMulti(char *orig, int subCount,
char *in[], char *out[])
/* Perform multiple substitions on orig. */
{
int i;
struct dyString *s = dyStringNew(256), *d = NULL;
dyStringAppend(s, orig);
for (i=0; istring, in[i], out[i]);
dyStringFree(&s);
s = d;
d = NULL;
}
return s;
}
char *replaceInUrl(char *url, char *idInUrl, struct cart *cart, char *db, char *seqName,
int winStart, int winEnd, char *track, boolean encode, struct slPair *fields)
/* replace $$ in url with idInUrl. Supports many other wildchards, and custom fields $
* XX Do we have readable docs for these parameters somewhere?
* Look at http://genome.ucsc.edu/goldenpath/help/trackDb/trackDbHub.html */
{
struct dyString *uUrl = NULL;
struct dyString *eUrl = NULL;
char startString[64], endString[64],oneBasedStart[64];
char *ins[14], *outs[14];
char *eItem = (encode ? cgiEncode(idInUrl) : cloneString(idInUrl));
char *scName = NULL;
// try to avoid the mysql query it not necessary
if (stringIn("$n", url))
{
char *tmp = hScientificName(db);
scName = replaceChars(tmp, " ", "_");
freeMem(tmp);
}
char *taxId = NULL;
// try to avoid the mysql query it not necessary
if (stringIn("$taxId", url))
{
char query[256];
struct sqlConnection *centralConn = hConnectCentral();
sqlSafef(query, sizeof(query),
"select taxId from %s "
"where name='%s'", dbDbTable(), db);
taxId = sqlQuickString(centralConn, query);
hDisconnectCentral(¢ralConn);
}
safef(startString, sizeof startString, "%d", winStart);
safef(endString, sizeof endString, "%d", winEnd);
ins[0] = "$$";
outs[0] = idInUrl;
ins[1] = "$T";
outs[1] = track;
ins[2] = "$S";
outs[2] = seqName;
ins[3] = "$[";
outs[3] = startString;
ins[4] = "$]";
outs[4] = endString;
ins[5] = "$s";
outs[5] = skipChr(seqName);
ins[6] = "$D";
outs[6] = trackHubSkipHubName(db);
ins[7] = "$P"; /* for an item name of the form: prefix:suffix */
ins[8] = "$p"; /* the P is the prefix, the p is the suffix */
if (idInUrl && stringIn(":", idInUrl)) {
char *itemClone = cloneString(idInUrl);
char *suffix = stringIn(":", itemClone);
char *suffixClone = cloneString(suffix+1); /* +1 skip the : */
char *nextColon = stringIn(":", suffixClone+1);
if (nextColon) /* terminate suffixClone suffix */
*nextColon = '\0'; /* when next colon is present */
*suffix = '\0'; /* terminate itemClone prefix */
outs[7] = itemClone;
outs[8] = suffixClone;
/* small memory leak here for these cloned strings */
/* not important for a one-time operation in a CGI that will exit */
} else {
outs[7] = idInUrl; /* otherwise, these are not expected */
outs[8] = idInUrl; /* to be used */
}
// URL may now contain item boundaries
ins[9] = "${";
ins[10] = "$}";
ins[13] = "$#";
if (cart!=NULL && cartOptionalString(cart, "o") && cartOptionalString(cart, "t"))
{
char *itemBeg = cartString(cart, "o"); // unexpected commas?
char *itemEnd = cartString(cart, "t");
outs[9] = itemBeg;
outs[10] = itemEnd;
safef(oneBasedStart, sizeof(oneBasedStart), "%d", cartInt(cart, "o") + 1);
outs[13] = oneBasedStart;
}
else // should never be but I am unwilling to bet the farm
{
outs[9] = startString;
outs[10] = endString;
safef(oneBasedStart, sizeof(oneBasedStart), "%d", winStart + 1);
outs[13] = oneBasedStart;
}
ins[11] = "$n";
outs[11] = scName;
ins[12] = "$taxId";
outs[12] = taxId;
uUrl = subMulti(url, ArraySize(ins), ins, outs);
outs[0] = eItem;
eUrl = subMulti(url, ArraySize(ins), ins, outs);
dyStringFree(&uUrl);
freeMem(eItem);
freeMem(scName);
// substitute $ variables
if (!fields)
return eUrl->string;
int fieldCount = slCount(fields);
char **fieldNames = NULL, **fieldVals = NULL;
AllocArray(fieldNames, fieldCount);
AllocArray(fieldVals, fieldCount);
int i;
struct slPair *field;
for (i=0, field=fields; inext)
{
char buf[64];
safef(buf, sizeof buf, "$<%s>", field->name);
fieldNames[i] = cloneString(buf);
fieldVals[i] = (char *)field->val;
}
struct dyString *fUrl = subMulti(eUrl->string, fieldCount, fieldNames, fieldVals);
return fUrl->string;
}
char *checkDataVersion(char *database, struct trackDb *tdb)
/* see if trackDb has a dataVersion setting and check that file for version */
{
// try the metadata
metadataForTable(database, tdb, NULL);
char *version = (char *)metadataFindValue(tdb, "dataVersion");
// try trackDb itself, this automatically will go up the hierarchy
if (version == NULL)
version = trackDbSetting(tdb, "dataVersion");
if (version != NULL)
{
// dataVersion can also be the path to a local file, for otto tracks
if (!trackHubDatabase(database) && !isHubTrack(tdb->table) && startsWith("/", version))
{
char *path = replaceInUrl(version, "", NULL, database, "", 0, 0, tdb->track, FALSE, NULL);
struct lineFile* lf = lineFileMayOpen(path, TRUE);
if (lf)
version = lineFileReadAll(lf);
else
version = NULL;
lineFileClose(&lf);
}
}
return version;
}
void printDataVersion(char *database, struct trackDb *tdb)
/* If this annotation has a dataVersion setting, print it.
* check hgFixed.trackVersion, meta data and trackDb 'dataVersion'. */
{
char *version = checkDataVersion(database, tdb);
if (version == NULL)
{
// try the hgFixed.trackVersion table
struct trackVersion *trackVersion = getTrackVersion(database, tdb->track);
// try trackVersion table with parent, for composites/superTracks
if (trackVersion == NULL && tdb->parent != NULL)
trackVersion = getTrackVersion(database, tdb->parent->track);
if (trackVersion != NULL)
version = trackVersion->version;
}
if (isNotEmpty(version))
printf("Version: %s \n", version);
}
void printRelatedTracks(char *database, struct hash *trackHash, struct trackDb *tdb, struct cart *cart)
/* Maybe print a "related track" section */
{
if (trackHubDatabase(database))
return;
char *relatedTrackTable = cfgOptionDefault("db.relatedTrack","relatedTrack");
struct sqlConnection *conn = hAllocConn(database);
if (!sqlTableExists(conn, relatedTrackTable))
{
hFreeConn(&conn);
return;
}
char query[256];
sqlSafef(query, sizeof(query),
"select track2, why from %s where track1='%s'", relatedTrackTable, tdb->track);
char **row;
struct sqlResult *sr;
sr = sqlGetResult(conn, query);
row = sqlNextRow(sr);
if (row != NULL)
{
puts("Related tracks\n");
puts("
\n");
struct hash *otherTracksAndDesc = hashNew(0);
char *why, *otherTrack;
for (; row != NULL; row = sqlNextRow(sr))
{
otherTrack = row[0];
why = row[1];
// hopefully relatedTracks.ra doesn't have dupes but hash them just in case
hashReplace(otherTracksAndDesc, cloneString(otherTrack), cloneString(why));
}
struct hashEl *hel, *helList = hashElListHash(otherTracksAndDesc);
for (hel = helList; hel != NULL; hel = hel->next)
{
char *otherTrack = (char *)hel->name;
char *why = (char *)hel->val;
struct trackDb *otherTdb = hashFindVal(trackHash, otherTrack);
// super tracks are not in the hash:
if (!otherTdb)
otherTdb = tdbForTrack(database, otherTrack, NULL);
if (otherTdb)
{
puts("