77dc06c4bdc5e10f5d2705a51300ec028a7d785f
tdreszer
Fri Oct 1 10:51:48 2010 -0700
FindTracks now finds superTracks (Advanced only) and hgTrackUi now does superTrack reshaping when children have vis changed
diff --git src/hg/hgTracks/hgTracks.c.old src/hg/hgTracks/hgTracks.c.old
new file mode 100644
index 0000000..f68887d
--- /dev/null
+++ src/hg/hgTracks/hgTracks.c.old
@@ -0,0 +1,5646 @@
+/* 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 "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 group *groupList = NULL; /* List of all tracks. */
+char *browserName; /* Test or public browser */
+char *organization; /* UCSC */
+
+
+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;
+ }
+
+ #ifdef SUBTRACKS_HAVE_VIS
+ // 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;
+ }
+ }
+ #endif///def SUBTRACKS_HAVE_VIS
+ }
+ }
+ }
+ }
+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");
+}
+
+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). */
+{
+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", mapName);
+ /* Draw the ideogram. */
+ ideoHeight = gfxBorder + ideoTrack->height;
+ if (psOutput)
+ {
+ trashDirFile(ideoTn, "hgtIdeo", "hgtIdeo", ".ps");
+ hvg = hvGfxOpenPostScript(ideoWidth, ideoHeight, ideoTn->forCgi);
+ }
+ else
+ {
+#ifdef USE_PNG
+ trashDirFile(ideoTn, "hgtIdeo", "hgtIdeo", ".png");
+ hvg = hvGfxOpenPng(ideoWidth, ideoHeight, ideoTn->forCgi, FALSE);
+#else
+ trashDirFile(ideoTn, "hgtIdeo", "hgtIdeo", ".gif");
+ hvg = hvGfxOpenGif(ideoWidth, ideoHeight, ideoTn->forCgi, FALSE);
+#endif
+ }
+ hvg->rc = revCmplDisp;
+ initColors(hvg);
+ ideoTrack->ixColor = hvGfxFindRgb(hvg, &ideoTrack->color);
+ ideoTrack->ixAltColor = hvGfxFindRgb(hvg, &ideoTrack->altColor);
+ hvGfxSetClip(hvg, 0, gfxBorder, ideoWidth, ideoTrack->height);
+ if(sameString(startBand, endBand))
+ safef(title, sizeof(title), "%s (%s)", chromName, startBand);
+ else
+ safef(title, sizeof(title), "%s (%s-%s)", chromName, startBand, endBand);
+ textWidth = mgFontStringWidth(font, title);
+ hvGfxTextCentered(hvg, 2, gfxBorder, textWidth, ideoTrack->height, MG_BLACK, font, title);
+ ideoTrack->drawItems(ideoTrack, winStart, winEnd, hvg, textWidth+4, gfxBorder, ideoWidth-textWidth-4,
+ font, ideoTrack->ixColor, ideoTrack->limitedVis);
+ hvGfxUnclip(hvg);
+ /* Save out picture and tell html file about it. */
+ hvGfxClose(&hvg);
+ /* Finish map. */
+ if (!psOutput)
+ hPrintf(" \n");
+ }
+hPrintf("
");
+if (doIdeo && !psOutput)
+ {
+ hPrintf(" ");
+ hPrintf(" ",
+ ideoTn->forHtml, ideoWidth, ideoHeight, mapName);
+ hPrintf(" ");
+ hPrintf("
\n");
+ }
+else
+ hPrintf(" \n");
+if(ideoTrack != NULL)
+ {
+ ideoTrack->limitedVisSet = TRUE;
+ ideoTrack->limitedVis = tvHide; /* Don't draw in main gif. */
+ }
+}
+
+char *pcrResultMapItemName(struct track *tg, void *item)
+/* Stitch accession and display name back together (if necessary). */
+{
+struct linkedFeatures *lf = item;
+return pcrResultItemAccName(lf->name, lf->extra);
+}
+
+void pcrResultLoad(struct track *tg)
+/* Load locations of primer matches into linkedFeatures items. */
+{
+char *pslFileName, *primerFileName;
+struct targetDb *target;
+if (! pcrResultParseCart(database, cart, &pslFileName, &primerFileName, &target))
+ return;
+
+/* Don't free psl -- used in drawing phase by baseColor code. */
+struct psl *pslList = pslLoadAll(pslFileName), *psl;
+struct linkedFeatures *itemList = NULL;
+if (target != NULL)
+ {
+ int rowOffset = hOffsetPastBin(database, chromName, target->pslTable);
+ struct sqlConnection *conn = hAllocConn(database);
+ struct sqlResult *sr;
+ char **row;
+ char query[2048];
+ struct psl *tpsl;
+ for (tpsl = pslList; tpsl != NULL; tpsl = tpsl->next)
+ {
+ char *itemAcc = pcrResultItemAccession(tpsl->tName);
+ char *itemName = pcrResultItemName(tpsl->tName);
+ /* Query target->pslTable to get target-to-genomic mapping: */
+ safef(query, sizeof(query), "select * from %s where qName = '%s'",
+ target->pslTable, itemAcc);
+ sr = sqlGetResult(conn, query);
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ struct psl *gpsl = pslLoad(row+rowOffset);
+ if (sameString(gpsl->tName, chromName) && gpsl->tStart < winEnd && gpsl->tEnd > winStart)
+ {
+ struct psl *trimmed = pslTrimToQueryRange(gpsl, tpsl->tStart,
+ tpsl->tEnd);
+ struct linkedFeatures *lf;
+ char *targetStyle = cartUsualString(cart,
+ PCR_RESULT_TARGET_STYLE, PCR_RESULT_TARGET_STYLE_DEFAULT);
+ if (sameString(targetStyle, PCR_RESULT_TARGET_STYLE_TALL))
+ {
+ lf = lfFromPslx(gpsl, 1, FALSE, FALSE, tg);
+ lf->tallStart = trimmed->tStart;
+ lf->tallEnd = trimmed->tEnd;
+ }
+ else
+ {
+ lf = lfFromPslx(trimmed, 1, FALSE, FALSE, tg);
+ }
+ lf->name = cloneString(itemAcc);
+ char extraInfo[512];
+ safef(extraInfo, sizeof(extraInfo), "%s|%d|%d",
+ (itemName ? itemName : ""), tpsl->tStart, tpsl->tEnd);
+ lf->extra = cloneString(extraInfo);
+ slAddHead(&itemList, lf);
+ }
+ }
+ }
+ hFreeConn(&conn);
+ }
+else
+ for (psl = pslList; psl != NULL; psl = psl->next)
+ if (sameString(psl->tName, chromName) && psl->tStart < winEnd && psl->tEnd > winStart)
+ {
+ struct linkedFeatures *lf =
+ lfFromPslx(psl, 1, FALSE, FALSE, tg);
+ lf->name = cloneString("");
+ lf->extra = cloneString("");
+ slAddHead(&itemList, lf);
+ }
+slSort(&itemList, linkedFeaturesCmp);
+tg->items = itemList;
+}
+
+char *pcrResultTrackItemName(struct track *tg, void *item)
+/* If lf->extra is non-empty, return it (display name for item).
+ * Otherwise default to item name. */
+{
+struct linkedFeatures *lf = item;
+char *extra = (char *)lf->extra;
+if (isNotEmpty(extra))
+ {
+ static char displayName[512];
+ safecpy(displayName, sizeof(displayName), extra);
+ char *ptr = strchr(displayName, '|');
+ if (ptr != NULL)
+ *ptr = '\0';
+ if (isNotEmpty(displayName))
+ return displayName;
+ }
+return lf->name;
+}
+
+struct track *pcrResultTg()
+/* Make track of hgPcr results (alignments of user's submitted primers). */
+{
+struct trackDb *tdb = pcrResultFakeTdb();
+struct track *tg = trackFromTrackDb(tdb);
+tg->loadItems = pcrResultLoad;
+tg->itemName = pcrResultTrackItemName;
+tg->mapItemName = pcrResultMapItemName;
+tg->exonArrows = TRUE;
+tg->hasUi = TRUE;
+return tg;
+}
+
+struct track *linkedFeaturesTg()
+/* Return generic track for linked features. */
+{
+struct track *tg = trackNew();
+linkedFeaturesMethods(tg);
+tg->colorShades = shadesOfGray;
+return tg;
+}
+
+void setTgDarkLightColors(struct track *tg, int r, int g, int b)
+/* Set track color to r,g,b. Set altColor to a lighter version
+ * of the same. */
+{
+tg->colorShades = NULL;
+tg->color.r = r;
+tg->color.g = g;
+tg->color.b = b;
+tg->altColor.r = (r+255)/2;
+tg->altColor.g = (g+255)/2;
+tg->altColor.b = (b+255)/2;
+}
+
+void parseSs(char *ss, char **retPsl, char **retFa)
+/* Parse out ss variable into components. */
+{
+static char buf[1024];
+char *words[2];
+int wordCount;
+
+safecpy(buf, sizeof(buf), ss);
+wordCount = chopLine(buf, words);
+if (wordCount < 2)
+ errAbort("Badly formated ss variable");
+*retPsl = words[0];
+*retFa = words[1];
+}
+
+boolean ssFilesExist(char *ss)
+/* Return TRUE if both files in ss exist. */
+{
+char *faFileName, *pslFileName;
+parseSs(ss, &pslFileName, &faFileName);
+return fileExists(pslFileName) && fileExists(faFileName);
+}
+
+void loadUserPsl(struct track *tg)
+/* Load up hgBlat results from table into track items. */
+{
+char *ss = userSeqString;
+char buf2[3*512];
+char *faFileName, *pslFileName;
+struct lineFile *f;
+struct psl *psl;
+struct linkedFeatures *lfList = NULL, *lf;
+enum gfType qt, tt;
+int sizeMul = 1;
+
+parseSs(ss, &pslFileName, &faFileName);
+pslxFileOpen(pslFileName, &qt, &tt, &f);
+if (qt == gftProt)
+ {
+ setTgDarkLightColors(tg, 0, 80, 150);
+ tg->colorShades = NULL;
+ sizeMul = 3;
+ }
+tg->itemName = linkedFeaturesName;
+while ((psl = pslNext(f)) != NULL)
+ {
+ if (sameString(psl->tName, chromName) && psl->tStart < winEnd && psl->tEnd > winStart)
+ {
+ lf = lfFromPslx(psl, sizeMul, TRUE, FALSE, tg);
+ sprintf(buf2, "%s %s", ss, psl->qName);
+ lf->extra = cloneString(buf2);
+ slAddHead(&lfList, lf);
+ /* Don't free psl -- used in drawing phase by baseColor code. */
+ }
+ else
+ pslFree(&psl);
+ }
+slSort(&lfList, linkedFeaturesCmpStart);
+lineFileClose(&f);
+tg->items = lfList;
+}
+
+static void addUserSeqBaseAndIndelSettings(struct trackDb *tdb)
+/* If user sequence is a dna or rna alignment, add settings to enable
+ * base-level differences and indel display. */
+{
+enum gfType qt, tt;
+struct lineFile *lf;
+char *faFileName, *pslFileName;
+parseSs(userSeqString, &pslFileName, &faFileName);
+pslxFileOpen(pslFileName, &qt, &tt, &lf);
+lineFileClose(&lf);
+if (qt != gftProt)
+ {
+ if (tdb->settingsHash == NULL)
+ tdb->settingsHash = hashNew(0);
+ hashAdd(tdb->settingsHash, BASE_COLOR_DEFAULT, cloneString("diffBases"));
+ hashAdd(tdb->settingsHash, BASE_COLOR_USE_SEQUENCE, cloneString("ss"));
+ hashAdd(tdb->settingsHash, SHOW_DIFF_BASES_ALL_SCALES, cloneString("."));
+ hashAdd(tdb->settingsHash, INDEL_DOUBLE_INSERT, cloneString("on"));
+ hashAdd(tdb->settingsHash, INDEL_QUERY_INSERT, cloneString("on"));
+ hashAdd(tdb->settingsHash, INDEL_POLY_A, cloneString("on"));
+ }
+}
+
+struct track *userPslTg()
+/* Make track of user pasted sequence. */
+{
+struct track *tg = linkedFeaturesTg();
+struct trackDb *tdb;
+tg->track = "hgUserPsl";
+tg->table = tg->track;
+tg->canPack = TRUE;
+tg->visibility = tvPack;
+tg->longLabel = "Your Sequence from Blat Search";
+tg->shortLabel = "Blat Sequence";
+tg->loadItems = loadUserPsl;
+tg->mapItemName = lfMapNameFromExtra;
+tg->priority = 100;
+tg->defaultPriority = tg->priority;
+tg->groupName = "map";
+tg->defaultGroupName = cloneString(tg->groupName);
+tg->exonArrows = TRUE;
+
+/* better to create the tdb first, then use trackFromTrackDb */
+AllocVar(tdb);
+tdb->track = cloneString(tg->track);
+tdb->table = cloneString(tg->table);
+tdb->visibility = tg->visibility;
+tdb->shortLabel = cloneString(tg->shortLabel);
+tdb->longLabel = cloneString(tg->longLabel);
+tdb->grp = cloneString(tg->groupName);
+tdb->priority = tg->priority;
+tdb->type = cloneString("psl");
+trackDbPolish(tdb);
+addUserSeqBaseAndIndelSettings(tdb);
+tg->tdb = tdb;
+return tg;
+}
+
+char *oligoMatchSeq()
+/* Return sequence for oligo matching. */
+{
+char *s = cartOptionalString(cart, oligoMatchVar);
+if (s != NULL)
+ {
+ int len;
+ tolowers(s);
+ dnaFilter(s, s);
+ len = strlen(s);
+ if (len < 2)
+ s = NULL;
+ }
+if (s == NULL)
+ s = cloneString(oligoMatchDefault);
+return s;
+}
+
+char *oligoMatchName(struct track *tg, void *item)
+/* Return name for oligo, which is just the base position. */
+{
+struct bed *bed = item;
+static char buf[22];
+buf[0] = bed->strand[0];
+sprintLongWithCommas(buf+1, bed->chromStart+1);
+return buf;
+}
+
+char *dnaInWindow()
+/* This returns the DNA in the window, all in lower case. */
+{
+static struct dnaSeq *seq = NULL;
+if (seq == NULL)
+ seq = hDnaFromSeq(database, chromName, winStart, winEnd, dnaLower);
+return seq->dna;
+}
+
+
+void oligoMatchLoad(struct track *tg)
+/* Create track of perfect matches to oligo on either strand. */
+{
+char *dna = dnaInWindow();
+char *fOligo = oligoMatchSeq();
+int oligoSize = strlen(fOligo);
+char *rOligo = cloneString(fOligo);
+char *rMatch = NULL, *fMatch = NULL;
+struct bed *bedList = NULL, *bed;
+char strand;
+int count = 0, maxCount = 1000000;
+
+if (oligoSize >= 2)
+ {
+ fMatch = stringIn(fOligo, dna);
+ reverseComplement(rOligo, oligoSize);
+ if (sameString(rOligo, fOligo))
+ rOligo = NULL;
+ else
+ rMatch = stringIn(rOligo, dna);
+ for (;;)
+ {
+ char *oneMatch = NULL;
+ if (rMatch == NULL)
+ {
+ if (fMatch == NULL)
+ break;
+ else
+ {
+ oneMatch = fMatch;
+ fMatch = stringIn(fOligo, fMatch+1);
+ strand = '+';
+ }
+ }
+ else if (fMatch == NULL)
+ {
+ oneMatch = rMatch;
+ rMatch = stringIn(rOligo, rMatch+1);
+ strand = '-';
+ }
+ else if (rMatch < fMatch)
+ {
+ oneMatch = rMatch;
+ rMatch = stringIn(rOligo, rMatch+1);
+ strand = '-';
+ }
+ else
+ {
+ oneMatch = fMatch;
+ fMatch = stringIn(fOligo, fMatch+1);
+ strand = '+';
+ }
+ if (count < maxCount)
+ {
+ ++count;
+ AllocVar(bed);
+ bed->chromStart = winStart + (oneMatch - dna);
+ bed->chromEnd = bed->chromStart + oligoSize;
+ bed->strand[0] = strand;
+ slAddHead(&bedList, bed);
+ }
+ else
+ break;
+ }
+ slReverse(&bedList);
+ if (count < maxCount)
+ tg->items = bedList;
+ else
+ warn("More than %d items in %s, suppressing display", maxCount, tg->shortLabel);
+ }
+}
+
+struct track *oligoMatchTg()
+/* Make track of perfect matches to oligomer. */
+{
+struct track *tg = trackNew();
+char *oligo = oligoMatchSeq();
+int oligoSize = strlen(oligo);
+char *medOligo = cloneString(oligo);
+static char longLabel[80];
+struct trackDb *tdb;
+
+/* Generate abbreviated strings. */
+if (oligoSize >= 30)
+ {
+ memset(medOligo + 30-3, '.', 3);
+ medOligo[30] = 0;
+ }
+touppers(medOligo);
+
+bedMethods(tg);
+tg->track = "oligoMatch";
+tg->table = tg->track;
+tg->canPack = TRUE;
+tg->visibility = tvHide;
+tg->hasUi = TRUE;
+tg->shortLabel = cloneString(OLIGO_MATCH_TRACK_LABEL);
+safef(longLabel, sizeof(longLabel),
+ "Perfect Matches to Short Sequence (%s)", medOligo);
+tg->longLabel = longLabel;
+tg->loadItems = oligoMatchLoad;
+tg->itemName = oligoMatchName;
+tg->mapItemName = oligoMatchName;
+tg->priority = 99;
+tg->defaultPriority = tg->priority;
+tg->groupName = "map";
+tg->defaultGroupName = cloneString(tg->groupName);
+
+AllocVar(tdb);
+tdb->track = cloneString(tg->track);
+tdb->table = cloneString(tg->table);
+tdb->visibility = tg->visibility;
+tdb->shortLabel = cloneString(tg->shortLabel);
+tdb->longLabel = cloneString(tg->longLabel);
+tdb->grp = cloneString(tg->groupName);
+tdb->priority = tg->priority;
+trackDbPolish(tdb);
+tg->tdb = tdb;
+return tg;
+}
+
+static int doLeftLabels(struct track *track, struct hvGfx *hvg, MgFont *font,
+ int y)
+/* Draw left labels. Return y coord. */
+{
+struct slList *prev = NULL;
+
+/* for sample tracks */
+double minRangeCutoff, maxRangeCutoff;
+double minRange, maxRange;
+double min0, max0;
+char minRangeStr[32];
+char maxRangeStr[32];
+
+int ymin, ymax;
+int start;
+int newy;
+char o4[256];
+char o5[256];
+struct slList *item;
+enum trackVisibility vis = track->limitedVis;
+enum trackVisibility savedVis = vis;
+Color labelColor = (track->labelColor ?
+ track->labelColor : track->ixColor);
+int fontHeight = mgFontLineHeight(font);
+int tHeight = trackPlusLabelHeight(track, fontHeight);
+if (vis == tvHide)
+ return y;
+
+/* if a track can do its own left labels, do them after drawItems */
+if (track->drawLeftLabels != NULL)
+ return y + tHeight;
+
+/* Wiggle tracks depend upon clipping. They are reporting
+ * totalHeight artifically high by 1 so this will leave a
+ * blank area one pixel high below the track.
+ */
+if (sameString("wig",track->tdb->type) || sameString("bedGraph",track->tdb->type))
+ hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight-1);
+else
+ hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight);
+
+minRange = 0.0;
+safef( o4, sizeof(o4),"%s.min.cutoff", track->track);
+safef( o5, sizeof(o5),"%s.max.cutoff", track->track);
+minRangeCutoff = max( atof(cartUsualString(cart,o4,"0.0"))-0.1,
+ track->minRange );
+maxRangeCutoff = min( atof(cartUsualString(cart,o5,"1000.0"))+0.1,
+ track->maxRange);
+if( sameString( track->table, "humMusL" ) ||
+ sameString( track->table, "musHumL" ) ||
+ sameString( track->table, "mm3Rn2L" ) ||
+ sameString( track->table, "hg15Mm3L" ) ||
+ sameString( track->table, "mm3Hg15L" ) ||
+ sameString( track->table, "regpotent" ) ||
+ sameString( track->table, "HMRConservation" ) )
+ {
+ int binCount = round(1.0/track->scaleRange);
+ minRange = whichSampleBin( minRangeCutoff, track->minRange, track->maxRange, binCount );
+ maxRange = whichSampleBin( maxRangeCutoff, track->minRange, track->maxRange ,binCount );
+ min0 = whichSampleNum( minRange, track->minRange,track->maxRange, binCount );
+ max0 = whichSampleNum( maxRange, track->minRange, track->maxRange, binCount );
+ sprintf( minRangeStr, " " );
+ sprintf( maxRangeStr, " " );
+ if( vis == tvFull && track->heightPer >= 74 )
+ {
+ samplePrintYAxisLabel( hvg, y+5, track, "1.0", min0, max0 );
+ samplePrintYAxisLabel( hvg, y+5, track, "2.0", min0, max0 );
+ samplePrintYAxisLabel( hvg, y+5, track, "3.0", min0, max0 );
+ samplePrintYAxisLabel( hvg, y+5, track, "4.0", min0, max0 );
+ samplePrintYAxisLabel( hvg, y+5, track, "5.0", min0, max0 );
+ samplePrintYAxisLabel( hvg, y+5, track, "6.0", min0, max0 );
+ }
+ }
+else
+ {
+ sprintf( minRangeStr, "%d", (int)round(minRangeCutoff));
+ sprintf( maxRangeStr, "%d", (int)round(maxRangeCutoff));
+ }
+/* special label handling for wigMaf type tracks -- they
+ display a left label in pack mode. To use the full mode
+ labeling, temporarily set visibility to full.
+ Restore savedVis later */
+if (startsWith("wigMaf", track->tdb->type) || startsWith("maf", track->tdb->type))
+ vis = tvFull;
+
+switch (vis)
+ {
+ case tvHide:
+ break; /* Do nothing; */
+ case tvPack:
+ case tvSquish:
+ y += tHeight;
+ break;
+ case tvFull:
+ if (isCenterLabelIncluded(track))
+ y += fontHeight;
+ start = 1;
+
+ if( track->subType == lfSubSample && track->items == NULL )
+ y += track->height;
+
+ for (item = track->items; item != NULL; item = item->next)
+ {
+ char *rootName;
+ char *name = track->itemName(track, item);
+ int itemHeight = track->itemHeight(track, item);
+ newy = y;
+
+ if (track->itemLabelColor != NULL)
+ labelColor = track->itemLabelColor(track, item, hvg);
+
+ /* Do some fancy stuff for sample tracks.
+ * Draw y-value limits for 'sample' tracks. */
+ if (track->subType == lfSubSample )
+ {
+ if( prev == NULL )
+ newy += itemHeight;
+ else
+ newy += sampleUpdateY(name,
+ track->itemName(track, prev), itemHeight);
+ if( newy == y )
+ continue;
+
+ if( track->heightPer > (3 * fontHeight ) )
+ {
+ ymax = y - (track->heightPer / 2) + (fontHeight / 2);
+ ymin = y + (track->heightPer / 2) - (fontHeight / 2);
+ hvGfxTextRight(hvg, leftLabelX, ymin, leftLabelWidth-1,
+ itemHeight, track->ixAltColor,
+ font, minRangeStr );
+ hvGfxTextRight(hvg, leftLabelX, ymax, leftLabelWidth-1,
+ itemHeight, track->ixAltColor,
+ font, maxRangeStr );
+ }
+ prev = item;
+
+ rootName = cloneString( name );
+ beforeFirstPeriod( rootName );
+ if( sameString( track->table, "humMusL" ) ||
+ sameString( track->table, "hg15Mm3L" ))
+ hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
+ itemHeight, track->ixColor, font, "Mouse Cons");
+ else if( sameString( track->table, "musHumL" ) ||
+ sameString( track->table, "mm3Hg15L"))
+ hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
+ itemHeight, track->ixColor, font, "Human Cons");
+ else if( sameString( track->table, "mm3Rn2L" ))
+ hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
+ itemHeight, track->ixColor, font, "Rat Cons");
+ else
+ hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
+ itemHeight, track->ixColor, font, rootName );
+ freeMem( rootName );
+ start = 0;
+ y = newy;
+ }
+ else
+ {
+ /* standard item labeling */
+ if (highlightItem(track, item))
+ {
+ int nameWidth = mgFontStringWidth(font, name);
+ int boxStart = leftLabelX + leftLabelWidth - 2 - nameWidth;
+ hvGfxBox(hvg, boxStart, y, nameWidth+1, itemHeight - 1,
+ labelColor);
+ hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth-1,
+ itemHeight, MG_WHITE, font, name);
+ }
+ else
+ hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
+ itemHeight, labelColor, font, name);
+ y += itemHeight;
+ }
+ }
+ break;
+ case tvDense:
+
+ if (isCenterLabelIncluded(track))
+ y += fontHeight;
+
+ /*draw y-value limits for 'sample' tracks.
+ * (always puts 0-100% range)*/
+ if( track->subType == lfSubSample &&
+ track->heightPer > (3 * fontHeight ) )
+ {
+ ymax = y - (track->heightPer / 2) + (fontHeight / 2);
+ ymin = y + (track->heightPer / 2) - (fontHeight / 2);
+ hvGfxTextRight(hvg, leftLabelX, ymin,
+ leftLabelWidth-1, track->lineHeight,
+ track->ixAltColor, font, minRangeStr );
+ hvGfxTextRight(hvg, leftLabelX, ymax,
+ leftLabelWidth-1, track->lineHeight,
+ track->ixAltColor, font, maxRangeStr );
+ }
+ hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth-1,
+ track->lineHeight, labelColor, font,
+ track->shortLabel);
+ y += track->height;
+ break;
+ }
+/* NOTE: might want to just restore savedVis here for all track types,
+ but I'm being cautious... */
+if (sameString(track->tdb->type, "wigMaf"))
+ vis = savedVis;
+hvGfxUnclip(hvg);
+return y;
+}
+
+static void doLabelNextItemButtons(struct track *track, struct track *parentTrack, struct hvGfx *hvg, MgFont *font, int y,
+ int trackPastTabX, int trackPastTabWidth, int fontHeight,
+ int insideHeight, Color labelColor)
+/* If the track allows label next-item buttons (next gene), draw them. */
+/* The button will cause hgTracks to run again with the additional CGI */
+/* vars nextItem=trackName or prevItem=trackName, which will then */
+/* signal the browser to find the next thing on the track before it */
+/* does anything else. */
+{
+int portWidth = insideWidth;
+int portX = insideX;
+#ifdef IMAGEv2_DRAG_SCROLL
+// If a portal was established, then set the portal dimensions
+int portalStart,chromStart;
+double basesPerPixel;
+if (imgBoxPortalDimensions(theImgBox,&chromStart,NULL,NULL,NULL,&portalStart,NULL,&portWidth,&basesPerPixel))
+ {
+ portX = (int)((portalStart - chromStart) / basesPerPixel);
+ portX += gfxBorder;
+ if (withLeftLabels)
+ portX += tl.leftLabelWidth + gfxBorder;
+ portWidth = portWidth-gfxBorder-insideX;
+ }
+#endif//def IMAGEv2_DRAG_SCROLL
+int arrowWidth = insideHeight;
+int arrowButtonWidth = arrowWidth + 2 * NEXT_ITEM_ARROW_BUFFER;
+int rightButtonX = portX + portWidth - arrowButtonWidth - 1;
+char buttonText[256];
+Color fillColor = lightGrayIndex();
+labelColor = blackIndex();
+hvGfxNextItemButton(hvg, rightButtonX + NEXT_ITEM_ARROW_BUFFER, y, arrowWidth, arrowWidth, labelColor, fillColor, TRUE);
+hvGfxNextItemButton(hvg, portX + NEXT_ITEM_ARROW_BUFFER, y, arrowWidth, arrowWidth, labelColor, fillColor, FALSE);
+safef(buttonText, ArraySize(buttonText), "hgt.prevItem=%s", track->track);
+mapBoxReinvoke(hvg, portX, y + 1, arrowButtonWidth, insideHeight, NULL,
+ NULL, 0, 0, (revCmplDisp ? "Next item" : "Prev item"), buttonText);
+mapBoxToggleVis(hvg, portX + arrowButtonWidth, y + 1, portWidth - (2 * arrowButtonWidth),
+ insideHeight, (theImgBox ? track : parentTrack));
+safef(buttonText, ArraySize(buttonText), "hgt.nextItem=%s", track->track);
+mapBoxReinvoke(hvg, portX + portWidth - arrowButtonWidth, y + 1, arrowButtonWidth, insideHeight, NULL,
+ NULL, 0, 0, (revCmplDisp ? "Prev item" : "Next item"), buttonText);
+}
+
+static int doCenterLabels(struct track *track, struct track *parentTrack,
+ struct hvGfx *hvg, MgFont *font, int y)
+/* Draw center labels. Return y coord */
+{
+if (track->limitedVis != tvHide)
+ {
+ if (isCenterLabelIncluded(track))
+ {
+ int trackPastTabX = (withLeftLabels ? trackTabWidth : 0);
+ int trackPastTabWidth = tl.picWidth - trackPastTabX;
+ int fontHeight = mgFontLineHeight(font);
+ int insideHeight = fontHeight-1;
+ boolean toggleDone = FALSE;
+ char *label = track->longLabel;
+ if (isCenterLabelConditional(track))
+ label = track->tdb->parent->longLabel;
+ Color labelColor = (track->labelColor ?
+ track->labelColor : track->ixColor);
+ hvGfxTextCentered(hvg, insideX, y+1, insideWidth, insideHeight,
+ labelColor, font, label);
+ if (track->nextItemButtonable && track->nextPrevItem && !tdbIsComposite(track->tdb))
+ {
+ if (withNextItemArrows || trackDbSettingOn(track->tdb, "nextItemButton"))
+ {
+ doLabelNextItemButtons(track, parentTrack, hvg, font, y, trackPastTabX,
+ trackPastTabWidth, fontHeight, insideHeight, labelColor);
+ toggleDone = TRUE;
+ }
+ }
+ if (!toggleDone)
+ mapBoxToggleVis(hvg, trackPastTabX, y+1,trackPastTabWidth, insideHeight,
+ (theImgBox ? track : parentTrack));
+ y += fontHeight;
+ }
+ y += track->totalHeight(track, track->limitedVis);
+ }
+return y;
+}
+
+static int doDrawItems(struct track *track, struct hvGfx *hvg, MgFont *font,
+ int y, long *lastTime)
+/* Draw track items. Return y coord */
+{
+int fontHeight = mgFontLineHeight(font);
+int pixWidth = tl.picWidth;
+if (isCenterLabelIncluded(track))
+ y += fontHeight;
+if (track->limitedVis == tvPack)
+ {
+ hvGfxSetClip(hvg, gfxBorder+trackTabWidth+1, y,
+ pixWidth-2*gfxBorder-trackTabWidth-1, track->height);
+ }
+else
+ hvGfxSetClip(hvg, insideX, y, insideWidth, track->height);
+track->drawItems(track, winStart, winEnd, hvg, insideX, y, insideWidth,
+ font, track->ixColor, track->limitedVis);
+if (measureTiming && lastTime)
+ {
+ long thisTime = clock1000();
+ track->drawTime = thisTime - *lastTime;
+ *lastTime = thisTime;
+ }
+hvGfxUnclip(hvg);
+y += track->totalHeight(track, track->limitedVis);
+return y;
+}
+
+static int doMapItems(struct track *track, struct hvGfx *hvg, int fontHeight, int y)
+/* Draw map boxes around track items */
+{
+char *type = track->tdb->type;
+int newy;
+int trackPastTabX = (withLeftLabels ? trackTabWidth : 0);
+int trackPastTabWidth = tl.picWidth - trackPastTabX;
+int start = 1;
+struct slList *item;
+boolean isWig = (sameString("wig", type) || startsWith("wig ", type) ||
+ startsWith("bedGraph", type));
+
+if (isCenterLabelIncluded(track))
+ y += fontHeight;
+if (track->mapsSelf)
+ {
+ return y+track->height;
+ }
+if (track->subType == lfSubSample && track->items == NULL)
+ y += track->lineHeight;
+
+/* override doMapItems for hapmapLd track */
+/* does not scale with subtracks right now, so this is commented out until it can be fixed
+if (startsWith("hapmapLd",track->table))
+ {
+ y += round((double)(scaleForPixels(insideWidth)*insideWidth/2));
+ return y;
+ }
+*/
+for (item = track->items; item != NULL; item = item->next)
+ {
+ int height = track->itemHeight(track, item);
+
+ /*wiggle tracks don't always increment height (y-value) here*/
+ if( track->subType == lfSubSample )
+ {
+ newy = y;
+ if( !start && item->next != NULL )
+ {
+ newy += sampleUpdateY( track->itemName(track, item),
+ track->itemName(track, item->next),
+ height );
+ }
+ else if( item->next != NULL || start )
+ newy += height;
+ start = 0;
+ y = newy;
+ }
+ else
+ {
+ if (track->mapItem == NULL)
+ track->mapItem = genericMapItem;
+ if (!track->mapsSelf)
+ {
+ track->mapItem(track, hvg, item, track->itemName(track, item),
+ track->mapItemName(track, item),
+ track->itemStart(track, item),
+ track->itemEnd(track, item), trackPastTabX,
+ y, trackPastTabWidth,height);
+ }
+ y += height;
+ }
+ }
+/* Wiggle track's ->height is actually one less than what it returns from
+ * totalHeight()... I think the least disruptive way to account for this
+ * (and not touch Ryan Weber's Sample stuff) is to just correct here if
+ * we see wiggle or bedGraph: */
+if (isWig)
+ y++;
+return y;
+}
+
+static int doOwnLeftLabels(struct track *track, struct hvGfx *hvg,
+ MgFont *font, int y)
+/* Track draws it own, custom left labels */
+{
+int fontHeight = mgFontLineHeight(font);
+int tHeight = trackPlusLabelHeight(track, fontHeight);
+Color labelColor = (track->labelColor ? track->labelColor : track->ixColor);
+hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight);
+track->drawLeftLabels(track, winStart, winEnd,
+ hvg, leftLabelX, y, leftLabelWidth, tHeight,
+ isCenterLabelIncluded(track), font, labelColor,
+ track->limitedVis);
+hvGfxUnclip(hvg);
+y += tHeight;
+return y;
+}
+
+// defined below:
+static int getMaxWindowToDraw(struct trackDb *tdb);
+
+int doTrackMap(struct track *track, struct hvGfx *hvg, int y, int fontHeight,
+ int trackPastTabX, int trackPastTabWidth)
+/* Write out the map for this track. Return the new offset. */
+{
+int mapHeight = 0;
+switch (track->limitedVis)
+ {
+ case tvPack:
+ case tvSquish:
+ y += trackPlusLabelHeight(track, fontHeight);
+ break;
+ case tvFull:
+ if (!nextItemCompatible(track))
+ {
+ if (trackIsCompositeWithSubtracks(track)) //TODO: Change when tracks->subtracks are always set for composite
+ {
+ if (isCenterLabelIncluded(track))
+ y += fontHeight;
+ struct track *subtrack;
+ for (subtrack = track->subtracks; subtrack != NULL;subtrack = subtrack->next)
+ {
+ if (isSubtrackVisible(subtrack))
+ {
+ if(subtrack->limitedVis == tvFull)
+ y = doMapItems(subtrack, hvg, fontHeight, y);
+ else
+ {
+ if (isCenterLabelIncluded(subtrack))
+ y += fontHeight;
+ if(theImgBox && subtrack->limitedVis == tvDense)
+ mapBoxToggleVis(hvg, trackPastTabX, y, trackPastTabWidth, track->lineHeight, subtrack);
+ y += subtrack->totalHeight(subtrack, subtrack->limitedVis);
+ }
+ }
+ }
+ }
+ else
+ y = doMapItems(track, hvg, fontHeight, y);
+ }
+ else
+ y += trackPlusLabelHeight(track, fontHeight);
+ break;
+ case tvDense:
+ if (isCenterLabelIncluded(track))
+ y += fontHeight;
+ if (tdbIsComposite(track->tdb))
+ mapHeight = track->height;
+ else
+ mapHeight = track->lineHeight;
+ int maxWinToDraw = getMaxWindowToDraw(track->tdb);
+ if (maxWinToDraw <= 1 || (winEnd - winStart) <= maxWinToDraw)
+ mapBoxToggleVis(hvg, trackPastTabX, y, trackPastTabWidth, mapHeight, track);
+ y += mapHeight;
+ break;
+ case tvHide:
+ default:
+ break; /* Do nothing; */
+ }
+return y;
+}
+
+int computeScaleBar(int numBases, char scaleText[], int scaleTextSize)
+/* Do some scalebar calculations and return the number of bases the scalebar will span. */
+{
+char *baseWord = "bases";
+int scaleBases = 0;
+int scaleBasesTextNum = 0;
+int numFigs = (int)log10(numBases);
+int frontNum = (int)(numBases/pow(10,numFigs));
+if (frontNum == 1)
+ {
+ numFigs--;
+ scaleBases = 5 * pow(10, numFigs);
+ scaleBasesTextNum = 5 * pow(10, numFigs % 3);
+ }
+else if ((frontNum > 1) && (frontNum <= 4))
+ {
+ scaleBases = pow(10, numFigs);
+ scaleBasesTextNum = pow(10, numFigs % 3);
+ }
+else if (frontNum > 4)
+ {
+ scaleBases = 2 * pow(10, numFigs);
+ scaleBasesTextNum = 2 * pow(10, numFigs % 3);
+ }
+if ((numFigs >= 3) && (numFigs < 6))
+ baseWord = "kb";
+else if ((numFigs >= 6) && (numFigs < 9))
+ baseWord = "Mb";
+safef(scaleText, scaleTextSize, "%d %s", scaleBasesTextNum, baseWord);
+return scaleBases;
+}
+
+enum trackVisibility limitedVisFromComposite(struct track *subtrack)
+/* returns the subtrack visibility which may be limited by composite with multi-view dropdowns. */
+{
+#ifdef SUBTRACKS_HAVE_VIS
+if (tdbIsCompositeChild(subtrack->tdb))
+ {
+ if (fourStateVisible(subtrackFourStateChecked(subtrack->tdb,cart))) // Don't need all 4 states here. Visible=checked&&enabled
+ {
+ char *var = cartOptionalString(cart, subtrack->track);
+ if (var)
+ {
+ subtrack->visibility = hTvFromString(var);
+
+ if (subtrack->limitedVisSet)
+ subtrack->limitedVis = tvMin(subtrack->visibility,subtrack->limitedVis);
+ else
+ {
+ if (subtrack->visibility != tvHide && slCount(subtrack->items) == 0)
+ subtrack->loadItems(subtrack);
+
+ limitVisibility(subtrack);
+ }
+ return hTvFromString(var);
+ }
+ }
+ else
+ return tvHide;
+ }
+#endif///def SUBTRACKS_HAVE_VIS
+
+enum trackVisibility vis = subtrack->limitedVis == tvHide ?
+ subtrack->visibility :
+ tvMin(subtrack->visibility,subtrack->limitedVis);
+struct trackDb *tdb = subtrack->tdb;
+if(tdbIsCompositeChild(tdb))
+ {
+ struct trackDb *parentTdb = tdbGetComposite(tdb);
+ assert(parentTdb != NULL);
+
+ char *viewName = NULL;
+ if (subgroupFind(tdb,"view",&viewName))
+ {
+ int len = strlen(parentTdb->track) + strlen(viewName) + 10;
+
+ // Create the view dropdown var name. This needs to have the view name surrounded by dots
+ // in the middle for the javascript to work.
+ char ddName[len];
+ safef(ddName,len,"%s.%s.vis", parentTdb->track,viewName);
+ char * fromParent = cartOptionalString(cart, ddName);
+ if(fromParent)
+ vis = tvMin(vis,hTvFromString(fromParent));
+ else
+ vis = tvMin(vis,visCompositeViewDefault(parentTdb,viewName));
+ subgroupFree(&viewName);
+ }
+ }
+return vis;
+}
+
+static int makeRulerZoomBoxes(struct hvGfx *hvg, struct cart *cart, int winStart,int winEnd,
+ int insideWidth,int seqBaseCount,int rulerClickY,int rulerClickHeight)
+/* Make hit boxes that will zoom program around ruler. */
+{
+int boxes = 30;
+int winWidth = winEnd - winStart;
+int newWinWidth = winWidth;
+int i, ws, we = 0, ps, pe = 0;
+int mid, ns, ne;
+double wScale = (double)winWidth/boxes;
+double pScale = (double)insideWidth/boxes;
+char message[32];
+char *zoomType = cartCgiUsualString(cart, RULER_BASE_ZOOM_VAR, ZOOM_3X);
+
+safef(message, sizeof(message), "%s zoom", zoomType);
+if (sameString(zoomType, ZOOM_1PT5X))
+ newWinWidth = winWidth/1.5;
+else if (sameString(zoomType, ZOOM_3X))
+ newWinWidth = winWidth/3;
+else if (sameString(zoomType, ZOOM_10X))
+ newWinWidth = winWidth/10;
+else if (sameString(zoomType, ZOOM_BASE))
+ newWinWidth = insideWidth/tl.mWidth;
+else
+ errAbort("invalid zoom type %s", zoomType);
+
+if (newWinWidth < 1)
+ newWinWidth = 1;
+
+for (i=1; i<=boxes; ++i)
+ {
+ ps = pe;
+ ws = we;
+ pe = round(pScale*i);
+ we = round(wScale*i);
+ mid = (ws + we)/2 + winStart;
+ ns = mid-newWinWidth/2;
+ ne = ns + newWinWidth;
+ if (ns < 0)
+ {
+ ns = 0;
+ ne -= ns;
+ }
+ if (ne > seqBaseCount)
+ {
+ ns -= (ne - seqBaseCount);
+ ne = seqBaseCount;
+ }
+ if(!dragZooming)
+ {
+ mapBoxJumpTo(hvg, ps+insideX,rulerClickY,pe-ps,rulerClickHeight,
+ chromName, ns, ne, message);
+ }
+ }
+return newWinWidth;
+}
+
+static int doDrawRuler(struct hvGfx *hvg,int *newWinWidth,int *rulerClickHeight,
+ int rulerHeight, int yAfterRuler, int yAfterBases, MgFont *font,
+ int fontHeight,boolean rulerCds)
+/* draws the ruler. */
+{
+int scaleBarPad = 2;
+int scaleBarHeight = fontHeight;
+int scaleBarTotalHeight = fontHeight + 2 * scaleBarPad;
+int titleHeight = fontHeight;
+int baseHeight = fontHeight;
+//int yAfterBases = yAfterRuler;
+int showPosHeight = fontHeight;
+int codonHeight = fontHeight;
+struct dnaSeq *seq = NULL;
+int rulerClickY = 0;
+*rulerClickHeight = rulerHeight;
+
+int y = rulerClickY;
+hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1);
+int relNumOff = winStart;
+
+if (baseTitle)
+ {
+ hvGfxTextCentered(hvg, insideX, y, insideWidth, titleHeight,MG_BLACK, font, baseTitle);
+ *rulerClickHeight += titleHeight;
+ y += titleHeight;
+ }
+if (baseShowPos||baseShowAsm)
+ {
+ char txt[256];
+ char numBuf[SMALLBUF];
+ char *freezeName = NULL;
+ freezeName = hFreezeFromDb(database);
+ sprintLongWithCommas(numBuf, winEnd-winStart);
+ if(freezeName == NULL)
+ freezeName = "Unknown";
+ if (baseShowPos&&baseShowAsm)
+ safef(txt,sizeof(txt),"%s %s %s (%s bp)",organism,
+ freezeName, addCommasToPos(database, position), numBuf);
+ else if (baseShowPos)
+ safef(txt,sizeof(txt),"%s (%s bp)",addCommasToPos(database, position),numBuf);
+ else
+ safef(txt,sizeof(txt),"%s %s",organism,freezeName);
+ hvGfxTextCentered(hvg, insideX, y, insideWidth, showPosHeight,MG_BLACK, font, txt);
+ *rulerClickHeight += showPosHeight;
+ freez(&freezeName);
+ y += showPosHeight;
+ }
+if (baseShowScaleBar)
+ {
+ char scaleText[32];
+ int numBases = winEnd-winStart;
+ int scaleBases = computeScaleBar(numBases, scaleText, sizeof(scaleText));
+ int scalePixels = (int)((double)insideWidth*scaleBases/numBases);
+ int scaleBarX = insideX + (int)(((double)insideWidth-scalePixels)/2);
+ int scaleBarEndX = scaleBarX + scalePixels;
+ int scaleBarY = y + 0.5 * scaleBarTotalHeight;
+ *rulerClickHeight += scaleBarTotalHeight;
+ hvGfxTextRight(hvg, insideX, y + scaleBarPad,
+ (scaleBarX-2)-insideX, scaleBarHeight, MG_BLACK, font, scaleText);
+ hvGfxLine(hvg, scaleBarX, scaleBarY, scaleBarEndX, scaleBarY, MG_BLACK);
+ hvGfxLine(hvg, scaleBarX, y+scaleBarPad, scaleBarX,
+ y+scaleBarTotalHeight-scaleBarPad, MG_BLACK);
+ hvGfxLine(hvg, scaleBarEndX, y+scaleBarPad, scaleBarEndX,
+ y+scaleBarTotalHeight-scaleBarPad, MG_BLACK);
+ y += scaleBarTotalHeight;
+ }
+if (baseShowRuler)
+ {
+ hvGfxDrawRulerBumpText(hvg, insideX, y, rulerHeight, insideWidth, MG_BLACK,
+ font, relNumOff, winBaseCount, 0, 1);
+ }
+*newWinWidth = makeRulerZoomBoxes(hvg, cart,winStart,winEnd,insideWidth,seqBaseCount,rulerClickY,*rulerClickHeight);
+
+if (zoomedToBaseLevel || rulerCds)
+ {
+ Color baseColor = MG_BLACK;
+ int start, end, chromSize;
+ struct dnaSeq *extraSeq;
+ /* extraSeq has extra leading & trailing bases
+ * for translation in to amino acids */
+ boolean complementRulerBases =
+ cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE);
+ // gray bases if not matching the direction of display
+ if (complementRulerBases != revCmplDisp)
+ baseColor = MG_GRAY;
+
+ /* get sequence, with leading & trailing 3 bases
+ * used for amino acid translation */
+ start = max(winStart - 3, 0);
+ chromSize = hChromSize(database, chromName);
+ end = min(winEnd + 3, chromSize);
+ extraSeq = hDnaFromSeq(database, chromName, start, end, dnaUpper);
+ if (start != winStart - 3 || end != winEnd + 3)
+ {
+ /* at chromosome boundaries, pad with N's to assure
+ * leading & trailing 3 bases */
+ char header[4] = "NNN", trailer[4] = "NNN";
+ int size = winEnd - winStart + 6;
+ char *padded = (char *)needMem(size+1);
+ header[max(3 - winStart, 0)] = 0;
+ trailer[max(winEnd - chromSize + 3, 0)] = 0;
+ safef(padded, size+1, "%s%s%s", header, extraSeq->dna, trailer);
+ extraSeq = newDnaSeq(padded, strlen(padded), extraSeq->name);
+ }
+
+ /* for drawing bases, must clip off leading and trailing 3 bases */
+ seq = cloneDnaSeq(extraSeq);
+ seq = newDnaSeq(seq->dna+3, seq->size-6, seq->name);
+
+ if (zoomedToBaseLevel)
+ drawBases(hvg, insideX, y+rulerHeight, insideWidth, baseHeight,
+ baseColor, font, complementRulerBases, seq);
+
+ /* set up clickable area to toggle ruler visibility */
+ {
+ char newRulerVis[100];
+ safef(newRulerVis, 100, "%s=%s", RULER_TRACK_NAME,
+ rulerMode == tvFull ?
+ rulerMenu[tvDense] :
+ rulerMenu[tvFull]);
+ mapBoxReinvoke(hvg, insideX, y+rulerHeight, insideWidth,baseHeight,
+ NULL, NULL, 0, 0, "", newRulerVis);
+ }
+ if (rulerCds)
+ {
+ /* display codons */
+ int frame;
+ int firstFrame = 0;
+ int mod; // for determining frame ordering on display
+ struct simpleFeature *sfList;
+ double scale = scaleForWindow(insideWidth, winStart, winEnd);
+
+ /* WARNING: tricky code to assure that an amino acid
+ * stays in the same frame line on the browser during panning.
+ * There may be a simpler way... */
+ if (complementRulerBases)
+ mod = (chromSize - winEnd) % 3;
+ else
+ mod = winStart % 3;
+ if (mod == 0)
+ firstFrame = 0;
+ else if (mod == 1)
+ firstFrame = 2;
+ else if (mod == 2)
+ firstFrame = 1;
+
+ y = yAfterBases;
+ if (complementRulerBases)
+ reverseComplement(extraSeq->dna, extraSeq->size);
+ for (frame = 0; frame < 3; frame++, y += codonHeight)
+ {
+ /* reference frame to start of chromosome */
+ int refFrame = (firstFrame + frame) % 3;
+
+ /* create list of codons in the specified coding frame */
+ sfList = baseColorCodonsFromDna(refFrame, winStart, winEnd,
+ extraSeq, complementRulerBases);
+ /* draw the codons in the list, with alternating colors */
+ baseColorDrawRulerCodons(hvg, sfList, scale, insideX, y,
+ codonHeight, font, winStart, MAXPIXELS,
+ zoomedToCodonLevel);
+ }
+ }
+ }
+hvGfxUnclip(hvg);
+return y;
+}
+
+static void rAddToTrackHash(struct hash *trackHash, struct track *trackList)
+/* Add list and any children of list to hash. */
+{
+struct track *track;
+for (track = trackList; track != NULL; track = track->next)
+ {
+ hashAddUnique(trackHash, track->track, track);
+ rAddToTrackHash(trackHash, track->subtracks);
+ }
+}
+
+static void makeGlobalTrackHash(struct track *trackList)
+/* Start a global track hash. */
+{
+trackHash = newHash(8);
+rAddToTrackHash(trackHash, trackList);
+}
+
+
+void makeActiveImage(struct track *trackList, char *psOutput)
+/* Make image and image map. */
+{
+struct track *track;
+MgFont *font = tl.font;
+struct hvGfx *hvg;
+struct tempName gifTn;
+char *mapName = "map";
+int fontHeight = mgFontLineHeight(font);
+int trackPastTabX = (withLeftLabels ? trackTabWidth : 0);
+int trackTabX = gfxBorder;
+int trackPastTabWidth = tl.picWidth - trackPastTabX;
+int pixWidth, pixHeight;
+int y=0;
+int titleHeight = fontHeight;
+int scaleBarPad = 2;
+int scaleBarHeight = fontHeight;
+int scaleBarTotalHeight = fontHeight + 2 * scaleBarPad;
+int showPosHeight = fontHeight;
+int rulerHeight = fontHeight;
+int baseHeight = fontHeight;
+int basePositionHeight = rulerHeight;
+int codonHeight = fontHeight;
+int rulerTranslationHeight = codonHeight * 3; // 3 frames
+int yAfterRuler = gfxBorder;
+int yAfterBases = yAfterRuler; // differs if base-level translation shown
+boolean rulerCds = zoomedToCdsColorLevel;
+int rulerClickHeight = 0;
+int newWinWidth = 0;
+
+/* Figure out dimensions and allocate drawing space. */
+pixWidth = tl.picWidth;
+
+leftLabelX = gfxBorder;
+leftLabelWidth = insideX - gfxBorder*3;
+
+struct image *theOneImg = NULL; // No need to be global, only the map needs to be global
+struct image *theSideImg = NULL; // Because dragScroll drags off end of image, the side label gets seen. Therefore we need 2 images!!
+struct hvGfx *hvgSide = NULL; // Strategy an extra pointer to a side image that can be built if needed
+//struct imgTrack *curImgTrack = NULL; // Make this global for now to avoid huge rewrite
+struct imgSlice *curSlice = NULL; // No need to be global, only the map needs to be global
+struct mapSet *curMap = NULL; // Make this global for now to avoid huge rewrite
+// Set up imgBox dimensions
+int sliceWidth[stMaxSliceTypes]; // Just being explicit
+int sliceOffsetX[stMaxSliceTypes];
+int sliceHeight = 0;
+int sliceOffsetY = 0;
+char *rulerTtl = NULL;
+if(theImgBox)
+// theImgBox is a global for now to avoid huge rewrite of hgTracks. It is started
+// prior to this in doTrackForm()
+ {
+ rulerTtl = (dragZooming?"drag select or click to zoom":"click to zoom 3x");
+ hPrintf(" \n", database);
+ hPrintf(" \n", chromName);
+ hPrintf(" \n", winStart);
+ hPrintf(" \n", winEnd);
+ hPrintf(" \n", tl.picWidth);
+ #ifdef IMAGEv2_DRAG_SCROLL
+ // If a portal was established, then set the global dimensions to the entire image size
+ if(imgBoxPortalDimensions(theImgBox,&winStart,&winEnd,&(tl.picWidth),NULL,NULL,NULL,NULL,NULL))
+ {
+ pixWidth = tl.picWidth;
+ winBaseCount = winEnd - winStart;
+ insideWidth = tl.picWidth-gfxBorder-insideX;
+ }
+ #endif//def IMAGEv2_DRAG_SCROLL
+ memset((char *)sliceWidth, 0,sizeof(sliceWidth));
+ memset((char *)sliceOffsetX,0,sizeof(sliceOffsetX));
+ if (withLeftLabels)
+ {
+ sliceWidth[stButton] = trackTabWidth + 1;
+ sliceWidth[stSide] = leftLabelWidth - sliceWidth[stButton] + 2;
+ sliceOffsetX[stSide] = (revCmplDisp? (tl.picWidth - sliceWidth[stSide] - sliceWidth[stButton]) : sliceWidth[stButton]);
+ sliceOffsetX[stButton] = (revCmplDisp? (tl.picWidth - sliceWidth[stButton]) : 0);
+ }
+ sliceOffsetX[stData] = (revCmplDisp?0:sliceWidth[stSide] + sliceWidth[stButton]);
+ sliceWidth[stData] = tl.picWidth - (sliceWidth[stSide] + sliceWidth[stButton]);
+ }
+struct flatTracks *flatTracks = NULL;
+struct flatTracks *flatTrack = NULL;
+
+if (rulerMode != tvFull)
+ {
+ rulerCds = FALSE;
+ }
+
+/* Figure out height of each visible track. */
+pixHeight = gfxBorder;
+if (rulerMode != tvHide)
+ {
+ if (!baseShowRuler && !baseTitle && !baseShowPos && !baseShowAsm && !baseShowScaleBar && !zoomedToBaseLevel && !rulerCds)
+ {
+ warn("Can't turn everything off in base position track. Turning ruler back on");
+ baseShowRuler = TRUE;
+ cartSetBoolean(cart, BASE_SHOWRULER, TRUE);
+ }
+
+ if (baseTitle)
+ basePositionHeight += titleHeight;
+
+ if (baseShowPos||baseShowAsm)
+ basePositionHeight += showPosHeight;
+
+ if (baseShowScaleBar)
+ basePositionHeight += scaleBarTotalHeight;
+
+ if (!baseShowRuler)
+ {
+ basePositionHeight -= rulerHeight;
+ rulerHeight = 0;
+ }
+
+ if (zoomedToBaseLevel)
+ basePositionHeight += baseHeight;
+
+ yAfterRuler += basePositionHeight;
+ yAfterBases = yAfterRuler;
+ pixHeight += basePositionHeight;
+ if (rulerCds)
+ {
+ yAfterRuler += rulerTranslationHeight;
+ pixHeight += rulerTranslationHeight;
+ }
+ }
+
+boolean safeHeight = TRUE;
+/* firefox on Linux worked almost up to 34,000 at the default 620 width */
+#define maxSafeHeight 32000
+/* Hash tracks/subtracks, limit visibility and calculate total image height: */
+for (track = trackList; track != NULL; track = track->next)
+ {
+ limitVisibility(track);
+ if (!safeHeight)
+ {
+ track->limitedVis = tvHide;
+ track->limitedVisSet = TRUE;
+ continue;
+ }
+#ifndef SUBTRACKS_HAVE_VIS
+ if (track->limitedVis != tvHide)
+#endif///ndef SUBTRACKS_HAVE_VIS
+ {
+ if (tdbIsComposite(track->tdb))
+ {
+ struct track *subtrack;
+ for (subtrack = track->subtracks; subtrack != NULL;
+ subtrack = subtrack->next)
+ {
+ if (!isSubtrackVisible(subtrack))
+ continue;
+
+ // If the composite track has "view" based drop downs, set visibility based upon those
+ enum trackVisibility vis = limitedVisFromComposite(subtrack);
+ if(subtrack->visibility != vis)
+ {
+ subtrack->visibility = vis;
+ if (subtrack->limitedVisSet)
+ {
+ subtrack->limitedVis = tvMin(vis, subtrack->limitedVis);
+ }
+ else
+ {
+ subtrack->limitedVis = tvMin(vis,subtrack->visibility);
+ subtrack->limitedVisSet = (subtrack->limitedVis != tvHide && subtrack->visibility != subtrack->limitedVis);
+ }
+ }
+ if (!subtrack->limitedVisSet && track->limitedVisSet)
+ {
+ subtrack->visibility = track->visibility;
+ subtrack->limitedVis = track->limitedVis;
+ subtrack->limitedVisSet = track->limitedVisSet;
+ }
+
+ #ifdef SUBTRACKS_HAVE_VIS
+ if (subtrack->limitedVis != tvHide)
+ #endif///def SUBTRACKS_HAVE_VIS
+ {
+ subtrack->hasUi = track->hasUi;
+ flatTracksAdd(&flatTracks,subtrack,cart);
+ }
+ }
+ }
+ else
+ #ifdef SUBTRACKS_HAVE_VIS
+ if (track->limitedVis != tvHide)
+ #endif///def SUBTRACKS_HAVE_VIS
+ flatTracksAdd(&flatTracks,track,cart);
+ if (maxSafeHeight < (pixHeight+trackPlusLabelHeight(track,fontHeight)))
+ {
+ char numBuf[SMALLBUF];
+ sprintLongWithCommas(numBuf, maxSafeHeight);
+ printf("warning: image is over %s pixels high at "
+ "track '%s', remaining tracks set to hide "
+ "for this view. \n", numBuf, track->tdb->shortLabel);
+ safeHeight = FALSE;
+ track->limitedVis = tvHide;
+ track->limitedVisSet = TRUE;
+ }
+ }
+ }
+flatTracksSort(&flatTracks); // Now we should have a perfectly good flat track list!
+struct track *prevTrack = NULL;
+for (flatTrack = flatTracks,prevTrack=NULL; flatTrack != NULL; flatTrack = flatTrack->next)
+ {
+ track = flatTrack->track;
+ if (maxSafeHeight < (pixHeight+trackPlusLabelHeight(track,fontHeight)))
+ {
+ char numBuf[SMALLBUF];
+ sprintLongWithCommas(numBuf, maxSafeHeight);
+ printf("warning: image is over %s pixels high at "
+ "track '%s', remaining tracks set to hide "
+ "for this view. \n", numBuf, track->tdb->shortLabel);
+ safeHeight = FALSE;
+ track->limitedVis = tvHide;
+ track->limitedVisSet = TRUE;
+ }
+ if (track->limitedVis != tvHide)
+ {
+ track->prevTrack = prevTrack; // Important for keeping track of conditional center labels!
+ pixHeight += trackPlusLabelHeight(track, fontHeight);
+ prevTrack = track;
+ }
+ }
+
+imagePixelHeight = pixHeight;
+if (psOutput)
+ {
+ hvg = hvGfxOpenPostScript(pixWidth, pixHeight, psOutput);
+ hvgSide = hvg; // Always only one image
+ }
+else
+ {
+ boolean transparentImage = FALSE;
+ if (theImgBox!=NULL)
+ transparentImage = TRUE; // transparent because BG (blue ruler lines) is separate image
+
+#ifdef USE_PNG
+ trashDirFile(&gifTn, "hgt", "hgt", ".png");
+ hvg = hvGfxOpenPng(pixWidth, pixHeight, gifTn.forCgi, transparentImage);
+#else //ifndef
+ trashDirFile(&gifTn, "hgt", "hgt", ".gif");
+ hvg = hvGfxOpenGif(pixWidth, pixHeight, gifTn.forCgi, transparentImage);
+#endif //ndef USE_PNG
+
+ if(theImgBox)
+ {
+ // Adds one single image for all tracks (COULD: build the track by track images)
+ theOneImg = imgBoxImageAdd(theImgBox,gifTn.forHtml,NULL,pixWidth, pixHeight,FALSE);
+ theSideImg = theOneImg; // Unlkess this is overwritten below, there is a single image
+ }
+ hvgSide = hvg; // Unlkess this is overwritten below, there is a single image
+
+ if (theImgBox && theImgBox->showPortal && withLeftLabels)
+ {
+ // TODO: It would be great to make the images smaller, but keeping both the same full size for now
+ struct tempName gifTnSide;
+ #ifdef USE_PNG
+ trashDirFile(&gifTnSide, "hgt", "side", ".png");
+ hvgSide = hvGfxOpenPng(pixWidth, pixHeight, gifTnSide.forCgi, transparentImage);
+ #else //ifndef
+ trashDirFile(&gifTnSide, "hgt", "side", ".gif");
+ hvgSide = hvGfxOpenGif(pixWidth, pixHeight, gifTnSide.forCgi, transparentImage);
+ #endif //ndef USE_PNG
+
+ // Also add the side image
+ theSideImg = imgBoxImageAdd(theImgBox,gifTnSide.forHtml,NULL,pixWidth, pixHeight,FALSE);
+ hvgSide->rc = revCmplDisp;
+ initColors(hvgSide);
+ }
+ }
+hvg->rc = revCmplDisp;
+initColors(hvg);
+
+/* Start up client side map. */
+hPrintf("\n", mapName);
+
+/* Find colors to draw in. */
+findTrackColors(hvg, trackList);
+
+// Good to go ahead and add all imgTracks regardless of buttons, left label, centerLabel, etc.
+if(theImgBox)
+ {
+ if (rulerMode != tvHide)
+ {
+ curImgTrack = imgBoxTrackFindOrAdd(theImgBox,NULL,RULER_TRACK_NAME,rulerMode,FALSE,IMG_FIXEDPOS); // No tdb, no centerlabel, not reorderable
+ }
+
+ for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
+ {
+ track = flatTrack->track;
+ if (track->limitedVis != tvHide)
+ {
+ #ifdef SUBTRACKS_HAVE_VIS
+ if(track->labelColor == track->ixColor && track->ixColor == 0)
+ track->ixColor = hvGfxFindRgb(hvg, &track->color);
+ #endif//def SUBTRACKS_HAVE_VIS
+ int order = flatTrack->order;
+ curImgTrack = imgBoxTrackFindOrAdd(theImgBox,track->tdb,NULL,track->limitedVis,isCenterLabelIncluded(track),order);
+ }
+ }
+ }
+
+
+/* Draw mini-buttons. */
+if (withLeftLabels && psOutput == NULL)
+ {
+ int butOff;
+ boolean grayButtonGroup = FALSE;
+ struct group *lastGroup = NULL;
+ y = gfxBorder;
+ if (rulerMode != tvHide)
+ {
+ /* draw button for Base Position pseudo-track */
+ int height = basePositionHeight;
+ if (rulerCds)
+ height += rulerTranslationHeight;
+ if(theImgBox)
+ {
+ // Mini-buttons (side label slice) for ruler
+ sliceHeight = height + 1;
+ sliceOffsetY = 0;
+ curImgTrack = imgBoxTrackFind(theImgBox,NULL,RULER_TRACK_NAME);
+ curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stButton,NULL,NULL,sliceWidth[stButton],sliceHeight,sliceOffsetX[stButton],sliceOffsetY); // flatTracksButton is all html, no jpg
+ }
+ drawGrayButtonBox(hvgSide, trackTabX, y, trackTabWidth, height, TRUE);
+ mapBoxTrackUi(hvgSide, trackTabX, y, trackTabWidth, height,
+ RULER_TRACK_NAME, RULER_TRACK_LABEL, "ruler");
+ y += height + 1;
+ }
+
+ for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
+ {
+ track = flatTrack->track;
+ int h, yStart = y, yEnd;
+ if (track->limitedVis != tvHide)
+ {
+ y += trackPlusLabelHeight(track, fontHeight);
+ yEnd = y;
+ h = yEnd - yStart - 1;
+
+ /* alternate button colors for track groups*/
+ if (track->group != lastGroup)
+ grayButtonGroup = !grayButtonGroup;
+ lastGroup = track->group;
+ if (grayButtonGroup)
+ drawGrayButtonBox(hvgSide, trackTabX, yStart, trackTabWidth,
+ h, track->hasUi);
+ else
+ drawBlueButtonBox(hvgSide, trackTabX, yStart, trackTabWidth,
+ h, track->hasUi);
+ if(theImgBox)
+ {
+ // Mini-buttons (side label slice) for tracks
+ sliceHeight = yEnd - yStart;
+ sliceOffsetY = yStart - 1;
+ curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
+ curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stButton,NULL,NULL,sliceWidth[stButton],sliceHeight,sliceOffsetX[stButton],sliceOffsetY); // flatTracksButton is all html, no jpg
+ }
+ if (track->hasUi)
+ {
+ if(tdbIsCompositeChild(track->tdb))
+ {
+ struct trackDb *parent = tdbGetComposite(track->tdb);
+ mapBoxTrackUi(hvgSide, trackTabX, yStart, trackTabWidth, (yEnd - yStart - 1),
+ parent->track, parent->shortLabel, track->track);
+ }
+ else
+ mapBoxTrackUi(hvgSide, trackTabX, yStart, trackTabWidth, h, track->track, track->shortLabel, track->track);
+ }
+ }
+ }
+ butOff = trackTabX + trackTabWidth;
+ leftLabelX += butOff;
+ leftLabelWidth -= butOff;
+ }
+
+if (withLeftLabels)
+ {
+ Color lightRed = hvGfxFindColorIx(hvgSide, 255, 180, 180);
+
+ hvGfxBox(hvgSide, leftLabelX + leftLabelWidth, 0,
+ gfxBorder, pixHeight, lightRed);
+ y = gfxBorder;
+ if (rulerMode != tvHide)
+ {
+ if(theImgBox)
+ {
+ // side label slice for ruler
+ sliceHeight = basePositionHeight + (rulerCds ? rulerTranslationHeight : 0) + 1;
+ sliceOffsetY = 0;
+ curImgTrack = imgBoxTrackFind(theImgBox,NULL,RULER_TRACK_NAME);
+ curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stSide,theSideImg,NULL,sliceWidth[stSide],sliceHeight,sliceOffsetX[stSide],sliceOffsetY);
+ curMap = sliceMapFindOrStart(curSlice,RULER_TRACK_NAME,NULL); // No common linkRoot
+ }
+ if (baseTitle)
+ {
+ hvGfxTextRight(hvgSide, leftLabelX, y, leftLabelWidth-1, titleHeight,
+ MG_BLACK, font, WIN_TITLE_LABEL);
+ y += titleHeight;
+ }
+ if (baseShowPos||baseShowAsm)
+ {
+ hvGfxTextRight(hvgSide, leftLabelX, y, leftLabelWidth-1, showPosHeight,
+ MG_BLACK, font, WIN_POS_LABEL);
+ y += showPosHeight;
+ }
+ if (baseShowScaleBar)
+ {
+ y += scaleBarPad;
+ hvGfxTextRight(hvgSide, leftLabelX, y, leftLabelWidth-1, scaleBarHeight,
+ MG_BLACK, font, SCALE_BAR_LABEL);
+ y += scaleBarHeight + scaleBarPad;
+ }
+ if (baseShowRuler)
+ {
+ char rulerLabel[SMALLBUF];
+ char *shortChromName = cloneString(chromName);
+ safef(rulerLabel,ArraySize(rulerLabel),":%s",shortChromName);
+ int labelWidth = mgFontStringWidth(font,rulerLabel);
+ while ((labelWidth > 0) && (labelWidth > leftLabelWidth))
+ {
+ int len = strlen(shortChromName);
+ shortChromName[len-1] = 0;
+ safef(rulerLabel,ArraySize(rulerLabel),":%s",shortChromName);
+ labelWidth = mgFontStringWidth(font,rulerLabel);
+ }
+ if (hvgSide->rc)
+ safef(rulerLabel,ArraySize(rulerLabel),":%s",shortChromName);
+ else
+ safef(rulerLabel,ArraySize(rulerLabel),"%s:",shortChromName);
+ hvGfxTextRight(hvgSide, leftLabelX, y, leftLabelWidth-1, rulerHeight,
+ MG_BLACK, font, rulerLabel);
+ y += rulerHeight;
+ freeMem(shortChromName);
+ }
+ if (zoomedToBaseLevel || rulerCds)
+ {
+ /* disable complement toggle for HIV because HIV is single stranded RNA */
+ if (!hIsGsidServer())
+ drawComplementArrow(hvgSide,leftLabelX, y,
+ leftLabelWidth-1, baseHeight, font);
+ if (zoomedToBaseLevel)
+ y += baseHeight;
+ }
+ if (rulerCds)
+ y += rulerTranslationHeight;
+ }
+ for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
+ {
+ track = flatTrack->track;
+ if (track->limitedVis == tvHide)
+ continue;
+ if(theImgBox)
+ {
+ // side label slice for tracks
+ sliceHeight = trackPlusLabelHeight(track, fontHeight);
+ sliceOffsetY = y;
+ curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
+ curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stSide,theSideImg,NULL,sliceWidth[stSide],sliceHeight,sliceOffsetX[stSide],sliceOffsetY);
+ curMap = sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot
+ }
+ y = doLeftLabels(track, hvgSide, font, y);
+ }
+ }
+else
+ {
+ leftLabelX = leftLabelWidth = 0;
+ }
+
+/* Draw guidelines. */
+if (withGuidelines)
+ {
+ //if(theImgBox)
+ // TODO: We should be making transparent data images and a separate background img for guidelines.
+ // This will allow the guidelines to dragscroll while the center labels are static.
+ // NOTE: The background image could easily be a reusable file, based upon zoom level and width. Height could propbaby easily be stretched.
+ // struct image *bgImg = imgBoxImageAdd(theImgBox,gifBg.forHtml,
+ // (char *)(dragZooming?"click or drag mouse in base position track to zoom in" : NULL),
+ // pixWidth, pixHeight,FALSE);
+ struct hvGfx *bgImg = hvg; // Default to the one image
+ if(theImgBox)
+ {
+ struct tempName gifBg;
+ #ifdef USE_PNG
+ trashDirFile(&gifBg, "hgt", "bg", ".png"); // TODO: We could have a few static files by (pixHeight*pixWidth) And I doubt pixHeight is needed!
+ bgImg = hvGfxOpenPng(pixWidth, pixHeight, gifBg.forCgi, TRUE);
+ #else //ifndef
+ trashDirFile(&gifBg, "hgt", "bg", ".gif");
+ bgImg = hvGfxOpenGif(pixWidth, pixHeight, gifBg.forCgi, TRUE);
+ #endif //ndef USE_PNG
+ bgImg->rc = revCmplDisp;
+ imgBoxImageAdd(theImgBox,gifBg.forHtml,NULL,pixWidth, pixHeight,TRUE); // Adds BG image
+ }
+ int height = pixHeight - 2*gfxBorder;
+ int x;
+ Color lightBlue = hvGfxFindRgb(bgImg, &guidelineColor);
+
+ hvGfxSetClip(bgImg, insideX, gfxBorder, insideWidth, height);
+ y = gfxBorder;
+
+ for (x = insideX+guidelineSpacing-1; xnext)
+ {
+ track = flatTrack->track;
+ if (track->limitedVis == tvHide)
+ continue;
+
+ if(theImgBox)
+ {
+ // center label slice of tracks Must always make, even if the centerLabel is empty
+ sliceHeight = fontHeight;
+ sliceOffsetY = y;
+ curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
+ curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stCenter,theOneImg,NULL,sliceWidth[stData],sliceHeight,sliceOffsetX[stData],sliceOffsetY);
+ curMap = sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot
+ if (isCenterLabelConditional(track))
+ imgTrackUpdateCenterLabelSeen(curImgTrack,isCenterLabelConditionallySeen(track)?clNowSeen:clNotSeen);
+ }
+ y = doCenterLabels(track, track, hvg, font, y);
+ }
+ hvGfxUnclip(hvg);
+ }
+
+/* Draw tracks. */
+ {
+ long lastTime = 0;
+ y = yAfterRuler;
+ if (measureTiming)
+ lastTime = clock1000();
+ for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
+ {
+ track = flatTrack->track;
+ if (track->limitedVis == tvHide)
+ continue;
+
+ int centerLabelHeight = (isCenterLabelIncluded(track) ? fontHeight : 0);
+ int yStart = y + centerLabelHeight;
+ int yEnd = y + trackPlusLabelHeight(track, fontHeight);
+ if(theImgBox)
+ {
+ // data slice of tracks
+ sliceOffsetY = yStart;
+ sliceHeight = yEnd - yStart - 1;
+ curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
+ if(sliceHeight > 0)
+ {
+ curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stData,theOneImg,NULL,sliceWidth[stData],sliceHeight,sliceOffsetX[stData],sliceOffsetY);
+ curMap = sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot
+ }
+ }
+ y = doDrawItems(track, hvg, font, y, &lastTime);
+
+ if (theImgBox && track->limitedVis == tvDense && tdbIsCompositeChild(track->tdb))
+ mapBoxToggleVis(hvg, 0, yStart,tl.picWidth, sliceHeight,track); // Strange mabBoxToggleLogic handles reverse complement itself so x=0, width=tl.picWidth
+
+ if(yEnd!=y)
+ warn("Slice height does not add up. Expecting %d != %d actual",yEnd - yStart - 1,y-yStart);
+ }
+ y++;
+ }
+/* if a track can draw its left labels, now is the time since it
+ * knows what exactly happened during drawItems
+ */
+if (withLeftLabels)
+ {
+ y = yAfterRuler;
+ for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
+ {
+ track = flatTrack->track;
+ if (track->limitedVis == tvHide)
+ continue;
+ if(theImgBox)
+ {
+ // side label slice of tracks
+ sliceHeight = trackPlusLabelHeight(track, fontHeight);
+ sliceOffsetY = y;
+ curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
+ curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stSide,theSideImg,NULL,sliceWidth[stSide],sliceHeight,sliceOffsetX[stSide],sliceOffsetY);
+ curMap = sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot
+ }
+
+ if (track->drawLeftLabels != NULL)
+ y = doOwnLeftLabels(track, hvgSide, font, y);
+ else
+ y += trackPlusLabelHeight(track, fontHeight);
+ }
+ }
+
+
+/* Make map background. */
+y = yAfterRuler;
+for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
+ {
+ track = flatTrack->track;
+ if (track->limitedVis != tvHide)
+ {
+ if(theImgBox)
+ {
+ // Set imgTrack in case any map items will be set
+ sliceHeight = trackPlusLabelHeight(track, fontHeight);
+ sliceOffsetY = y;
+ curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL);
+ }
+ y = doTrackMap(track, hvg, y, fontHeight, trackPastTabX, trackPastTabWidth);
+ }
+ }
+
+/* Finish map. */
+hPrintf(" \n");
+
+hPrintf(" \n", dragZooming ? 1 : 0);
+if(rulerClickHeight)
+ {
+ hPrintf(" \n", rulerClickHeight);
+ }
+if(newWinWidth)
+ {
+ hPrintf(" \n", newWinWidth);
+ }
+
+/* Save out picture and tell html file about it. */
+if(hvgSide != hvg)
+ hvGfxClose(&hvgSide);
+hvGfxClose(&hvg);
+if(theImgBox)
+ {
+ imageBoxDraw(theImgBox);
+ #ifdef IMAGEv2_DRAG_SCROLL
+ // If a portal was established, then set the global dimensions back to the portal size
+ if(imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&winStart,&winEnd,&(tl.picWidth),NULL))
+ {
+ pixWidth = tl.picWidth;
+ winBaseCount = winEnd - winStart;
+ insideWidth = tl.picWidth-gfxBorder-insideX;
+ }
+ #endif//def IMAGEv2_DRAG_SCROLL
+ imgBoxFree(&theImgBox);
+ }
+else
+ {
+ char *titleAttr = dragZooming ? "title='click or drag mouse in base position track to zoom in'" : "";
+ hPrintf(" \n");
+ }
+flatTracksFree(&flatTracks);
+}
+
+static void printEnsemblAnchor(char *database, char* archive,
+ char *chrName, int start, int end)
+/* Print anchor to Ensembl display on same window. */
+{
+char *scientificName = hScientificName(database);
+char *dir = ensOrgNameFromScientificName(scientificName);
+struct dyString *ensUrl;
+char *name;
+int localStart, localEnd;
+
+name = chrName;
+
+if (sameWord(scientificName, "Takifugu rubripes"))
+ {
+ /* for Fugu, must give scaffold, not chr coordinates */
+ /* Also, must give "chrom" as "scaffold_N", name below. */
+ if (differentWord(chromName,"chrM") &&
+ !hScaffoldPos(database, chromName, winStart, winEnd,
+ &name, &localStart, &localEnd))
+ /* position doesn't appear on Ensembl browser.
+ * Ensembl doesn't show scaffolds < 2K */
+ return;
+ }
+else if (sameWord(scientificName, "Gasterosteus aculeatus"))
+ {
+ if (differentWord("chrM", chrName))
+ {
+ char *fixupName = replaceChars(chrName, "chr", "group");
+ name = fixupName;
+ }
+ }
+else if (sameWord(scientificName, "Ciona intestinalis"))
+ {
+ if (stringIn("chr0", chrName))
+ {
+ char *fixupName = replaceChars(chrName, "chr0", "chr");
+ name = fixupName;
+ }
+ }
+else if (sameWord(scientificName, "Saccharomyces cerevisiae"))
+ {
+ if (stringIn("2micron", chrName))
+ {
+ char *fixupName = replaceChars(chrName, "2micron", "2-micron");
+ name = fixupName;
+ }
+ }
+
+if (sameWord(chrName, "chrM"))
+ name = "chrMt";
+localStart = start;
+localEnd = end + 1; // Ensembl base-1 display coordinates
+ensUrl = ensContigViewUrl(database, dir, name, seqBaseCount, localStart, localEnd, archive);
+hPrintf("", ensUrl->string);
+/* NOTE: you can not freeMem(dir) because sometimes it is a literal
+ * constant */
+freeMem(scientificName);
+dyStringFree(&ensUrl);
+}
+
+void makeHgGenomeTrackVisible(struct track *track)
+/* This turns on a track clicked from hgGenome, even if it was previously */
+/* hidden manually and there are cart vars to support that. */
+{
+struct hashEl *hels;
+struct hashEl *hel;
+char prefix[SMALLBUF];
+/* First check if the click was from hgGenome. If not, leave. */
+/* get the names of the tracks in the cart */
+safef(prefix, sizeof(prefix), "%s_", hggGraphPrefix);
+hels = cartFindPrefix(cart, prefix);
+/* loop through them and compare them to the track passed into this */
+/* function. */
+for (hel = hels; hel != NULL; hel = hel->next)
+ {
+ struct trackDb *subtrack;
+ char *subtrackName = hel->val;
+ /* check non-subtrack. */
+ if (sameString(track->tdb->track, subtrackName))
+ {
+ track->visibility = tvFull;
+ track->tdb->visibility = tvFull;
+ cartSetString(cart, track->tdb->track, "full");
+ }
+ else if (track->tdb->subtracks != NULL)
+ {
+ struct slRef *tdbRef, *tdbRefList = trackDbListGetRefsToDescendants(track->tdb->subtracks);
+ for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
+ {
+ subtrack = tdbRef->val;
+ if (sameString(subtrack->track, subtrackName))
+ {
+ char selName[SMALLBUF];
+ track->visibility = tvFull;
+ cartSetString(cart, track->tdb->track, "full");
+ track->tdb->visibility = tvFull;
+ subtrack->visibility = tvFull;
+ safef(selName, sizeof(selName), "%s_sel", subtrackName);
+ cartSetBoolean(cart, selName, TRUE);
+ }
+ }
+ slFreeList(&tdbRefList);
+ }
+ }
+hashElFreeList(&hels);
+}
+
+struct sqlConnection *remoteTrackConnection(struct track *tg)
+/* Get a connection to remote database as specified in remoteSql settings... */
+{
+if (!tg->isRemoteSql)
+ {
+ internalErr();
+ return NULL;
+ }
+else
+ {
+ return sqlConnectRemote(tg->remoteSqlHost, tg->remoteSqlUser, tg->remoteSqlPassword,
+ tg->remoteSqlDatabase);
+ }
+}
+
+void addTdbListToTrackList(struct trackDb *tdbList, char *trackNameFilter,
+ struct track **pTrackList)
+/* Convert a list of trackDb's to tracks, and append these to trackList. */
+{
+struct trackDb *tdb, *next;
+struct track *track;
+TrackHandler handler;
+tdbSortPrioritiesFromCart(cart, &tdbList);
+for (tdb = tdbList; tdb != NULL; tdb = next)
+ {
+ next = tdb->next;
+ if(trackNameFilter != NULL && strcmp(trackNameFilter, tdb->track))
+ // suppress loading & display of all tracks except for the one passed in via trackNameFilter
+ continue;
+ track = trackFromTrackDb(tdb);
+ track->hasUi = TRUE;
+ if (slCount(tdb->subtracks) != 0)
+ {
+ tdbSortPrioritiesFromCart(cart, &(tdb->subtracks));
+ if (trackDbLocalSetting(tdb, "compositeTrack"))
+ makeCompositeTrack(track, tdb);
+ else if (trackDbLocalSetting(tdb, "container"))
+ makeContainerTrack(track, tdb);
+ }
+ else
+ {
+ handler = lookupTrackHandler(tdb->table);
+ if (handler != NULL)
+ handler(track);
+ }
+ if (cgiVarExists("hgGenomeClick"))
+ makeHgGenomeTrackVisible(track);
+ if (track->loadItems == NULL)
+ warn("No load handler for %s; possible missing trackDb `type' or `subTrack' attribute", tdb->track);
+ else if (track->drawItems == NULL)
+ warn("No draw handler for %s", tdb->track);
+ else
+ slAddHead(pTrackList, track);
+ }
+}
+
+void loadFromTrackDb(struct track **pTrackList)
+/* Load tracks from database, consulting handler list. */
+{
+char *trackNameFilter = cartOptionalString(cart, "hgt.trackNameFilter");
+struct trackDb *tdbList;
+if(trackNameFilter == NULL)
+ tdbList = hTrackDb(database, chromName);
+else
+ tdbList = hTrackDbForTrack(database, trackNameFilter);
+addTdbListToTrackList(tdbList, trackNameFilter, pTrackList);
+}
+
+static int getScoreFilter(char *trackName)
+/* check for score filter configuration setting */
+{
+char optionScoreStr[256];
+
+safef(optionScoreStr, sizeof(optionScoreStr), "%s.scoreFilter", trackName);
+return cartUsualInt(cart, optionScoreStr, 0);
+}
+
+void ctLoadSimpleBed(struct track *tg)
+/* Load the items in one custom track - just move beds in
+ * window... */
+{
+struct customTrack *ct = tg->customPt;
+struct bed *bed, *nextBed, *list = NULL;
+int scoreFilter = getScoreFilter(ct->tdb->track);
+
+if (ct->dbTrack)
+ {
+ int fieldCount = ct->fieldCount;
+ int rowOffset;
+ char **row;
+ struct sqlConnection *conn =
+ hAllocConn(CUSTOM_TRASH);
+ struct sqlResult *sr = NULL;
+
+ sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
+ NULL, &rowOffset);
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ bed = bedLoadN(row+rowOffset, fieldCount);
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ slAddHead(&list, bed);
+ }
+ hFreeConn(&conn);
+ }
+else
+ {
+ for (bed = ct->bedList; bed != NULL; bed = nextBed)
+ {
+ nextBed = bed->next;
+ if (bed->chromStart < winEnd && bed->chromEnd > winStart
+ && sameString(chromName, bed->chrom))
+ {
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ slAddHead(&list, bed);
+ }
+ }
+ }
+slSort(&list, bedCmp);
+tg->items = list;
+}
+
+void ctLoadBed9(struct track *tg)
+/* Convert bed info in window to linked feature. */
+{
+struct customTrack *ct = tg->customPt;
+struct bed *bed;
+struct linkedFeatures *lfList = NULL, *lf;
+boolean useItemRgb = FALSE;
+int scoreFilter = getScoreFilter(ct->tdb->track);
+
+useItemRgb = bedItemRgb(ct->tdb);
+
+if (ct->dbTrack)
+ {
+ int rowOffset;
+ char **row;
+ struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
+ struct sqlResult *sr = NULL;
+
+ sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
+ NULL, &rowOffset);
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ bed = bedLoadN(row+rowOffset, 9);
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ bed8To12(bed);
+ lf = lfFromBed(bed);
+ if (useItemRgb)
+ {
+ lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
+ lf->filterColor=bed->itemRgb;
+ }
+ slAddHead(&lfList, lf);
+ }
+ hFreeConn(&conn);
+ }
+else
+ {
+ for (bed = ct->bedList; bed != NULL; bed = bed->next)
+ {
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ if (bed->chromStart < winEnd && bed->chromEnd > winStart
+ && sameString(chromName, bed->chrom))
+ {
+ bed8To12(bed);
+ lf = lfFromBed(bed);
+ if (useItemRgb)
+ {
+ lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
+ lf->filterColor=bed->itemRgb;
+ }
+ slAddHead(&lfList, lf);
+ }
+ }
+ }
+slReverse(&lfList);
+slSort(&lfList, linkedFeaturesCmp);
+tg->items = lfList;
+}
+
+
+void ctLoadBed8(struct track *tg)
+/* Convert bed info in window to linked feature. */
+{
+struct customTrack *ct = tg->customPt;
+struct bed *bed;
+struct linkedFeatures *lfList = NULL, *lf;
+int scoreFilter = getScoreFilter(ct->tdb->track);
+
+if (ct->dbTrack)
+ {
+ int fieldCount = ct->fieldCount;
+ int rowOffset;
+ char **row;
+ struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
+ struct sqlResult *sr = NULL;
+
+ sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
+ NULL, &rowOffset);
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ bed = bedLoadN(row+rowOffset, fieldCount);
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ bed8To12(bed);
+ lf = lfFromBed(bed);
+ slAddHead(&lfList, lf);
+ }
+ hFreeConn(&conn);
+ }
+else
+ {
+ for (bed = ct->bedList; bed != NULL; bed = bed->next)
+ {
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ if (bed->chromStart < winEnd && bed->chromEnd > winStart
+ && sameString(chromName, bed->chrom))
+ {
+ bed8To12(bed);
+ lf = lfFromBed(bed);
+ slAddHead(&lfList, lf);
+ }
+ }
+ }
+slReverse(&lfList);
+slSort(&lfList, linkedFeaturesCmp);
+tg->items = lfList;
+}
+
+void ctLoadGappedBed(struct track *tg)
+/* Convert bed info in window to linked feature. */
+{
+struct customTrack *ct = tg->customPt;
+struct bed *bed;
+struct linkedFeatures *lfList = NULL, *lf;
+boolean useItemRgb = FALSE;
+int scoreFilter = getScoreFilter(ct->tdb->track);
+
+useItemRgb = bedItemRgb(ct->tdb);
+
+if (ct->dbTrack)
+ {
+ int fieldCount = ct->fieldCount;
+ int rowOffset;
+ char **row;
+ struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
+ struct sqlResult *sr = NULL;
+
+ sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
+ NULL, &rowOffset);
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ bed = bedLoadN(row+rowOffset, fieldCount);
+ lf = lfFromBed(bed);
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ if (useItemRgb)
+ {
+ lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
+ lf->filterColor=bed->itemRgb;
+ }
+ slAddHead(&lfList, lf);
+ }
+ hFreeConn(&conn);
+ }
+else
+ {
+ for (bed = ct->bedList; bed != NULL; bed = bed->next)
+ {
+ if (scoreFilter && bed->score < scoreFilter)
+ continue;
+ if (bed->chromStart < winEnd && bed->chromEnd > winStart
+ && sameString(chromName, bed->chrom))
+ {
+ lf = lfFromBed(bed);
+ if (useItemRgb)
+ {
+ lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
+ lf->filterColor=bed->itemRgb;
+ }
+ slAddHead(&lfList, lf);
+ }
+ }
+ }
+slReverse(&lfList);
+slSort(&lfList, linkedFeaturesCmp);
+tg->items = lfList;
+}
+
+void ctLoadColoredExon(struct track *tg)
+/* Convert bed info in window to linked features series for custom track. */
+{
+struct customTrack *ct = tg->customPt;
+struct bed *bed;
+struct linkedFeaturesSeries *lfsList = NULL, *lfs;
+if (ct->dbTrack)
+ {
+ int fieldCount = ct->fieldCount;
+ int rowOffset;
+ char **row;
+ struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
+ struct sqlResult *sr = NULL;
+ sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
+ NULL, &rowOffset);
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ bed = bedLoadN(row+rowOffset, fieldCount);
+ lfs = lfsFromColoredExonBed(bed);
+ slAddHead(&lfsList, lfs);
+ }
+ hFreeConn(&conn);
+ }
+else
+ {
+ for (bed = ct->bedList; bed != NULL; bed = bed->next)
+ {
+ if (bed->chromStart < winEnd && bed->chromEnd > winStart
+ && sameString(chromName, bed->chrom))
+ {
+ lfs = lfsFromColoredExonBed(bed);
+ slAddHead(&lfsList, lfs);
+ }
+ }
+ }
+slReverse(&lfsList);
+slSort(&lfsList, linkedFeaturesSeriesCmp);
+tg->items = lfsList;
+}
+
+char *ctMapItemName(struct track *tg, void *item)
+/* Return composite item name for custom tracks. */
+{
+char *itemName = tg->itemName(tg, item);
+static char buf[256];
+if (strlen(itemName) > 0)
+ sprintf(buf, "%s %s", ctFileName, itemName);
+else
+ sprintf(buf, "%s NoItemName", ctFileName);
+return buf;
+}
+
+
+void coloredExonMethodsFromCt(struct track *tg)
+/* same as coloredExonMethods but different loader. */
+{
+linkedFeaturesSeriesMethods(tg);
+tg->loadItems = ctLoadColoredExon;
+tg->canPack = TRUE;
+}
+
+struct track *newCustomTrack(struct customTrack *ct)
+/* Make up a new custom track. */
+{
+struct track *tg = NULL;
+struct trackDb *tdb = ct->tdb;
+boolean useItemRgb = FALSE;
+char *typeOrig = tdb->type;
+char *typeDupe = cloneString(typeOrig);
+char *typeParam = typeDupe;
+char *type = nextWord(&typeParam);
+
+if (ct->dbTrack)
+ {
+ // make sure we can connect
+ struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
+ hFreeConn(&conn);
+ }
+
+useItemRgb = bedItemRgb(tdb);
+
+if (sameString(type, "maf"))
+ {
+ tg = trackFromTrackDb(tdb);
+ tg->canPack = TRUE;
+
+ wigMafMethods(tg, tdb, 0, NULL);
+ if (!ct->dbTrack)
+ errAbort("custom maf tracks must be in database");
+
+ struct mafPriv *mp;
+ AllocVar(mp);
+ mp->ct = ct;
+
+ tg->customPt = mp;
+ tg->nextItemButtonable = FALSE;
+ }
+else if (sameString(type, "wig"))
+ {
+ tg = trackFromTrackDb(tdb);
+ if (ct->dbTrack)
+ tg->loadItems = wigLoadItems;
+ else
+ tg->loadItems = ctWigLoadItems;
+ tg->customPt = ct;
+ tg->nextItemButtonable = FALSE;
+ }
+else if (sameString(type, "bigWig"))
+ {
+ tg = trackFromTrackDb(tdb);
+ tg->bbiFile = ct->bbiFile;
+ tg->nextItemButtonable = FALSE;
+ }
+else if (sameString(type, "bigBed"))
+ {
+ struct bbiFile *bbi = ct->bbiFile;
+
+ /* Find field counts, and from that revise the tdb->type to be more complete. */
+ char extra = (bbi->fieldCount > bbi->definedFieldCount ? '+' : '.');
+ char typeBuf[64];
+ safef(typeBuf, sizeof(typeBuf), "bigBed %d %c", bbi->definedFieldCount, extra);
+ tdb->type = cloneString(typeBuf);
+
+ /* Finish wrapping track around tdb. */
+ tg = trackFromTrackDb(tdb);
+ tg->bbiFile = bbi;
+ tg->nextItemButtonable = FALSE;
+ }
+else if (sameString(type, "bedGraph"))
+ {
+ tg = trackFromTrackDb(tdb);
+ tg->canPack = FALSE;
+ tg->customPt = ct;
+ ct->wigFile = ctFileName;
+ tg->mapItemName = ctMapItemName;
+ tg->nextItemButtonable = FALSE;
+ }
+else if (sameString(type, "bed"))
+ {
+ tg = trackFromTrackDb(tdb);
+ if (ct->fieldCount < 8)
+ {
+ tg->loadItems = ctLoadSimpleBed;
+ }
+ else if (useItemRgb && ct->fieldCount == 9)
+ {
+ tg->loadItems = ctLoadBed9;
+ }
+ else if (ct->fieldCount < 12)
+ {
+ tg->loadItems = ctLoadBed8;
+ }
+ else if (ct->fieldCount == 15)
+ {
+ char *theType = trackDbSetting(tdb, "type");
+ if (theType && sameString(theType, "expRatio"))
+ {
+ tg = trackFromTrackDb(tdb);
+ expRatioMethodsFromCt(tg);
+ }
+ else
+ tg->loadItems = ctLoadGappedBed;
+ }
+ else
+ {
+ tg->loadItems = ctLoadGappedBed;
+ }
+ tg->mapItemName = ctMapItemName;
+ tg->canPack = TRUE;
+ tg->nextItemButtonable = TRUE;
+ tg->customPt = ct;
+ }
+else if (sameString(type, "chromGraph"))
+ {
+ tdb->type = NULL; /* Swap out type for the moment. */
+ tg = trackFromTrackDb(tdb);
+ chromGraphMethodsCt(tg);
+ tg->nextItemButtonable = FALSE;
+ tdb->type = typeOrig;
+ }
+else if (sameString(type, "array"))
+ {
+ tg = trackFromTrackDb(tdb);
+ expRatioMethodsFromCt(tg);
+ tg->nextItemButtonable = TRUE;
+ tg->customPt = ct;
+ }
+else if (sameString(type, "coloredExon"))
+ {
+ tg = trackFromTrackDb(tdb);
+ coloredExonMethodsFromCt(tg);
+ tg->nextItemButtonable = TRUE;
+ tg->customPt = ct;
+ }
+else if (sameString(type, "encodePeak"))
+ {
+ tg = trackFromTrackDb(tdb);
+ encodePeakMethodsCt(tg);
+ tg->nextItemButtonable = TRUE;
+ tg->customPt = ct;
+ }
+else if (sameString(type, "bam"))
+ {
+ tg = trackFromTrackDb(tdb);
+ tg->customPt = ct;
+ bamMethods(tg);
+ tg->mapItemName = ctMapItemName;
+ hashAdd(tdb->settingsHash, BASE_COLOR_USE_SEQUENCE, cloneString("lfExtra"));
+ hashAdd(tdb->settingsHash, BASE_COLOR_DEFAULT, cloneString("diffBases"));
+ hashAdd(tdb->settingsHash, SHOW_DIFF_BASES_ALL_SCALES, cloneString("."));
+ hashAdd(tdb->settingsHash, INDEL_DOUBLE_INSERT, cloneString("on"));
+ hashAdd(tdb->settingsHash, INDEL_QUERY_INSERT, cloneString("on"));
+ hashAdd(tdb->settingsHash, INDEL_POLY_A, cloneString("on"));
+ hashAdd(tdb->settingsHash, "showDiffBasesMaxZoom", cloneString("100"));
+ }
+else if (sameString(type, "makeItems"))
+ {
+ tg = trackFromTrackDb(tdb);
+ makeItemsMethods(tg);
+ tg->nextItemButtonable = TRUE;
+ tg->customPt = ct;
+ }
+else if (sameString(type, "bedDetail"))
+ {
+ tg = trackFromTrackDb(tdb);
+ bedDetailCtMethods(tg, ct);
+ tg->mapItemName = ctMapItemName; /* must be here to see ctMapItemName */
+ }
+else if (sameString(type, "pgSnp"))
+ {
+ tg = trackFromTrackDb(tdb);
+ pgSnpCtMethods(tg);
+ //tg->mapItemName = ctMapItemName;
+ tg->customPt = ct;
+ }
+else
+ {
+ errAbort("Unrecognized custom track type %s", type);
+ }
+if (!ct->dbTrack)
+ tg->nextItemButtonable = FALSE;
+tg->hasUi = TRUE;
+freez(&typeDupe);
+return tg;
+}
+
+char *getPositionFromCustomTracks()
+/* Parses custom track data to get the position variable
+ * return - The first chromosome position variable found in the
+ * custom track data. */
+{
+char *pos = NULL;
+struct slName *bl = NULL;
+
+ctList = customTracksParseCart(database, cart, &browserLines, &ctFileName);
+
+for (bl = browserLines; bl != NULL; bl = bl->next)
+ {
+ char *words[96];
+ int wordCount;
+ char *dupe = cloneString(bl->name);
+
+ wordCount = chopLine(dupe, words);
+ if (wordCount >= 3)
+ {
+ char *command = words[1];
+ if (sameString(command, "position"))
+ pos = cloneString(words[2]);
+ }
+ freez(&dupe);
+ if (pos != NULL)
+ break;
+ }
+return pos;
+}
+
+void loadCustomTracks(struct track **pTrackList)
+/* Load up custom tracks and append to list. */
+{
+struct customTrack *ct;
+struct track *tg;
+struct slName *bl;
+
+/* build up browser lines from cart variables set by hgCustom */
+char *visAll = cartCgiUsualString(cart, "hgt.visAllFromCt", NULL);
+if (visAll)
+ {
+ char buf[SMALLBUF];
+ safef(buf, sizeof buf, "browser %s %s", visAll, "all");
+ slAddTail(&browserLines, slNameNew(buf));
+ }
+struct hashEl *visEl;
+struct hashEl *visList = cartFindPrefix(cart, "hgtct.");
+for (visEl = visList; visEl != NULL; visEl = visEl->next)
+ {
+ char buf[256];
+ safef(buf, sizeof buf, "browser %s %s", cartString(cart, visEl->name),
+ chopPrefix(cloneString(visEl->name)));
+ slAddTail(&browserLines, slNameNew(buf));
+ cartRemove(cart, visEl->name);
+ }
+hashElFreeList(&visList);
+
+/* The loading is now handled by getPositionFromCustomTracks(). */
+/* Process browser commands in custom track. */
+for (bl = browserLines; bl != NULL; bl = bl->next)
+ {
+ char *words[96];
+ int wordCount;
+
+ wordCount = chopLine(bl->name, words);
+ if (wordCount > 1)
+ {
+ char *command = words[1];
+ if (sameString(command, "hide")
+ || sameString(command, "dense")
+ || sameString(command, "pack")
+ || sameString(command, "squish")
+ || sameString(command, "full"))
+ {
+ if (wordCount > 2)
+ {
+ int i;
+ for (i=2; inext)
+ {
+ if (toAll || sameString(s, tg->track))
+ {
+ if (hTvFromString(command) == tg->tdb->visibility)
+ /* remove if setting to default vis */
+ cartRemove(cart, tg->track);
+ else
+ cartSetString(cart, tg->track, command);
+ /* hide or show supertrack enclosing this track */
+ if (tdbIsSuperTrackChild(tg->tdb))
+ {
+ assert(tg->tdb->parentName != NULL);
+ cartSetString(cart, tg->tdb->parentName,
+ (sameString(command, "hide") ?
+ "hide" : "show"));
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (sameString(command, "position"))
+ {
+ if (wordCount < 3)
+ errAbort("Expecting 3 words in browser position line");
+ if (!hgIsChromRange(database, words[2]))
+ errAbort("browser position needs to be in chrN:123-456 format");
+ hgParseChromRange(database, words[2], &chromName, &winStart, &winEnd);
+
+ /*Fix a start window of -1 that is returned when a custom track position
+ begins at 0
+ */
+ if (winStart < 0)
+ {
+ winStart = 0;
+ }
+ }
+ else if (sameString(command, "pix"))
+ {
+ if (wordCount != 3)
+ errAbort("Expecting 3 words in pix line");
+ trackLayoutSetPicWidth(&tl, words[2]);
+ }
+ }
+ }
+for (ct = ctList; ct != NULL; ct = ct->next)
+ {
+ hasCustomTracks = TRUE;
+ tg = newCustomTrack(ct);
+ slAddHead(pTrackList, tg);
+ }
+}
+
+void addTracksFromDataHub(char *hubUrl, struct track **pTrackList)
+/* Load up stuff from data hub and append to list. The hubUrl points to
+ * a trackDb.ra format file. */
+{
+/* Squirrel away hub directory for later. */
+char hubDir[PATH_LEN];
+splitPath(hubUrl, hubDir, NULL, NULL);
+
+/* Load trackDb.ra file and make it into proper trackDb tree */
+struct trackDb *tdb, *tdbList = trackDbFromRa(hubUrl);
+for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
+ {
+ trackDbFieldsFromSettings(tdb);
+ trackDbPolish(tdb);
+ }
+trackDbLinkUpGenerations(tdbList);
+addTdbListToTrackList(tdbList, NULL, pTrackList);
+}
+
+void loadDataHubs(struct track **pTrackList)
+/* Load up stuff from data hubs and append to list. */
+{
+char *dataHubs = cloneString(cartUsualString(cart, "dataHubs", NULL));
+if (dataHubs == NULL)
+ return;
+int hubCount = chopByWhite(dataHubs, NULL, 10);
+char *hubArrays[hubCount];
+chopByWhite(dataHubs, hubArrays, hubCount);
+int i;
+for (i = 0; i%u",
+chromName, winEnd, winStart);
+sr = sqlGetResult(conn, query);
+
+int itemCount = 0;
+struct agpFrag *agpItem = NULL;
+while ((row = sqlNextRow(sr)) != NULL)
+ {
+ agpFragFree(&agpItem); // if there is a second one
+ agpItem = agpFragLoad(row+1);
+ ++itemCount;
+ if (itemCount > 1)
+ break;
+ }
+sqlFreeResult(&sr);
+hFreeConn(&conn);
+if (1 == itemCount)
+ { // verify *entirely* within single contig
+ if ((winEnd <= agpItem->chromEnd) &&
+ (winStart >= agpItem->chromStart))
+ {
+ int agpStart = winStart - agpItem->chromStart;
+ int agpEnd = agpStart + winEnd - winStart;
+ hPuts("");
+ printEnsemblAnchor(database, archive, agpItem->frag,
+ agpStart, agpEnd);
+ hPrintf("%s ", "Ensembl");
+ }
+ }
+agpFragFree(&agpItem); // the one we maybe used
+}
+
+void hotLinks()
+/* Put up the hot links bar. */
+{
+boolean gotBlat = hIsBlatIndexedDatabase(database);
+struct dyString *uiVars = uiStateUrlPart(NULL);
+char *orgEnc = cgiEncode(organism);
+boolean psOutput = cgiVarExists("hgt.psOutput");
+
+hPrintf("\n");
+hPrintf("\n");
+hPrintf("\n");
+hPrintf("Home ",
+ orgEnc, database, cartSessionVarName(), cartSessionId(cart));
+
+if (hIsGisaidServer())
+ {
+ /* disable hgGateway for gisaid for now */
+ //hPrintf(" Sequence View Gateway ", orgEnc, database);
+ hPrintf(
+ " %s ",
+ '%', '%',
+ cartSessionVarName(),
+ cartSessionId(cart),
+ "Select Subjects");
+ }
+else
+if (hIsGsidServer())
+ {
+ hPrintf(" Sequence View Gateway ", orgEnc, database);
+ hPrintf(
+ " %s ",
+ '%', '%', "Select Subjects");
+ }
+else
+ {
+ hPrintf(" Genomes ", orgEnc, database, cartSessionVarName(), cartSessionId(cart));
+ }
+if (psOutput)
+ {
+ hPrintf(" Genome Browser ", orgEnc, database, cartSessionVarName(), cartSessionId(cart));
+ }
+if (gotBlat)
+ {
+ hPrintf(" Blat ", uiVars->string);
+ }
+if (hIsGisaidServer())
+ {
+ hPrintf(" %s ",
+ database,
+ cartSessionVarName(),
+ cartSessionId(cart),
+ "Table View");
+ }
+else if (hIsGsidServer())
+ {
+ hPrintf(" %s ",
+ database, "Table View");
+ }
+else
+ {
+ /* disable TB for CGB servers */
+ if (!hIsCgbServer())
+ {
+ hPrintf(" %s ",
+ database, chromName, winStart+1, winEnd,
+ cartSessionVarName(),
+ cartSessionId(cart),
+ "Tables");
+ }
+ }
+
+if (hgNearOk(database))
+ {
+ hPrintf(" %s ",
+ uiVars->string, "Gene Sorter");
+ }
+if (hgPcrOk(database))
+ {
+ hPrintf(" PCR ", uiVars->string);
+ }
+if (!psOutput)
+ {
+ hPrintf(" "
+ " %s ", 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(" string, database, chromName, winStart+1, winEnd);
+ hPrintf("\" class=\"topbar\">Convert ");
+ }
+ }
+
+/* see if hgFixed.trackVersion exists */
+boolean trackVersionExists = hTableExists("hgFixed", "trackVersion");
+char ensVersionString[256];
+char ensDateReference[256];
+ensVersionString[0] = 0;
+ensDateReference[0] = 0;
+if (trackVersionExists)
+ {
+ struct sqlConnection *conn = hAllocConn("hgFixed");
+ char query[256];
+ safef(query, sizeof(query), "select version,dateReference from hgFixed.trackVersion where db = '%s' order by updateTime DESC limit 1", database);
+ struct sqlResult *sr = sqlGetResult(conn, query);
+ char **row;
+
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ safef(ensVersionString, sizeof(ensVersionString), "Ensembl %s",
+ row[0]);
+ safef(ensDateReference, sizeof(ensDateReference), "%s",
+ row[1]);
+ }
+ sqlFreeResult(&sr);
+ hFreeConn(&conn);
+ }
+
+if (!psOutput)
+ {
+ if (differentWord(database,"susScr2"))
+ {
+ /* Print Ensembl anchor for latest assembly of organisms we have
+ * supported by Ensembl == if versionString from trackVersion exists */
+ if (sameWord(database,"hg19"))
+ {
+ hPrintf(" ");
+ printEnsemblAnchor(database, NULL, chromName, winStart, winEnd);
+ hPrintf("%s ", "Ensembl");
+ }
+ else if (sameWord(database,"hg18"))
+ {
+ hPrintf(" ");
+ printEnsemblAnchor(database, "ncbi36", chromName, winStart, winEnd);
+ hPrintf("%s ", "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(" ");
+ printEnsemblAnchor(database, archive, ctgItem->contig,
+ ctgStart, ctgEnd);
+ hPrintf("%s ", "Ensembl");
+ }
+ }
+ ctgPosFree(&ctgItem); // the one we maybe used
+ }
+ }
+ else
+ {
+ hPrintf(" ");
+ printEnsemblAnchor(database, archive, chromName, winStart, winEnd);
+ hPrintf("%s ", "Ensembl");
+ }
+ }
+ }
+ }
+
+if (!psOutput)
+ {
+ /* Print NCBI MapView anchor */
+ if (sameString(database, "hg18"))
+ {
+ hPrintf(" ",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "hg19"))
+ {
+ hPrintf(" ",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "mm8"))
+ {
+ hPrintf("");
+ hPrintf("",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "danRer2"))
+ {
+ hPrintf("");
+ hPrintf("",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "galGal3"))
+ {
+ hPrintf("");
+ hPrintf("",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "canFam2"))
+ {
+ hPrintf("");
+ hPrintf("",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "rheMac2"))
+ {
+ hPrintf("");
+ hPrintf("",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "panTro2"))
+ {
+ hPrintf("");
+ hPrintf("",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (sameString(database, "anoGam1"))
+ {
+ hPrintf("");
+ hPrintf("",
+ skipChr(chromName), winStart+1, winEnd);
+ hPrintf("%s ", "NCBI");
+ }
+ if (startsWith("oryLat", database))
+ {
+ hPrintf(" %s ",
+ skipChr(chromName), winStart+1, winEnd, "UTGB");
+ }
+ if (sameString(database, "cb3"))
+ {
+ hPrintf(" %s ",
+ skipChr(chromName), winStart+1, winEnd, "WormBase");
+ }
+ if (sameString(database, "ce4"))
+ {
+ hPrintf(" %s ",
+ skipChr(chromName), winStart+1, winEnd, "WormBase");
+ }
+ if (sameString(database, "ce2"))
+ {
+ hPrintf(" %s ",
+ skipChr(chromName), winStart+1, winEnd, "WormBase");
+ }
+ }
+
+if (!psOutput)
+ {
+ hPrintf(" %s ",cartSessionVarName(),
+ cartSessionId(cart), "PDF/PS");
+ }
+
+if (!psOutput)
+ {
+ if (wikiLinkEnabled())
+ {
+ printf(" Session ",
+ cartSessionVarName(), cartSessionId(cart));
+ }
+ }
+if (hIsGisaidServer())
+ {
+ //hPrintf(" %s \n", "Help");
+ hPrintf(" %s \n", "Help");
+ }
+else
+if (hIsGsidServer())
+ {
+ hPrintf(" %s \n", "Help");
+ }
+else
+ {
+ hPrintf(" %s \n", "Help");
+ }
+hPuts("
");
+hPuts("
");
+hPuts("
\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 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;
+
+/* build group objects from database. */
+for (grp = grps; grp != NULL; grp = grp->next)
+ {
+ /* deal with group reordering */
+ float priority = grp->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);
+
+/* 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);
+ if (hashLookup(superHash, track->tdb->parentName))
+ /* ignore supertrack if it's already been handled */
+ continue;
+ /* create track and reference for the supertrack */
+ struct track *superTrack = trackFromTrackDb(track->tdb->parent);
+ 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, track->tdb->parent);
+ }
+ }
+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);
+#ifdef SOON
+loadDataHubs(&trackList);
+#endif /* SOON */
+loadCustomTracks(&trackList);
+groupTracks(&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 (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("", isOpen ? "" : "style='display: none' ", id, counter++);
+ cg->rowOpen = TRUE;
+ }
+if (cg->align)
+ printf("", cg->align);
+else
+ 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 dontLoadItems(struct track *tg)
+/* No-op loadItems when we aren't going to try. */
+{
+}
+
+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);
+}
+
+#ifdef SUBTRACKS_HAVE_VIS
+static 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)
+ {
+ if (tdbIsMultiTrack(track->tdb))
+ if(cartTdbTreeMatchSubtrackVis(cart,track->tdb)) // Note, this is done for found multi-track kids but composites are only straightened up in hgTrackUi
+ track->visibility = tdbVisLimitedByAncestry(cart,track->tdb,FALSE);
+
+ if (cartTdbTreeCleanupOverrides(track->tdb,newCart,oldVars))
+ { // Need to update track visibility
+ if (tdbIsSuperTrackChild(track->tdb))
+ {
+ // 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);
+ }
+ }
+ }
+ }
+}
+#endif///def SUBTRACKS_HAVE_VIS
+
+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("