b312b42ad8c3e89350417bb7b05c9e05a1507281
kate
Thu Jan 27 22:10:20 2011 -0800
Support for genome browser preview site.
1. Changes browser title in CGIs
2. Prints warning text on hgGateway
3. Prints warning text on home page
1. and 2. activated by HTTP_HOST prefix 'genome-preview', or by setting
test.preview=true in hg.conf
diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 0979185..2a80e1f 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -1,5670 +1,5670 @@
/* hgTracks - the original, and still the largest module for the UCSC Human Genome
* Browser main cgi script. Currently contains most of the track framework, though
* there's quite a bit of other framework type code in simpleTracks.c. The main
* routine got moved to create a new entry point to the bulk of the code for the
* hgRenderTracks web service. See mainMain.c for the main used by the hgTracks CGI. */
#include "common.h"
#include "hCommon.h"
#include "linefile.h"
#include "portable.h"
#include "memalloc.h"
#include "localmem.h"
#include "obscure.h"
#include "dystring.h"
#include "hash.h"
#include "jksql.h"
#include "gfxPoly.h"
#include "memgfx.h"
#include "hvGfx.h"
#include "psGfx.h"
#include "cheapcgi.h"
#include "hPrint.h"
#include "htmshell.h"
#include "cart.h"
#include "hdb.h"
#include "hui.h"
#include "hgFind.h"
#include "hgTracks.h"
#include "trashDir.h"
#include "grp.h"
#include "versionInfo.h"
#include "web.h"
#include "cds.h"
#include "cutterTrack.h"
#include "wikiTrack.h"
#include "ctgPos.h"
#include "bed.h"
#include "bigBed.h"
#include "bigWig.h"
#include "bedCart.h"
#include "customTrack.h"
#include "trackHub.h"
#include "hubConnect.h"
#include "cytoBand.h"
#include "ensFace.h"
#include "liftOver.h"
#include "pcrResult.h"
#include "wikiLink.h"
#include "jsHelper.h"
#include "mafTrack.h"
#include "hgConfig.h"
#include "encode.h"
#include "agpFrag.h"
#include "imageV2.h"
#include "suggest.h"
#include "searchTracks.h"
static char const rcsid[] = "$Id: doMiddle.c,v 1.1651 2010/06/11 17:53:06 larrym Exp $";
/* These variables persist from one incarnation of this program to the
* next - living mostly in the cart. */
boolean baseShowPos; /* TRUE if should display full position at top of base track */
boolean baseShowAsm; /* TRUE if should display assembly info at top of base track */
boolean baseShowScaleBar; /* TRUE if should display scale bar at very top of base track */
boolean baseShowRuler; /* TRUE if should display the basic ruler in the base track (default) */
char *baseTitle = NULL; /* Title it should display top of base track (optional)*/
static char *userSeqString = NULL; /* User sequence .fa/.psl file. */
/* These variables are set by getPositionFromCustomTracks() at the very
* beginning of tracksDisplay(), and then used by loadCustomTracks(). */
char *ctFileName = NULL; /* Custom track file. */
struct customTrack *ctList = NULL; /* Custom tracks. */
boolean hasCustomTracks = FALSE; /* whether any custom tracks are for this db*/
struct slName *browserLines = NULL; /* Custom track "browser" lines. */
boolean withNextItemArrows = FALSE; /* Display next feature (gene) navigation buttons near center labels? */
boolean withPriorityOverride = FALSE; /* Display priority for each track to allow reordering */
int gfxBorder = hgDefaultGfxBorder; /* Width of graphics border. */
int guidelineSpacing = 12; /* Pixels between guidelines. */
boolean withIdeogram = TRUE; /* Display chromosome ideogram? */
int rulerMode = tvHide; /* on, off, full */
char *rulerMenu[] =
/* dropdown for ruler visibility */
{
"hide",
"dense",
"full"
};
char *protDbName; /* Name of proteome database for this genome. */
#define MAX_CONTROL_COLUMNS 6
#define LOW 1
#define MEDIUM 2
#define BRIGHT 3
#define MAXCHAINS 50000000
boolean hgDebug = FALSE; /* Activate debugging code. Set to true by hgDebug=on in command line*/
int imagePixelHeight = 0;
boolean dragZooming = TRUE;
struct hash *oldVars = NULL;
boolean hideControls = FALSE; /* Hide all controls? */
boolean trackImgOnly = FALSE; /* caller wants just the track image and track table html */
boolean ideogramToo = FALSE; /* caller wants the ideoGram (when requesting just one track) */
/* Structure returned from findGenomePos.
* We use this to to expand any tracks to full
* that were found to contain the searched-upon
* position string */
struct hgPositions *hgp = NULL;
/* Other global variables. */
struct trackHub *hubList = NULL; /* List of all relevant hubs. */
struct group *groupList = NULL; /* List of all tracks. */
-char *browserName; /* Test or public browser */
+char *browserName; /* Test, preview, or public browser */
char *organization; /* UCSC */
struct hash *trackHash = NULL; /* Hash of the tracks by their name. */
#ifdef DEBUG
void uglySnoopTrackList(int depth, struct track *trackList)
/* Print out some info on track list. */
{
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
if (stringIn("FaireH1h", track->track))
{
repeatCharOut(uglyOut, '+', depth);
uglyf("%s pri=%g defPri=%g \n", track->track, track->priority, track->defaultPriority);
}
uglySnoopTrackList(depth+1, track->subtracks);
}
}
#endif /* DEBUG */
struct track *trackFindByName(struct track *tracks, char *trackName)
/* find a track in tracks by name, recursively searching subtracks */
{
struct track *track;
for (track = tracks; track != NULL; track = track->next)
{
if (sameString(track->track, trackName))
return track;
else if (track->subtracks != NULL)
{
struct track *st = trackFindByName(track->subtracks, trackName);
if (st != NULL)
return st;
}
}
return NULL;
}
int tgCmpPriority(const void *va, const void *vb)
/* Compare to sort based on priority; use shortLabel as secondary sort key. */
{
const struct track *a = *((struct track **)va);
const struct track *b = *((struct track **)vb);
float dif = a->group->priority - b->group->priority;
if (dif == 0)
dif = a->priority - b->priority;
if (dif < 0)
return -1;
else if (dif == 0.0)
/* secondary sort on label */
return strcasecmp(a->shortLabel, b->shortLabel);
else
return 1;
}
int trackRefCmpPriority(const void *va, const void *vb)
/* Compare based on priority. */
{
const struct trackRef *a = *((struct trackRef **)va);
const struct trackRef *b = *((struct trackRef **)vb);
return tgCmpPriority(&a->track, &b->track);
}
int gCmpPriority(const void *va, const void *vb)
/* Compare groups based on priority. */
{
const struct group *a = *((struct group **)va);
const struct group *b = *((struct group **)vb);
float dif = a->priority - b->priority;
if (dif == 0)
return 0;
if (dif < 0)
return -1;
else if (dif == 0.0)
return 0;
else
return 1;
}
void changeTrackVis(struct group *groupList, char *groupTarget, int changeVis)
/* Change track visibilities. If groupTarget is
* NULL then set visibility for tracks in all groups. Otherwise,
* just set it for the given group. If vis is -2, then visibility is
* unchanged. If -1 then set visibility to default, otherwise it should
* be tvHide, tvDense, etc.
* If we are going back to default visibility, then reset the track
* ordering also. */
{
struct group *group;
if (changeVis == -2)
return;
for (group = groupList; group != NULL; group = group->next)
{
struct trackRef *tr;
if (groupTarget == NULL || sameString(group->name,groupTarget))
{
static char pname[512];
/* if default vis then reset group priority */
if (changeVis == -1)
group->priority = group->defaultPriority;
for (tr = group->trackList; tr != NULL; tr = tr->next)
{
struct track *track = tr->track;
struct trackDb *tdb = track->tdb;
if (changeVis == -1) // to default
{
if(tdbIsComposite(tdb))
{
safef(pname, sizeof(pname),"%s.*",track->track); //to remove all settings associated with this composite!
cartRemoveLike(cart,pname);
struct track *subTrack;
for(subTrack = track->subtracks;subTrack != NULL; subTrack = subTrack->next)
{
subTrack->visibility = tdb->visibility;
cartRemove(cart, subTrack->track);
}
}
/* restore defaults */
if (tdbIsSuperTrackChild(tdb) || tdbIsCompositeChild(tdb))
{
assert(tdb->parent != NULL && tdb->parent->track);
cartRemove(cart, tdb->parent->track);
if (withPriorityOverride)
{
safef(pname, sizeof(pname), "%s.priority",tdb->parent->track);
cartRemove(cart, pname);
}
}
track->visibility = tdb->visibility;
cartRemove(cart, track->track);
/* set the track priority back to the default value */
if (withPriorityOverride)
{
safef(pname, sizeof(pname), "%s.priority",track->track);
cartRemove(cart, pname);
track->priority = track->defaultPriority;
}
}
else // to changeVis value (Usually tvHide)
{
/* change to specified visibility */
if (tdbIsSuperTrackChild(tdb))
{
assert(tdb->parent != NULL);
/* Leave supertrack members alone -- only change parent */
struct trackDb *parentTdb = tdb->parent;
if ((changeVis == tvHide && !parentTdb->isShow) ||
(changeVis != tvHide && parentTdb->isShow))
{
/* remove if setting to default vis */
cartRemove(cart, parentTdb->track);
}
else
cartSetString(cart, parentTdb->track,
changeVis == tvHide ? "hide" : "show");
}
else // Not super child
{
if (changeVis == tdb->visibility)
/* remove if setting to default vis */
cartRemove(cart, track->track);
else
cartSetString(cart, track->track, hStringFromTv(changeVis));
track->visibility = changeVis;
}
// Whether super child or not, if its a composite, then handle the children
if (tdbIsComposite(tdb))
{
struct track *subtrack;
for(subtrack=track->subtracks;subtrack!=NULL;subtrack=subtrack->next)
{
if (changeVis == tvHide)
cartRemove(cart, subtrack->track); // Since subtrack level vis is an override, simply remove it to hide it
else
cartSetString(cart, subtrack->track, hStringFromTv(changeVis));
subtrack->visibility = changeVis;
}
}
}
}
}
}
slSort(&groupList, gCmpPriority);
}
int trackOffsetX()
/* Return x offset where track display proper begins. */
{
int x = gfxBorder;
if (withLeftLabels)
x += tl.leftLabelWidth + gfxBorder;
return x;
}
static void mapBoxTrackUi(struct hvGfx *hvg, int x, int y, int width,
int height, char *name, char *shortLabel, char *id)
/* Print out image map rectangle that invokes hgTrackUi. */
{
x = hvGfxAdjXW(hvg, x, width);
char *url = trackUrl(name, chromName);
if(theImgBox && curImgTrack)
{
struct imgSlice *curSlice = imgTrackSliceGetByType(curImgTrack,stButton);
if(curSlice)
sliceAddLink(curSlice,url,shortLabel);
}
else
{
hPrintf("\n");
}
freeMem(url);
}
static void mapBoxToggleComplement(struct hvGfx *hvg, int x, int y, int width, int height,
struct track *toggleGroup, char *chrom,
int start, int end, char *message)
/*print out a box along the DNA bases that toggles a cart variable
* "complement" to complement the DNA bases at the top by the ruler*/
{
struct dyString *ui = uiStateUrlPart(toggleGroup);
x = hvGfxAdjXW(hvg, x, width);
if(theImgBox && curImgTrack)
{
char link[512];
safef(link,sizeof(link),"%s?complement_%s=%d&%s",
hgTracksName(), database, !cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE),ui->string);
imgTrackAddMapItem(curImgTrack,link,(char *)(message != NULL?message:NULL),x, y, x+width, y+height, NULL);
}
else
{
hPrintf("string);
freeDyString(&ui);
if (message != NULL)
mapStatusMessage("%s", message);
hPrintf(">\n");
}
}
char *trackUrl(char *mapName, char *chromName)
/* Return hgTrackUi url; chromName is optional. */
{
char *encodedMapName = cgiEncode(mapName);
char buf[2048];
if(chromName == NULL)
safef(buf, sizeof(buf), "%s?%s=%u&g=%s", hgTrackUiName(), cartSessionVarName(), cartSessionId(cart), encodedMapName);
else
safef(buf, sizeof(buf), "%s?%s=%u&c=%s&g=%s", hgTrackUiName(), cartSessionVarName(), cartSessionId(cart), chromName, encodedMapName);
freeMem(encodedMapName);
return(cloneString(buf));
}
void smallBreak()
/* Draw small horizontal break */
{
hPrintf(" \n");
}
#ifdef REMOTE_TRACK_AJAX_CALLBACK
static boolean trackUsesRemoteData(struct track *track)
/* returns TRUE is this track has a remote datasource */
{
if (!IS_KNOWN(track->remoteDataSource))
{
SET_TO_NO(track->remoteDataSource);
//if (track->bbiFile != NULL) // FIXME: Chicken or the egg. bigWig/bigBed "bbiFile" filled in by loadItems, but we don't want to load items.
// {
// if (!startsWith("/gbdb/",track->bbiFile->fileName))
// SET_TO_YES(track->remoteDataSource);
// }
if (startsWithWord("bigWig",track->tdb->type) || startsWithWord("bigBed",track->tdb->type))
{
SET_TO_YES(track->remoteDataSource);
}
else if (startsWithWord("bam",track->tdb->type))
{
SET_TO_YES(track->remoteDataSource);
}
}
return IS_YES(track->remoteDataSource);
}
boolean trackShouldUseAjaxRetrieval(struct track *track)
/* Tracks with remote data sources should berendered via an ajax callback */
{
return (theImgBox && !trackImgOnly && trackUsesRemoteData(track));
}
#endif///def REMOTE_TRACK_AJAX_CALLBACK
static int trackPlusLabelHeight(struct track *track, int fontHeight)
/* Return the sum of heights of items in this track (or subtrack as it may be)
* and the center label(s) above the items (if any). */
{
if (trackShouldUseAjaxRetrieval(track))
return REMOTE_TRACK_HEIGHT;
int y = track->totalHeight(track, limitVisibility(track));
if (isCenterLabelIncluded(track))
y += fontHeight;
if (tdbIsComposite(track->tdb))
{
struct track *subtrack;
for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
if (isSubtrackVisible(subtrack) && isCenterLabelIncluded(subtrack))
y += fontHeight;
}
}
return y;
}
void drawColoredButtonBox(struct hvGfx *hvg, int x, int y, int w, int h,
int enabled, Color shades[])
/* draw button box, providing shades of the desired button color */
{
int light = shades[1], mid = shades[2], dark = shades[4];
if (enabled)
{
hvGfxBox(hvg, x, y, w, 1, light);
hvGfxBox(hvg, x, y+1, 1, h-1, light);
hvGfxBox(hvg, x+1, y+1, w-2, h-2, mid);
hvGfxBox(hvg, x+1, y+h-1, w-1, 1, dark);
hvGfxBox(hvg, x+w-1, y+1, 1, h-1, dark);
}
else /* try to make the button look as if
* it is already depressed */
{
hvGfxBox(hvg, x, y, w, 1, dark);
hvGfxBox(hvg, x, y+1, 1, h-1, dark);
hvGfxBox(hvg, x+1, y+1, w-2, h-2, light);
hvGfxBox(hvg, x+1, y+h-1, w-1, 1, light);
hvGfxBox(hvg, x+w-1, y+1, 1, h-1, light);
}
}
void drawGrayButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled)
/* Draw a gray min-raised looking button. */
{
drawColoredButtonBox(hvg, x, y, w, h, enabled, shadesOfGray);
}
void drawBlueButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled)
/* Draw a blue min-raised looking button. */
{
drawColoredButtonBox(hvg, x, y, w, h, enabled, shadesOfSea);
}
void drawButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled)
/* Draw a standard (gray) min-raised looking button. */
{
drawGrayButtonBox(hvg, x, y, w, h, enabled);
}
void beforeFirstPeriod( char *str )
{
char *t = rindex( str, '.' );
if( t == NULL )
return;
else
str[strlen(str) - strlen(t)] = '\0';
}
static void drawBases(struct hvGfx *hvg, int x, int y, int width, int height,
Color color, MgFont *font, boolean complementSeq,
struct dnaSeq *thisSeq)
/* Draw evenly spaced bases. */
{
struct dnaSeq *seq;
if (thisSeq == NULL)
seq = hDnaFromSeq(database, chromName, winStart, winEnd, dnaUpper);
else
seq = thisSeq;
if (complementSeq)
complement(seq->dna, seq->size);
spreadBasesString(hvg, x, y, width, height, color, font,
seq->dna, seq->size, FALSE);
if (thisSeq == NULL)
freeDnaSeq(&seq);
}
void drawComplementArrow( struct hvGfx *hvg, int x, int y,
int width, int height, MgFont *font)
/* Draw arrow and create clickbox for complementing ruler bases */
{
boolean baseCmpl = cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE);
// reverse arrow when base complement doesn't match display
char *text = (baseCmpl == revCmplDisp) ? "--->" : "<---";
hvGfxTextRight(hvg, x, y, width, height, MG_BLACK, font, text);
mapBoxToggleComplement(hvg, x, y, width, height, NULL, chromName, winStart, winEnd,
"complement bases");
}
struct track *chromIdeoTrack(struct track *trackList)
/* Find chromosome ideogram track */
{
struct track *track;
for(track = trackList; track != NULL; track = track->next)
{
if(sameString(track->track, "cytoBandIdeo"))
{
if (hTableExists(database, track->table))
return track;
else
return NULL;
}
}
return NULL;
}
void removeTrackFromGroup(struct track *track)
/* Remove track from group it is part of. */
{
struct trackRef *tr = NULL;
for(tr = track->group->trackList; tr != NULL; tr = tr->next)
{
if(tr->track == track)
{
slRemoveEl(&track->group->trackList, tr);
break;
}
}
}
void fillInStartEndBands(struct track *ideoTrack, char *startBand, char *endBand, int buffSize)
/* Loop through the bands and fill in the one that the current window starts
on and ends on. */
{
struct cytoBand *cb = NULL, *cbList = ideoTrack->items;
for(cb = cbList; cb != NULL; cb = cb->next)
{
/* If the start or end is encompassed by this band fill
it in. */
if(winStart >= cb->chromStart &&
winStart <= cb->chromEnd)
{
safef(startBand, buffSize, "%s", cb->name);
}
/* End is > rather than >= due to odditiy in the
cytoband track where the starts and ends of two
bands overlaps by one. */
if(winEnd > cb->chromStart &&
winEnd <= cb->chromEnd)
{
safef(endBand, buffSize, "%s", cb->name);
}
}
}
void makeChromIdeoImage(struct track **pTrackList, char *psOutput,
struct tempName *ideoTn)
/* Make an ideogram image of the chromsome and our position in it. If the
* ideoTn parameter is not NULL, it is filled in if the ideogram is created. */
{
struct track *ideoTrack = NULL;
MgFont *font = tl.font;
char *mapName = "ideoMap";
struct hvGfx *hvg;
boolean doIdeo = TRUE;
boolean ideogramAvail = FALSE;
int ideoWidth = round(.8 *tl.picWidth);
int ideoHeight = 0;
int textWidth = 0;
struct tempName gifTn;
if (ideoTn == NULL)
ideoTn = &gifTn; // not returning value
ideoTrack = chromIdeoTrack(*pTrackList);
/* If no ideogram don't draw. */
if(ideoTrack == NULL)
doIdeo = FALSE;
else if(trackImgOnly && !ideogramToo)
{
doIdeo = FALSE;
}
else
{
ideogramAvail = TRUE;
/* Remove the track from the group and track list. */
removeTrackFromGroup(ideoTrack);
slRemoveEl(pTrackList, ideoTrack);
/* Fix for hide all button hiding the ideogram as well. */
if(withIdeogram && ideoTrack->items == NULL)
{
ideoTrack->visibility = tvDense;
ideoTrack->loadItems(ideoTrack);
}
limitVisibility(ideoTrack);
/* If hidden don't draw. */
if(ideoTrack->limitedVis == tvHide || !withIdeogram)
doIdeo = FALSE;
}
if(doIdeo)
{
char startBand[16];
char endBand[16];
char title[32];
startBand[0] = endBand[0] = '\0';
fillInStartEndBands(ideoTrack, startBand, endBand, sizeof(startBand));
/* Start up client side map. */
if (!psOutput)
hPrintf("\n");
}
hPrintf("
", hgcNameAndSettings(),
winStart, chromName, winStart, winEnd, database, uiVars->string, "DNA");
}
if (!psOutput)
{
/* disable Convert function for CGB servers for the time being */
if (!hIsCgbServer())
if (liftOverChainForDb(database) != NULL)
{
hPrintf("
", "Ensembl");
}
else if (ensVersionString[0])
{
char *archive = NULL;
if (ensDateReference[0] && differentWord("current", ensDateReference))
archive = cloneString(ensDateReference);
/* Can we perhaps map from a UCSC random chrom to an Ensembl contig ? */
if (sameWord(database,"oryCun2") || isUnknownChrom(database, chromName))
{
// which table to check
char *ctgPos = "ctgPos";
if (sameWord(database,"oryCun2"))
ctgPos = "ctgPos2";
if (sameWord(database,"fr2"))
fr2ScaffoldEnsemblLink(archive);
else if (hTableExists(database, ctgPos))
/* see if we are entirely within a single contig */
{
struct sqlConnection *conn = hAllocConn(database);
struct sqlResult *sr = NULL;
char **row = NULL;
char query[256];
safef(query, sizeof(query),
"select * from %s where chrom = '%s' and chromStart<%u and chromEnd>%u",
ctgPos, chromName, winEnd, winStart);
sr = sqlGetResult(conn, query);
int itemCount = 0;
struct ctgPos *ctgItem = NULL;
while ((row = sqlNextRow(sr)) != NULL)
{
ctgPosFree(&ctgItem); // if there is a second one
ctgItem = ctgPosLoad(row);
++itemCount;
if (itemCount > 1)
break;
}
sqlFreeResult(&sr);
hFreeConn(&conn);
if (1 == itemCount)
{ // verify *entirely* within single contig
if ((winEnd <= ctgItem->chromEnd) &&
(winStart >= ctgItem->chromStart))
{
int ctgStart = winStart - ctgItem->chromStart;
int ctgEnd = ctgStart + winEnd - winStart;
hPrintf("
\n");
}
static void setSuperTrackHasVisibleMembers(struct trackDb *tdb)
/* Determine if any member tracks are visible -- currently
* recording this in the parent's visibility setting */
{
tdb->visibility = tvDense;
}
boolean superTrackHasVisibleMembers(struct trackDb *tdb)
/* Determine if any member tracks are visible -- currently
* recording this in the parent's visibility setting */
{
if (!tdbIsSuper(tdb))
return FALSE;
return (tdb->visibility != tvHide);
}
static void groupTracks(struct trackHub *hubList, struct track **pTrackList,
struct group **pGroupList, int vis)
/* Make up groups and assign tracks to groups.
* If vis is -1, restore default groups to tracks. */
{
struct group *unknown = NULL;
struct group *group, *list = NULL;
struct hash *hash = newHash(8);
struct track *track;
struct trackRef *tr;
struct grp* grps = hLoadGrps(database);
struct grp *grp;
float maxPriority = 0;
/* build group objects from database. */
for (grp = grps; grp != NULL; grp = grp->next)
{
/* deal with group reordering */
float priority = grp->priority;
if (priority > maxPriority) maxPriority = priority;
if (withPriorityOverride)
{
char cartVar[512];
safef(cartVar, sizeof(cartVar), "%s.priority",grp->name);
if (vis != -1)
priority = (float)cartUsualDouble(cart, cartVar, grp->priority);
if (priority == grp->priority)
cartRemove(cart, cartVar);
}
/* create group object; add to list and hash */
AllocVar(group);
group->name = cloneString(grp->name);
group->label = cloneString(grp->label);
group->defaultPriority = grp->priority;
group->priority = priority;
group->defaultIsClosed = grp->defaultIsClosed;
slAddHead(&list, group);
hashAdd(hash, grp->name, group);
}
grpFreeList(&grps);
/* build group objects from hub */
{
struct trackHub *hub;
for (hub = hubList; hub != NULL; hub = hub->next)
{
AllocVar(group);
group->name = cloneString(hub->name);
group->label = cloneString(hub->shortLabel);
group->defaultPriority = group->priority = maxPriority;
maxPriority += 1;
slAddHead(&list, group);
hashAdd(hash, group->name, group);
}
}
/* Loop through tracks and fill in their groups.
* If necessary make up an unknown group. */
for (track = *pTrackList; track != NULL; track = track->next)
{
/* handle track reordering feature -- change group assigned to track */
if (withPriorityOverride)
{
char *groupName = NULL;
char cartVar[256];
/* belt and suspenders -- accomodate inconsistent track/trackDb
* creation. Note -- with code cleanup, these default variables
* could be retired, and the tdb versions used as defaults */
if (!track->defaultGroupName)
{
if (track->tdb && track->tdb->grp)
track->defaultGroupName = cloneString(track->tdb->grp);
else
track->defaultGroupName = cloneString("other");
}
if (tdbIsSuperTrackChild(track->tdb))
{
assert(track->tdb->parentName != NULL);
/* supertrack member must be in same group as its super */
/* determine supertrack group */
safef(cartVar, sizeof(cartVar), "%s.group",track->tdb->parentName);
groupName = cloneString( //1
cartUsualString(cart, cartVar, track->tdb->parent->grp));
track->tdb->parent->grp = cloneString(groupName); //2
}
else
{
/* get group */
safef(cartVar, sizeof(cartVar), "%s.group",track->track);
groupName = cloneString( //1
cartUsualString(cart, cartVar, track->defaultGroupName));
}
if (vis == -1)
groupName = track->defaultGroupName; //0
track->groupName = cloneString(groupName); // wasting a few clones! //3
if (sameString(groupName, track->defaultGroupName))
cartRemove(cart, cartVar);
/* get priority */
safef(cartVar, sizeof(cartVar), "%s.priority",track->track);
float priority = (float)cartUsualDouble(cart, cartVar,
track->defaultPriority);
/* remove cart variables that are the same as the trackDb settings */
/* UGLY - add me back when tdb->priority is no longer pre-clobbered by cart var value
if (priority == track->defaultPriority)
cartRemove(cart, cartVar);
*/
track->priority = priority;
}
/* assign group object to track */
if (track->groupName == NULL)
group = NULL;
else
group = hashFindVal(hash, track->groupName);
if (group == NULL)
{
if (unknown == NULL)
{
AllocVar(unknown);
unknown->name = cloneString("other");
unknown->label = cloneString("other");
unknown->priority = 1000000;
slAddHead(&list, unknown);
}
group = unknown;
}
track->group = group;
}
/* Sort tracks by combined group/track priority, and
* then add references to track to group. */
slSort(pTrackList, tgCmpPriority);
for (track = *pTrackList; track != NULL; track = track->next)
{
AllocVar(tr);
tr->track = track;
slAddHead(&track->group->trackList, tr);
}
/* Straighten things out, clean up, and go home. */
for (group = list; group != NULL; group = group->next)
slReverse(&group->trackList);
slSort(&list, gCmpPriority);
hashFree(&hash);
*pGroupList = list;
}
void groupTrackListAddSuper(struct cart *cart, struct group *group)
/* Construct a new track list that includes supertracks, sort by priority,
* and determine if supertracks have visible members.
* Replace the group track list with this new list.
* Shared by hgTracks and configure page to expand track list,
* in contexts where no track display functions (which don't understand
* supertracks) are invoked. */
{
struct trackRef *newList = NULL, *tr, *ref;
struct hash *superHash = hashNew(8);
if (!group || !group->trackList)
return;
for (tr = group->trackList; tr != NULL; tr = tr->next)
{
struct track *track = tr->track;
AllocVar(ref);
ref->track = track;
slAddHead(&newList, ref);
if (tdbIsSuperTrackChild(track->tdb))
{
assert(track->tdb->parentName != NULL);
if (hTvFromString(cartUsualString(cart, track->track,
hStringFromTv(track->tdb->visibility))) != tvHide)
setSuperTrackHasVisibleMembers(track->tdb->parent);
assert(track->parent == NULL);
track->parent = hashFindVal(superHash, track->tdb->parentName);
if (track->parent)
continue;
/* create track and reference for the supertrack */
struct track *superTrack = track->parent = trackFromTrackDb(track->tdb->parent);
track->parent = superTrack;
if (trackHash != NULL)
hashAddUnique(trackHash,superTrack->track,superTrack);
superTrack->hasUi = TRUE;
superTrack->group = group;
superTrack->groupName = cloneString(group->name);
superTrack->defaultGroupName = cloneString(group->name);
/* handle track reordering */
char cartVar[256];
safef(cartVar, sizeof(cartVar), "%s.priority",track->tdb->parentName);
float priority = (float)cartUsualDouble(cart, cartVar,
track->tdb->parent->priority);
/* remove cart variables that are the same as the trackDb settings */
if (priority == track->tdb->parent->priority)
cartRemove(cart, cartVar);
superTrack->priority = priority;
AllocVar(ref);
ref->track = superTrack;
slAddHead(&newList, ref);
hashAdd(superHash, track->tdb->parentName, superTrack);
}
}
slSort(&newList, trackRefCmpPriority);
hashFree(&superHash);
/* we could free the old track list here, but it's a trivial amount of mem */
group->trackList = newList;
}
void topButton(char *var, char *label)
/* create a 3 or 4-char wide button for top line of display.
* 3 chars wide for odd-length labels, 4 for even length.
* Pad with spaces so label is centered */
{
char paddedLabel[5] = " ";
int len = strlen(label);
if (len > 4)
{
/* truncate */
/* or maybe errabort ? */
label[3] = 0;
len = 4;
}
if (len % 2 != 0)
paddedLabel[3] = 0;
if (len == strlen(paddedLabel))
strcpy(paddedLabel, label);
else
{
int i;
for (i=0; itdb))
{
assert(track->tdb->parent != NULL);
if (sameString("hide", cartUsualString(cart, track->tdb->parent->track,
track->tdb->parent->isShow ? "show" : "hide")))
track->visibility = tvHide;
}
}
struct track *rFindTrackWithTable(char *tableName, struct track *trackList)
/* Recursively search through track list looking for first one that matches table. */
{
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
if (sameString(tableName, track->table))
return track;
struct track *subTrack = rFindTrackWithTable(tableName, track->subtracks);
if (subTrack != NULL)
return subTrack;
}
return NULL;
}
static void setSearchedTrackToPackOrFull(struct track *trackList)
/* Open track associated with search position if any. Also open its parents
* if any. At the moment parents include composites but not supertracks. */
{
if (NULL != hgp && NULL != hgp->tableList && NULL != hgp->tableList->name)
{
char *tableName = hgp->tableList->name;
struct track *matchTrack = rFindTrackWithTable(tableName, trackList);
if (matchTrack != NULL)
{
struct track *track;
for (track = matchTrack; track != NULL; track = track->parent)
cartSetString(cart, track->track, hCarefulTrackOpenVis(database, track->track));
}
}
}
struct track *getTrackList( struct group **pGroupList, int vis)
/* Return list of all tracks, organizing by groups.
* If vis is -1, restore default groups to tracks.
* Shared by hgTracks and configure page. */
{
struct track *track, *trackList = NULL;
registerTrackHandlers();
/* Load regular tracks, blatted tracks, and custom tracks.
* Best to load custom last. */
loadFromTrackDb(&trackList);
if (pcrResultParseCart(database, cart, NULL, NULL, NULL))
slSafeAddHead(&trackList, pcrResultTg());
if (userSeqString != NULL) slSafeAddHead(&trackList, userPslTg());
slSafeAddHead(&trackList, oligoMatchTg());
if (restrictionEnzymesOk())
{
slSafeAddHead(&trackList, cuttersTg());
}
if (wikiTrackEnabled(database, NULL))
{
addWikiTrack(&trackList);
struct sqlConnection *conn = wikiConnect();
if (sqlTableExists(conn, "variome"))
addVariomeWikiTrack(&trackList);
wikiDisconnect(&conn);
}
loadTrackHubs(&trackList, &hubList);
slReverse(&hubList);
loadCustomTracks(&trackList);
groupTracks(hubList, &trackList, pGroupList, vis);
setSearchedTrackToPackOrFull(trackList);
if (cgiOptionalString( "hideTracks"))
changeTrackVis(groupList, NULL, tvHide);
/* Get visibility values if any from ui. */
for (track = trackList; track != NULL; track = track->next)
{
char *s = cartOptionalString(cart, track->track);
if (cgiOptionalString("hideTracks"))
{
s = cgiOptionalString(track->track);
if (s != NULL && (hTvFromString(s) != track->tdb->visibility))
{
cartSetString(cart, track->track, s);
}
}
if (s != NULL && !track->limitedVisSet)
track->visibility = hTvFromString(s);
if (tdbIsCompositeChild(track->tdb))
track->visibility = tdbVisLimitedByAncestry(cart, track->tdb, FALSE);
else if (tdbIsComposite(track->tdb) && track->visibility != tvHide)
{
struct trackDb *parent = track->tdb->parent;
char *parentShow = NULL;
if (parent)
parentShow = cartUsualString(cart, parent->track,
parent->isShow ? "show" : "hide");
if (!parent || sameString(parentShow, "show"))
compositeTrackVis(track);
}
}
if (measureTiming)
uglyTime("getTrackList");
return trackList;
}
void doNextPrevItem(boolean goNext, char *trackName)
/* In case a next item arrow was clicked on a track, change */
/* position (i.e. winStart, winEnd, etc.) based on what track it was */
{
struct track *track = trackFindByName(trackList, trackName);
if ((track != NULL) && (track->nextPrevItem != NULL))
track->nextPrevItem(track, goNext);
}
char *collapseGroupVar(char *name)
/* Construct cart variable name for collapsing group */
{
static char varName[256];
safef(varName, sizeof(varName),
"%s%s_%s_%s", "hgt", "group", name, "close");
return (cloneString(varName));
}
boolean isCollapsedGroup(struct group *grp)
/* Determine if group is collapsed */
{
return cartUsualInt(cart, collapseGroupVar(grp->name), grp->defaultIsClosed);
}
void collapseGroupGoodies(boolean isOpen, boolean wantSmallImage,
char **img, char **indicator, char **otherState)
/* Get image, char representation of image, and 'otherState' (1 or 0)
* for a group, based on whether it is collapsed, and whether we want
* larger or smaller image for collapse box */
{
if (otherState)
*otherState = (isOpen ? "1" : "0");
if (indicator)
*indicator = (isOpen ? "-" : "+");
if (img)
{
if (wantSmallImage)
*img = (isOpen ? "../images/remove_sm.gif" : "../images/add_sm.gif");
else
*img = (isOpen ? "../images/remove.gif" : "../images/add.gif");
}
}
void collapseGroup(char *name, boolean doCollapse)
/* Set cart variable to cause group to collapse */
{
cartSetBoolean(cart, collapseGroupVar(name), doCollapse);
}
void myControlGridStartCell(struct controlGrid *cg, boolean isOpen, char *id)
/* Start a new cell in control grid; support Javascript open/collapsing by including id's in tr's.
id is used as id prefix (a counter is added to make id's unique). */
{
static int counter = 1;
if (cg->columnIx == cg->columns)
controlGridEndRow(cg);
if (!cg->rowOpen)
{
#if 0
/* This is unnecessary, b/c we can just use a blank display attribute to show the element rather
than figuring out what the browser specific string is to turn on display of the tr;
however, we may want to put in browser specific strings in the future, so I'm leaving this
code in as a reference. */
char *ua = getenv("HTTP_USER_AGENT");
char *display = ua && stringIn("MSIE", ua) ? "block" : "table-row";
#endif
// use counter to ensure unique tr id's (prefix is used to find tr's in javascript).
printf("
");
}
static void pruneRedundantCartVis(struct track *trackList)
/* When the config page or track form has been submitted, there usually
* are many track visibility cart variables that have not been changed
* from the default. To keep down cart bloat, prune those out before we
* save the cart. changeTrackVis does this too, but this is for the
* more common case where track visibilities are tweaked. */
{
struct track *track;
if (measureTiming)
uglyTime("Done with trackForm");
for (track = trackList; track != NULL; track = track->next)
{
char *cartVis = cartOptionalString(cart, track->track);
if (cartVis != NULL && hTvFromString(cartVis) == track->tdb->visibility)
cartRemove(cart, track->track);
}
if (measureTiming)
{
uglyTime("Pruned redundant vis from cart");
}
}
static int getMaxWindowToDraw(struct trackDb *tdb)
/* If trackDb setting maxWindowToDraw exists and is a sensible size, return it, else 0. */
{
if (tdb == NULL)
return 0;
char *maxWinToDraw = trackDbSettingClosestToHome(tdb, "maxWindowToDraw");
if (isNotEmpty(maxWinToDraw))
{
unsigned maxWTD = sqlUnsigned(maxWinToDraw);
if (maxWTD > 1)
return maxWTD;
}
return 0;
}
static void drawMaxWindowWarning(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg,
int xOff, int yOff, int width, MgFont *font, Color color,
enum trackVisibility vis)
/* This is a stub drawItems handler to be swapped in for the usual drawItems when the window
* size is larger than the threshold specified by trackDb setting maxWindowToDraw. */
{
int maxWinToDraw = getMaxWindowToDraw(tg->tdb);
char commafied[256];
sprintLongWithCommas(commafied, maxWinToDraw);
char message[512];
safef(message, sizeof(message), "zoom in to <= %s bases to view items", commafied);
Color yellow = hvGfxFindRgb(hvg, &undefinedYellowColor);
hvGfxBox(hvg, xOff, yOff, width, tg->heightPer, yellow);
hvGfxTextCentered(hvg, xOff, yOff, width, tg->heightPer, MG_BLACK, font, message);
}
static void checkMaxWindowToDraw(struct track *tg)
/* If (winEnd - winStart) > trackDb setting maxWindowToDraw, force track to a dense line
* that will ask the user to zoom in closer to see track items and return TRUE so caller
* can skip loading items. */
{
int maxWinToDraw = getMaxWindowToDraw(tg->tdb);
if (tdbIsComposite(tg->tdb))
{
struct track *subtrack;
for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
if (!isSubtrackVisible(subtrack))
continue;
maxWinToDraw = getMaxWindowToDraw(subtrack->tdb);
if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw)
{
subtrack->loadItems = dontLoadItems;
subtrack->drawItems = drawMaxWindowWarning;
subtrack->limitedVis = tvDense;
subtrack->limitedVisSet = TRUE;
}
}
}
else if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw)
{
tg->loadItems = dontLoadItems;
tg->drawItems = drawMaxWindowWarning;
tg->limitedVis = tvDense;
tg->limitedVisSet = TRUE;
}
}
void printTrackInitJavascript(struct track *trackList)
{
hPrintf("\n", hgtJsCommand, hgtJsCommand);
hPrintf("\n");
}
void jsCommandDispatch(char *command, struct track *trackList)
/* Dispatch a command sent to us from some javaScript event.
* This gets executed after the track list is built, but before
* the track->loadItems methods are called. */
{
if (startsWithWord("makeItems", command))
makeItemsJsCommand(command, trackList, trackHash);
else
warn("Unrecognized jsCommand %s", command);
}
void parentChildCartCleanup(struct track *trackList,struct cart *newCart,struct hash *oldVars)
/* When composite/view settings changes, remove subtrack specific vis
When superTrackChild is found and selected, shape superTrack to match. */
{
struct track *track = trackList;
for (;track != NULL; track = track->next)
{
boolean shapedByubtrackOverride = FALSE;
boolean cleanedByContainerSettings = FALSE;
// Top-down 'cleanup' MUST GO BEFORE bottom up reshaping.
cleanedByContainerSettings = cartTdbTreeCleanupOverrides(track->tdb,newCart,oldVars);
if (tdbIsContainer(track->tdb))
{
shapedByubtrackOverride = cartTdbTreeReshapeIfNeeded(cart,track->tdb);
if(shapedByubtrackOverride)
track->visibility = tdbVisLimitedByAncestors(cart,track->tdb,TRUE,TRUE);
}
if ((shapedByubtrackOverride || cleanedByContainerSettings) && tdbIsSuperTrackChild(track->tdb)) // Either cleanup may require supertrack intervention
{ // Need to update track visibility
// Unfortunately, since supertracks are not in trackList, this occurs on superChildren,
// So now we need to find the supertrack and take changed cart values of its children
struct slRef *childRef;
for(childRef = track->tdb->parent->children;childRef != NULL;childRef = childRef->next)
{
struct trackDb * childTdb = childRef->val;
struct track *child = hashFindVal(trackHash, childTdb->track);
char *cartVis = cartOptionalString(cart,child->track);
if (cartVis)
child->visibility = hTvFromString(cartVis);
}
}
}
}
void doTrackForm(char *psOutput, struct tempName *ideoTn)
/* Make the tracks display form with the zoom/scroll buttons and the active
* image. If the ideoTn parameter is not NULL, it is filled in if the
* ideogram is created. */
{
struct group *group;
struct track *track;
char *freezeName = NULL;
boolean hideAll = cgiVarExists("hgt.hideAll");
boolean defaultTracks = cgiVarExists("hgt.reset");
boolean showedRuler = FALSE;
boolean showTrackControls = cartUsualBoolean(cart, "trackControlsOnMain", TRUE);
long thisTime = 0, lastTime = 0;
char *clearButtonJavascript;
basesPerPixel = ((float)winBaseCount) / ((float)insideWidth);
zoomedToBaseLevel = (winBaseCount <= insideWidth / tl.mWidth);
zoomedToCodonLevel = (ceil(winBaseCount/3) * tl.mWidth) <= insideWidth;
zoomedToCdsColorLevel = (winBaseCount <= insideWidth*3);
if (psOutput != NULL)
{
hPrintDisable();
hideControls = TRUE;
withNextItemArrows = FALSE;
withNextExonArrows = FALSE;
hgFindMatches = NULL;
}
/* Tell browser where to go when they click on image. */
hPrintf("