f20643ce0ffbd806e8e1b7baac473372f93d0edf
tdreszer
Fri Feb 11 09:47:49 2011 -0800
Fixed bug screwing up track search tab switching. It seems that ui.code.js does not play well with the tabs widget.
diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 34a4df2..90f1e13 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -1,5695 +1,5698 @@
/* 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"
#include "errCatch.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, 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("