86ab10b2e387dfeb4505329c84ad399d44fcc1d6 max Wed Jan 4 11:43:05 2017 -0800 changing UK to US spelling, refs #18513 diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index 24b926c..25bba1e 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -1,9722 +1,9722 @@ /* hgTracks - the /riginal, 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. */ /* Copyright (C) 2014 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include #include #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 "udc.h" #include "customTrack.h" #include "trackHub.h" #include "hubConnect.h" #include "cytoBand.h" #include "ensFace.h" #include "liftOver.h" #include "pcrResult.h" #include "jsHelper.h" #include "mafTrack.h" #include "hgConfig.h" #include "encode.h" #include "agpFrag.h" #include "imageV2.h" #include "suggest.h" #include "search.h" #include "errCatch.h" #include "iupac.h" #include "botDelay.h" #include "chromInfo.h" #include "extTools.h" #include "basicBed.h" #include "customFactory.h" #include "genbank.h" #include "bigWarn.h" #include "wigCommon.h" #include "knetUdc.h" /* Other than submit and Submit all these vars should start with hgt. * to avoid weeding things out of other program's namespaces. * Because the browser is a central program, most of its cart * variables are not hgt. qualified. It's a good idea if other * program's unique variables be qualified with a prefix though. */ char *excludeVars[] = { "submit", "Submit", "dirty", "hgt.reset", "hgt.in1", "hgt.in2", "hgt.in3", "hgt.inBase", "hgt.out1", "hgt.out2", "hgt.out3", "hgt.out4", "hgt.left1", "hgt.left2", "hgt.left3", "hgt.right1", "hgt.right2", "hgt.right3", "hgt.dinkLL", "hgt.dinkLR", "hgt.dinkRL", "hgt.dinkRR", "hgt.tui", "hgt.hideAll", "hgt.visAllFromCt", "hgt.psOutput", "hideControls", "hgt.toggleRevCmplDisp", "hgt.collapseGroups", "hgt.expandGroups", "hgt.suggest", "hgt.jump", "hgt.refresh", "hgt.setWidth", "hgt.trackImgOnly", "hgt.ideogramToo", "hgt.trackNameFilter", "hgt.imageV1", "hgt.suggestTrack", "hgt.setWidth", TRACK_SEARCH, TRACK_SEARCH_ADD_ROW, TRACK_SEARCH_DEL_ROW, TRACK_SEARCH_PAGER, "hgt.contentType", "hgt.positionInput", "hgt.internal", NULL }; /* 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 */ 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 */ struct hvGfx *hvgSide = NULL; // Extra pointer to a sideLabel image that can be built if needed 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; struct hash *oldVars = NULL; struct jsonElement *jsonForClient = 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, preview, or public browser */ char *organization; /* UCSC */ struct hash *trackHash = NULL; /* Hash of the tracks by their name. */ #ifdef DEBUG void uglySnoopTrackList(int depth, struct track *trackList) /* Print out some info on track list. */ { struct track *track; for (track = trackList; track != NULL; track = track->next) { if (stringIn("FaireH1h", track->track)) { repeatCharOut(uglyOut, '+', depth); } uglySnoopTrackList(depth+1, track->subtracks); } } #endif /* DEBUG */ struct track *trackFindByName(struct track *tracks, char *trackName) /* find a track in tracks by name, recursively searching subtracks */ { struct track *track; for (track = tracks; track != NULL; track = track->next) { if (sameString(track->track, trackName)) return track; else if (track->subtracks != NULL) { struct track *st = trackFindByName(track->subtracks, trackName); if (st != NULL) return st; } } return NULL; } int tgCmpPriority(const void *va, const void *vb) /* Compare to sort based on priority; use shortLabel as secondary sort key. */ { const struct track *a = *((struct track **)va); const struct track *b = *((struct track **)vb); float dif = 0; if (a->group && b->group) dif = a->group->priority - b->group->priority; if (dif == 0) dif = a->priority - b->priority; if (dif < 0) return -1; else if (dif == 0.0) /* secondary sort on label */ return strcasecmp(a->shortLabel, b->shortLabel); else return 1; } int trackRefCmpPriority(const void *va, const void *vb) /* Compare based on priority. */ { const struct trackRef *a = *((struct trackRef **)va); const struct trackRef *b = *((struct trackRef **)vb); return tgCmpPriority(&a->track, &b->track); } int gCmpPriority(const void *va, const void *vb) /* Compare groups based on priority. */ { const struct group *a = *((struct group **)va); const struct group *b = *((struct group **)vb); float dif = a->priority - b->priority; if (dif == 0) return 0; if (dif < 0) return -1; else if (dif == 0.0) return 0; else return 1; } void changeTrackVis(struct group *groupList, char *groupTarget, int changeVis) /* Change track visibilities. If groupTarget is * NULL then set visibility for tracks in all groups. Otherwise, * just set it for the given group. If vis is -2, then visibility is * unchanged. If -1 then set visibility to default, otherwise it should * be tvHide, tvDense, etc. * If we are going back to default visibility, then reset the track * ordering also. */ { struct group *group; if (changeVis == -2) return; for (group = groupList; group != NULL; group = group->next) { struct trackRef *tr; if (groupTarget == NULL || sameString(group->name,groupTarget)) { static char pname[512]; /* if default vis then reset group priority */ if (changeVis == -1) group->priority = group->defaultPriority; for (tr = group->trackList; tr != NULL; tr = tr->next) { struct track *track = tr->track; struct trackDb *tdb = track->tdb; if (changeVis == -1) // to default { if (tdbIsComposite(tdb)) { safef(pname, sizeof(pname),"%s.*",track->track); //to remove all settings associated with this composite! cartRemoveLike(cart,pname); struct track *subTrack; for (subTrack = track->subtracks;subTrack != NULL; subTrack = subTrack->next) { subTrack->visibility = tdb->visibility; cartRemove(cart, subTrack->track); } } /* restore defaults */ if (tdbIsSuperTrackChild(tdb) || tdbIsCompositeChild(tdb)) { assert(tdb->parent != NULL && tdb->parent->track); cartRemove(cart, tdb->parent->track); if (withPriorityOverride) { safef(pname, sizeof(pname), "%s.priority",tdb->parent->track); cartRemove(cart, pname); } } track->visibility = tdb->visibility; cartRemove(cart, track->track); /* set the track priority back to the default value */ if (withPriorityOverride) { safef(pname, sizeof(pname), "%s.priority",track->track); cartRemove(cart, pname); track->priority = track->defaultPriority; } } else // to changeVis value (Usually tvHide) { /* change to specified visibility */ if (tdbIsSuperTrackChild(tdb)) { assert(tdb->parent != NULL); /* Leave supertrack members alone -- only change parent */ struct trackDb *parentTdb = tdb->parent; if ((changeVis == tvHide && !parentTdb->isShow) || (changeVis != tvHide && parentTdb->isShow)) { /* remove if setting to default vis */ cartRemove(cart, parentTdb->track); } else cartSetString(cart, parentTdb->track, changeVis == tvHide ? "hide" : "show"); } else // Not super child { if (changeVis == tdb->visibility) /* remove if setting to default vis */ cartRemove(cart, track->track); else cartSetString(cart, track->track, hStringFromTv(changeVis)); track->visibility = changeVis; } // Whether super child or not, if its a composite, then handle the children if (tdbIsComposite(tdb)) { struct track *subtrack; for (subtrack=track->subtracks;subtrack!=NULL;subtrack=subtrack->next) { if (changeVis == tvHide) // Since subtrack level vis is an cartRemove(cart, subtrack->track); // override, simply remove to hide else cartSetString(cart, subtrack->track, hStringFromTv(changeVis)); subtrack->visibility = changeVis; } } } } } } slSort(&groupList, gCmpPriority); } int trackOffsetX() /* Return x offset where track display proper begins. */ { int x = gfxBorder; if (withLeftLabels) x += tl.leftLabelWidth + gfxBorder; return x; } static void mapBoxTrackUi(struct hvGfx *hvg, int x, int y, int width, int height, char *name, char *shortLabel, char *id) /* Print out image map rectangle that invokes hgTrackUi. */ { x = hvGfxAdjXW(hvg, x, width); char *url = trackUrl(name, chromName); if (theImgBox && curImgTrack) { struct imgSlice *curSlice = imgTrackSliceGetByType(curImgTrack,stButton); if (curSlice) sliceAddLink(curSlice,url,shortLabel); } else { hPrintf("\n"); } freeMem(url); } static void mapBoxToggleComplement(struct hvGfx *hvg, int x, int y, int width, int height, struct track *toggleGroup, char *chrom, int start, int end, char *message) /*print out a box along the DNA bases that toggles a cart variable * "complement" to complement the DNA bases at the top by the ruler*/ { struct dyString *ui = uiStateUrlPart(toggleGroup); x = hvGfxAdjXW(hvg, x, width); if (theImgBox && curImgTrack) { char link[512]; safef(link,sizeof(link),"%s?complement_%s=%d&%s", hgTracksName(), database, !cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE),ui->string); imgTrackAddMapItem(curImgTrack,link,(char *)(message != NULL?message:NULL),x, y, x+width, y+height, NULL); } else { hPrintf("string); freeDyString(&ui); if (message != NULL) mapStatusMessage("%s", message); hPrintf(">\n"); } } char *trackUrl(char *mapName, char *chromName) /* Return hgTrackUi url; chromName is optional. */ { char *encodedMapName = cgiEncode(mapName); char buf[2048]; char *hgTrackUi = hTrackUiForTrack(mapName); if(chromName == NULL) safef(buf, sizeof(buf), "%s?%s=%s&g=%s", hgTrackUi, cartSessionVarName(), cartSessionId(cart), encodedMapName); else safef(buf, sizeof(buf), "%s?%s=%s&c=%s&g=%s", hgTrackUi, cartSessionVarName(), cartSessionId(cart), chromName, encodedMapName); freeMem(encodedMapName); return(cloneString(buf)); } #ifdef REMOTE_TRACK_AJAX_CALLBACK static boolean trackUsesRemoteData(struct track *track) /* returns TRUE is this track has a remote datasource */ { if (!IS_KNOWN(track->remoteDataSource)) { SET_TO_NO(track->remoteDataSource); //if (track->bbiFile != NULL) // FIXME: Chicken or the egg. bigWig/bigBed "bbiFile" filled // // in by loadItems, but we don't want to load items. // { // if (!startsWith("/gbdb/",track->bbiFile->fileName)) // SET_TO_YES(track->remoteDataSource); // } if (startsWithWord("bigWig",track->tdb->type) || startsWithWord("bigBed",track->tdb->type) || startsWithWord("halSnake",track->tdb->type) || startsWithWord("bigPsl",track->tdb->type) || startsWithWord("bigGenePred",track->tdb->type) || startsWithWord("bigChain",track->tdb->type) || startsWithWord("bigMaf",track->tdb->type) || startsWithWord("bam",track->tdb->type) || startsWithWord("vcfTabix", track->tdb->type)) { SET_TO_YES(track->remoteDataSource); } } return IS_YES(track->remoteDataSource); } boolean trackShouldUseAjaxRetrieval(struct track *track) /* Tracks with remote data sources should berendered via an ajax callback */ { return (theImgBox && !trackImgOnly && trackUsesRemoteData(track)); } #endif///def REMOTE_TRACK_AJAX_CALLBACK static int trackPlusLabelHeight(struct track *track, int fontHeight) /* Return the sum of heights of items in this track (or subtrack as it may be) * and the center label(s) above the items (if any). */ { if (trackShouldUseAjaxRetrieval(track)) return REMOTE_TRACK_HEIGHT; int y = track->totalHeight(track, limitVisibility(track)); if (isCenterLabelIncluded(track)) y += fontHeight; if (tdbIsComposite(track->tdb)) { struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack) && isCenterLabelIncluded(subtrack)) y += fontHeight; } } return y; } void drawColoredButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled, Color shades[]) /* draw button box, providing shades of the desired button color */ { int light = shades[1], mid = shades[2], dark = shades[4]; if (enabled) { hvGfxBox(hvg, x, y, w, 1, light); hvGfxBox(hvg, x, y+1, 1, h-1, light); hvGfxBox(hvg, x+1, y+1, w-2, h-2, mid); hvGfxBox(hvg, x+1, y+h-1, w-1, 1, dark); hvGfxBox(hvg, x+w-1, y+1, 1, h-1, dark); } else /* try to make the button look as if * it is already depressed */ { hvGfxBox(hvg, x, y, w, 1, dark); hvGfxBox(hvg, x, y+1, 1, h-1, dark); hvGfxBox(hvg, x+1, y+1, w-2, h-2, light); hvGfxBox(hvg, x+1, y+h-1, w-1, 1, light); hvGfxBox(hvg, x+w-1, y+1, 1, h-1, light); } } void drawGrayButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled) /* Draw a gray min-raised looking button. */ { drawColoredButtonBox(hvg, x, y, w, h, enabled, shadesOfGray); } void drawBlueButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled) /* Draw a blue min-raised looking button. */ { drawColoredButtonBox(hvg, x, y, w, h, enabled, shadesOfSea); } void drawButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled) /* Draw a standard (gray) min-raised looking button. */ { drawGrayButtonBox(hvg, x, y, w, h, enabled); } void beforeFirstPeriod( char *str ) { char *t = rindex( str, '.' ); if( t == NULL ) return; else str[strlen(str) - strlen(t)] = '\0'; } static void drawBases(struct hvGfx *hvg, int x, int y, int width, int height, Color color, MgFont *font, boolean complementSeq, struct dnaSeq *thisSeq) /* Draw evenly spaced bases. */ { struct dnaSeq *seq; if (thisSeq == NULL) seq = hDnaFromSeq(database, chromName, winStart, winEnd, dnaUpper); else seq = thisSeq; if (complementSeq) complement(seq->dna, seq->size); spreadBasesString(hvg, x, y, width, height, color, font, seq->dna, seq->size, FALSE); if (thisSeq == NULL) freeDnaSeq(&seq); } void drawComplementArrow( struct hvGfx *hvg, int x, int y, int width, int height, MgFont *font) /* Draw arrow and create clickbox for complementing ruler bases */ { boolean baseCmpl = cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE); // reverse arrow when base complement doesn't match display char *text = (baseCmpl == revCmplDisp) ? "--->" : "<---"; hvGfxTextRight(hvg, x, y, width, height, MG_BLACK, font, text); mapBoxToggleComplement(hvg, x, y, width, height, NULL, chromName, winStart, winEnd, "complement bases"); } struct track *chromIdeoTrack(struct track *trackList) /* Find chromosome ideogram track */ { struct track *track; for(track = trackList; track != NULL; track = track->next) { if(sameString(track->track, "cytoBandIdeo")) { if (hTableExists(database, track->table)) return track; else return NULL; } } return NULL; } void removeTrackFromGroup(struct track *track) /* Remove track from group it is part of. */ { struct trackRef *tr = NULL; for(tr = track->group->trackList; tr != NULL; tr = tr->next) { if(tr->track == track) { slRemoveEl(&track->group->trackList, tr); break; } } } void fillInStartEndBands(struct track *ideoTrack, char *startBand, char *endBand, int buffSize) /* Loop through the bands and fill in the one that the current window starts on and ends on. */ { struct cytoBand *cb = NULL, *cbList = ideoTrack->items; for(cb = cbList; cb != NULL; cb = cb->next) { /* If the start or end is encompassed by this band fill it in. */ if (winStart >= cb->chromStart && winStart <= cb->chromEnd) { safef(startBand, buffSize, "%s", cb->name); } /* End is > rather than >= due to odditiy in the cytoband track where the starts and ends of two bands overlaps by one. */ if (winEnd > cb->chromStart && winEnd <= cb->chromEnd) { safef(endBand, buffSize, "%s", cb->name); } } } boolean makeChromIdeoImage(struct track **pTrackList, char *psOutput, struct tempName *ideoTn) /* Make an ideogram image of the chromosome 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; int ideoWidth = round(.8 *tl.picWidth); int ideoHeight = 0; int textWidth = 0; struct tempName pngTn; boolean nukeIdeoFromList = FALSE; if (ideoTn == NULL) ideoTn = &pngTn; // not returning value ideoTrack = chromIdeoTrack(*pTrackList); /* If no ideogram don't draw. */ if(ideoTrack == NULL) { doIdeo = FALSE; } else if(trackImgOnly && !ideogramToo) { doIdeo = FALSE; } else { /* Remove the track from the group and track list. */ removeTrackFromGroup(ideoTrack); slRemoveEl(pTrackList, ideoTrack); nukeIdeoFromList = TRUE; /* 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; } // TODO use DIV in future (can update entire div at once in hgTracks.js) //hPrintf("
\n"); // FYI from testing, I see that there is code that inserts warning error messages // right before ideoMap, so any changes to that name or adding the DIV would require // updating the warning-insertion target name. if(doIdeo) { char startBand[16]; char endBand[16]; char title[64]; // was 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 { trashDirFile(ideoTn, "hgtIdeo", "hgtIdeo", ".png"); hvg = hvGfxOpenPng(ideoWidth, ideoHeight, ideoTn->forCgi, FALSE); } 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 (virtMode) { if (!sameString(virtModeShortDescr,"")) safef(title, sizeof(title), "%s (%s)", chromName, virtModeShortDescr); else safef(title, sizeof(title), "%s (%s)", chromName, virtModeType); } else if (isEmpty(startBand)) safef(title, sizeof(title), "%s", chromName); else 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); // cytoBandDrawAt() clips x based on insideX+insideWidth, // but in virtMode we may be in a window that is smaller than the ideo width // so temporarily set them to the actual ideo graphic offset and width int saveInsideX = insideX; int saveInsideWidth = insideWidth; insideX = textWidth+4; insideWidth = ideoWidth-insideX; ideoTrack->drawItems(ideoTrack, winStart, winEnd, hvg, insideX, gfxBorder, insideWidth, font, ideoTrack->ixColor, ideoTrack->limitedVis); insideX = saveInsideX; insideWidth = saveInsideWidth; hvGfxUnclip(hvg); /* Save out picture and tell html file about it. */ hvGfxClose(&hvg); /* Finish map. */ if (!psOutput) hPrintf("\n"); } // create an empty hidden-map place holder which can change dynamically with ajax callbacks. if (!doIdeo && !psOutput) { hPrintf("\n", mapName); hPrintf("\n"); } hPrintf(""); if (!psOutput) { // by default, create an empty hidden ideo place holder for future dynamic ajax update char *srcPath = ""; char *style = "display: none;"; if (doIdeo) { srcPath = ideoTn->forHtml; style = "display: inline;"; } hPrintf(""); hPrintf(""); hPrintf("
", srcPath, ideoWidth, ideoHeight, mapName, style); hPrintf("
\n"); } else hPrintf("\n"); //hPrintf("
\n"); // TODO use DIV in future if(ideoTrack != NULL) { ideoTrack->limitedVisSet = TRUE; ideoTrack->limitedVis = tvHide; /* Don't draw in main gif. */ } return nukeIdeoFromList; } 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: */ sqlSafef(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 = 103; 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"); tdb->canPack = tg->canPack; 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); iupacFilter(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; } char *stringInWrapper(char *needle, char *haystack) /* Wrapper around string in to make it so it's a function rather than a macro. */ { return stringIn(needle, haystack); } void oligoMatchLoad(struct track *tg) /* Create track of perfect matches to oligo on either strand. * * Note that if you are extending this code, there is also a parallel copy * in src/hg/utils/oligoMatch/ that should be kept up to date! * */ { char *dna = dnaInWindow(); char *fOligo = oligoMatchSeq(); char *(*finder)(char *needle, char *haystack) = (anyIupac(fOligo) ? iupacIn : stringInWrapper); 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 = finder(fOligo, dna); iupacReverseComplement(rOligo, oligoSize); if (sameString(rOligo, fOligo)) rOligo = NULL; else rMatch = finder(rOligo, dna); for (;;) { char *oneMatch = NULL; if (rMatch == NULL) { if (fMatch == NULL) break; else { oneMatch = fMatch; fMatch = finder(fOligo, fMatch+1); strand = '+'; } } else if (fMatch == NULL) { oneMatch = rMatch; rMatch = finder(rOligo, rMatch+1); strand = '-'; } else if (rMatch < fMatch) { oneMatch = rMatch; rMatch = finder(rOligo, rMatch+1); strand = '-'; } else { oneMatch = fMatch; fMatch = finder(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 = OLIGO_MATCH_TRACK_PRIORITY; 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; tdb->canPack = tg->canPack; trackDbPolish(tdb); tg->tdb = tdb; return tg; } Color maybeDarkerLabels(struct track *track, struct hvGfx *hvg, Color color) /* For tracks having light track display but needing a darker label */ { if (trackDbSetting(track->tdb, "darkerLabels")) { struct hsvColor hsv = mgRgbToHsv(mgColorIxToRgb(NULL, color)); // check if really pale if (hsv.s < 500 ||(hsv.h > 40.0 && hsv.h < 150.0)) return somewhatDarkerColor(hvg, color); return slightlyDarkerColor(hvg, color); } return color; } 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 newy; char o4[256]; char o5[256]; struct slList *item; enum trackVisibility vis = track->limitedVis; Color labelColor = (track->labelColor ? track->labelColor : track->ixColor); labelColor = maybeDarkerLabels(track, hvg, labelColor); 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("bigMaf", track->tdb->type) || startsWith("wigMaf", track->tdb->type) || startsWith("maf", track->tdb->type)) vis = tvFull; /* behave temporarily like pack for these */ if (track->limitedVis == tvFull && isTypeBedLike(track)) vis = tvPack; switch (vis) { case tvHide: break; /* Do nothing; */ case tvPack: case tvSquish: y += tHeight; break; case tvFull: if (isCenterLabelIncluded(track)) y += fontHeight; 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); //warn(" track %s, itemHeight %d\n", track->shortLabel, itemHeight); newy = y; if (track->itemLabelColor != NULL) labelColor = track->itemLabelColor(track, item, hvg); else labelColor = maybeDarkerLabels(track, hvg, labelColor); /* 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 ); 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; } hvGfxUnclip(hvg); return y; } void setGlobalsFromWindow(struct window *window); // FORWARD DECLARATION 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 = fullInsideWidth; int portX = fullInsideX; // If a portal was established, then set the portal dimensions long portalStart,chromStart; double basesPerPixel; // TODO GALT need to tweak it still for virtchrom stuff, e.g. maybe change some var names or types to long if (theImgBox && 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; } 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, track, FALSE, NULL, 0, 0, (revCmplDisp ? "Next item" : "Prev item"), buttonText); #ifdef IMAGEv2_SHORT_TOGGLE char *label = (theImgBox ? track->longLabel : parentTrack->longLabel); int width = portWidth - (2 * arrowButtonWidth); int x = portX + arrowButtonWidth; // make toggle cover only actual label int size = mgFontStringWidth(font,label) + 12; // get close enough to the label if (width > size) { x += width/2 - size/2; width = size; } mapBoxToggleVis(hvg, x, y + 1, width, insideHeight, (theImgBox ? track : parentTrack)); #else///ifndef IMAGEv2_SHORT_TOGGLE mapBoxToggleVis(hvg, portX + arrowButtonWidth, y + 1, portWidth - (2 * arrowButtonWidth), insideHeight, (theImgBox ? track : parentTrack)); #endif///ndef IMAGEv2_SHORT_TOGGLE // use the last window globals instead of the first struct window *w=windows; while(w->next) w = w->next; setGlobalsFromWindow(w); // use last window safef(buttonText, ArraySize(buttonText), "hgt.nextItem=%s", track->track); mapBoxReinvoke(hvg, portX + portWidth - arrowButtonWidth, y + 1, arrowButtonWidth, insideHeight, track, FALSE, NULL, 0, 0, (revCmplDisp ? "Prev item" : "Next item"), buttonText); setGlobalsFromWindow(windows); // restore first window } static int doCenterLabels(struct track *track, struct track *parentTrack, struct hvGfx *hvg, MgFont *font, int y, int fullInsideWidth ) /* 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; Color labelColor = (track->labelColor ? track->labelColor : track->ixColor); if (isCenterLabelConditional(track)) { struct trackDb* tdbComposite = tdbGetComposite(track->tdb); if (tdbComposite != NULL) { label = tdbComposite->longLabel; labelColor = hvGfxFindColorIx(hvg, tdbComposite->colorR, tdbComposite->colorG, tdbComposite->colorB); } } labelColor = maybeDarkerLabels(track, hvg, labelColor); hvGfxTextCentered(hvg, insideX, y+1, fullInsideWidth, 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) { #ifdef IMAGEv2_SHORT_TOGGLE // make toggle cover only actual label int size = mgFontStringWidth(font,label) + 12; // get close enough to the label if (trackPastTabWidth > size) { trackPastTabX = insideX + insideWidth/2 - size/2; trackPastTabWidth = size; } #endif///def IMAGEv2_SHORT_TOGGLE mapBoxToggleVis(hvg, trackPastTabX, y+1,trackPastTabWidth, insideHeight, (theImgBox ? track : parentTrack)); } y += fontHeight; } y += track->totalHeight(track, track->limitedVis); } return y; } static void doPreDrawItems(struct track *track, struct hvGfx *hvg, MgFont *font, int y, long *lastTime) /* Do Pre-Draw track items. */ { int fontHeight = mgFontLineHeight(font); if (isCenterLabelIncluded(track)) y += fontHeight; if (track->preDrawItems) track->preDrawItems(track, winStart, winEnd, hvg, insideX, y, insideWidth, font, track->ixColor, track->limitedVis); if (measureTiming && lastTime) { long thisTime = clock1000(); track->drawTime = thisTime - *lastTime; *lastTime = thisTime; } } 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); labelColor = maybeDarkerLabels(track, hvg, labelColor); 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; } long computeScaleBar(long numBases, char scaleText[], int scaleTextSize) /* Do some scalebar calculations and return the number of bases the scalebar will span. */ { char *baseWord = "bases"; long 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"; else if ((numFigs >= 9) && (numFigs < 12)) baseWord = "Gb"; 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. */ { if (tdbIsCompositeChild(subtrack->tdb)) { if (!subtrack->limitedVisSet) { subtrack->visibility = tdbVisLimitedByAncestors(cart, subtrack->tdb, TRUE, TRUE); limitVisibility(subtrack); } } else limitVisibility(subtrack); return subtrack->limitedVis; } static int calcNewWinWidth(struct cart *cart, int winStart, int winEnd, int insideWidth) /* Calc width of hit boxes that will zoom program around ruler. */ // TODO GALT // probably should change this to use full and virt vars and make it // go across the entire full image width. Especially since this is about // zooming on the virt chrom when user clicks on scalebar or chrom-ruler // Default is to just zoom current window in by 3x to 1/3 of current width. { int winWidth = winEnd - winStart; int newWinWidth = winWidth; 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_100X)) newWinWidth = winWidth/100; else if (sameString(zoomType, ZOOM_BASE)) newWinWidth = insideWidth/tl.mWidth; else errAbort("invalid zoom type %s", zoomType); if (newWinWidth < 1) newWinWidth = 1; return newWinWidth; } static void drawScaleBar( struct hvGfx *hvg, MgFont *font, int fontHeight, int yAfterRuler, int y, int scaleBarTotalHeight ) /* draws the scale bar */ { int scaleBarPad = 2; int scaleBarHeight = fontHeight; // can have one for entire multi-window image char scaleText[32]; long numBases = 0; struct window *w; for (w=windows; w; w=w->next) numBases += (w->winEnd - w->winStart); long scaleBases = computeScaleBar(numBases, scaleText, sizeof(scaleText)); int scalePixels = (int)((double)fullInsideWidth*scaleBases/numBases); int scaleBarX = fullInsideX + (int)(((double)fullInsideWidth-scalePixels)/2); int scaleBarEndX = scaleBarX + scalePixels; int scaleBarY = y + 0.5 * scaleBarTotalHeight; hvGfxTextRight(hvg, fullInsideX, y + scaleBarPad, (scaleBarX-2)-fullInsideX, 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); if(cartUsualBoolean(cart, BASE_SHOWASM_SCALEBAR, TRUE)) { int fHeight = vgGetFontPixelHeight(hvg->vg, font); hvGfxText(hvg, scaleBarEndX + 10, y + (scaleBarTotalHeight - fHeight)/2 + ((font == mgSmallFont()) ? 1 : 0), MG_BLACK, font, trackHubSkipHubName(database)); } } static int doDrawRuler(struct hvGfx *hvg, int *rulerClickHeight, int rulerHeight, int yAfterRuler, int yAfterBases, MgFont *font, int fontHeight, boolean rulerCds, int scaleBarTotalHeight, struct window *window) /* draws the ruler. */ { 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) { if (window == windows) // first window, only need to do once { hvGfxUnclip(hvg); hvGfxSetClip(hvg, fullInsideX, y, fullInsideWidth, yAfterRuler-y+1); hvGfxTextCentered(hvg, fullInsideX, y, fullInsideWidth, titleHeight,MG_BLACK, font, baseTitle); hvGfxUnclip(hvg); hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1); } *rulerClickHeight += titleHeight; y += titleHeight; } if (baseShowPos||baseShowAsm) { if (window == windows) // first window, only need to do once { hvGfxUnclip(hvg); hvGfxSetClip(hvg, fullInsideX, y, fullInsideWidth, yAfterRuler-y+1); char txt[256]; char numBuf[SMALLBUF]; char *freezeName = NULL; freezeName = hFreezeFromDb(database); sprintLongWithCommas(numBuf, virtWinEnd-virtWinStart); if (freezeName == NULL) freezeName = cloneString("Unknown"); if (baseShowPos&&baseShowAsm) safef(txt,sizeof(txt),"%s %s %s (%s bp)",trackHubSkipHubName(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",trackHubSkipHubName(organism),freezeName); freez(&freezeName); hvGfxTextCentered(hvg, fullInsideX, y, fullInsideWidth, showPosHeight,MG_BLACK, font, txt); hvGfxUnclip(hvg); hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1); } *rulerClickHeight += showPosHeight; y += showPosHeight; } if (baseShowScaleBar) { if (window == windows) // first window { hvGfxUnclip(hvg); hvGfxSetClip(hvg, fullInsideX, y, fullInsideWidth, yAfterRuler-y+1); drawScaleBar(hvg, font, fontHeight, yAfterRuler, y, scaleBarTotalHeight); hvGfxUnclip(hvg); hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1); } y += scaleBarTotalHeight; *rulerClickHeight += scaleBarTotalHeight; } if (baseShowRuler && (insideWidth >=36)) { hvGfxDrawRulerBumpText(hvg, insideX, y, rulerHeight, insideWidth, MG_BLACK, font, relNumOff, winBaseCount, 0, 1); } 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]); // GALT TODO should this be fullInsideX and fullInsideWidth? mapBoxReinvoke(hvg, insideX, y+rulerHeight, insideWidth,baseHeight, NULL, FALSE, 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 logTrackList(struct dyString *dy, struct track *trackList) /* add visibile tracks to dyString, recursively called */ { if (trackList == NULL) return; struct track *track; for (track = trackList; track != NULL; track = track->next) { int vis = track->limitedVisSet ? track->limitedVis : track->visibility; if (vis) { logTrackList(dy, track->subtracks); if (dy->stringSize) dyStringAppendC(dy, ','); dyStringPrintf(dy,"%s:%d", track->track, vis); } } } static void logTrackVisibilities (char *hgsid, struct track *trackList) /* log visibile tracks and hgsid */ { struct dyString *dy = newDyString(1024); // build up dyString logTrackList(dy, trackList); // put out ~1024 bye blocks to error_log because otherwise // Apache will chop up the lines char *begin = dy->string; char *ptr = begin; int count = 0; for(ptr=begin; ((ptr = strchr(ptr, ',')) != NULL); ptr++) { if (ptr - begin > 800) { *ptr = 0; fprintf(stderr, "trackLog %d %s %s %s\n", count++, database, hgsid, begin); begin = ptr+1; } } fprintf(stderr, "trackLog %d %s %s %s\n", count++, database, hgsid, begin); dyStringFree(&dy); } 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); } } struct highlightVar // store highlight information { struct highlightVar *next; char *db; char *chrom; long chromStart; long chromEnd; char *hexColor; }; struct highlightVar *parseHighlightInfo() // Parse highlight info from cart var to a linked list of highlightVar structs // db.chrom:start-end#hexColor|db.chrom:start-end#hexColor|... { struct highlightVar *hlList = NULL; char *highlightDef = cartOptionalString(cart, "highlight"); if(highlightDef) { char *hlArr[256]; int hlCount = chopByChar(cloneString(highlightDef), '|', hlArr, ArraySize(hlArr)); int i; for (i=0; idb = cloneNextWordByDelimiter(&oneHl,'.'); h->chrom = cloneNextWordByDelimiter(&oneHl,':'); // long to handle virt chrom coordinates h->chromStart = atol(cloneNextWordByDelimiter(&oneHl,'-')); h->chromEnd = atol(cloneNextWordByDelimiter(&oneHl,'#')); h->chromStart--; // Not zero based if (highlightDef && *highlightDef != '\0') h->hexColor = cloneString(oneHl); slAddHead(&hlList, h); } slReverse(&hlList); } return hlList; } static void highlightRegions(struct cart *cart, struct hvGfx *hvg, int imagePixelHeight) // Highlights regions in the image. Only done if theImgBox is not defined. // Thus it is done for ps/pdf and view image but the hgTracks image is highlighted via js { struct highlightVar *hlList = parseHighlightInfo(); if(hlList && theImgBox == NULL) // Only highlight region when imgBox is not used. (pdf and show-image) { struct highlightVar *h; for (h=hlList; h; h=h->next) { if (virtualSingleChrom()) // DISGUISE VMODE { if ((h->db && sameString(h->db, database)) && (h->chrom && sameString(h->chrom,chromName))) { char position[1024]; safef(position, sizeof position, "%s:%ld-%ld", h->chrom, h->chromStart, h->chromEnd); char *newPosition = undisguisePosition(position); // UN-DISGUISE VMODE if (startsWith("virt:", newPosition)) { parseVPosition(newPosition, &h->chrom, &h->chromStart, &h->chromEnd); } } } if ((h->db && sameString(h->db, database)) && (h->chrom && sameString(h->chrom,virtChromName)) && (h->chromEnd != 0) && (h->chromStart <= virtWinEnd && h->chromEnd >= virtWinStart)) { h->chromStart = max(h->chromStart, virtWinStart); h->chromEnd = min(h->chromEnd, virtWinEnd); double pixelsPerBase = (double)fullInsideWidth/(virtWinEnd - virtWinStart); int startPixels = pixelsPerBase * (h->chromStart - virtWinStart); // floor if (startPixels < 0) startPixels *= -1; // reverse complement int width = pixelsPerBase * (double)(h->chromEnd - h->chromStart) + 0.5; // round up if (width < 2) width = 2; // Default color to light blue, but if setting has color, use it. unsigned int hexColor = MAKECOLOR_32(170, 255, 255); if (h->hexColor) { long rgb = strtol(h->hexColor,NULL,16); // Big and little Endians hexColor = MAKECOLOR_32( ((rgb>>16)&0xff), ((rgb>>8)&0xff), (rgb&0xff) ); } hvGfxBox(hvg, fullInsideX + startPixels, 0, width, imagePixelHeight, hexColor); } } } } struct hash *makeGlobalTrackHash(struct track *trackList) /* Create a global track hash and returns a pointer to it. */ { trackHash = newHash(8); rAddToTrackHash(trackHash, trackList); return trackHash; } //void domAddMenu(char *afterMenuId, char *newMenuId, char *label) ///* Append a new drop down menu after a given menu, by changing the DOM with jquery */ //{ //printf("$('#%s').last().after('
  • %s" // "
    • ');\n", // afterMenuId, newMenuId, label); //} // //void domAppendToMenu(char *menuId, char *url, char *label) ///* Add an entry to a drop down menu, by changing the DOM with jquery */ //{ ////printf("$('#%s ul').last().after('
    • %s
    • ');\n", menuId, url, label); //printf("$('#%s ul').append('
    • %s
    • ');\n", menuId, url, label); //} //void menuBarAppendExtTools() ///* printf a little javascript that adds entries to a menu */ //{ // char url[SMALLBUF]; // safef(url,ArraySize(url),"hgTracks?%s=%s&hgt.redirectTool=crispor",cartSessionVarName(), cartSessionId(cart)); // printf("\n"); //} char *rgbColorToString(struct rgbColor color) /* make rgbColor into printable string */ { char buf[256]; safef(buf, sizeof buf, "rgbColor r:%d g:%d b:%d", color.r, color.g, color.b); return cloneString(buf); } char *trackDumpString(struct track *track) /* Write out info on track to string */ { struct dyString *dy = dyStringNew(256); dyStringPrintf(dy, "next: %lu\n", (unsigned long)track->next); // struct track *next; /* Next on list. */ // dyStringPrintf(dy, "track: %s\n", track->track); //char *track; /* Track symbolic name. Name on image map etc. Same as tdb->track. */ dyStringPrintf(dy, "table: %s\n", track->table); //char *table; /* Table symbolic name. Name of database table. Same as tdb->table.*/ dyStringPrintf(dy, "visibility: %s\n", hStringFromTv(track->visibility)); //enum trackVisibility visibility; /* How much of this want to see. */ dyStringPrintf(dy, "limitedVis: %s\n", hStringFromTv(track->limitedVis)); //enum trackVisibility limitedVis; /* How much of this actually see. */ dyStringPrintf(dy, "limitedVisSet: %d\n", track->limitedVisSet); //boolean limitedVisSet; /* Is limited visibility set? */ dyStringPrintf(dy, "longLabel: %s\n", track->longLabel); //char *longLabel; /* Long label to put in center. */ dyStringPrintf(dy, "shortLabel: %s\n", track->shortLabel); //char *shortLabel; /* Short label to put on side. */ dyStringPrintf(dy, "mapsSelf: %d\n", track->mapsSelf); //bool mapsSelf; /* True if system doesn't need to do map box. */ dyStringPrintf(dy, "drawName: %d\n", track->drawName); //bool drawName; /* True if BED wants name drawn in box. */ dyStringPrintf(dy, "colorShades: %lu\n", (unsigned long)track->colorShades); //Color *colorShades; /* Color scale (if any) to use. */ dyStringPrintf(dy, "color: %s\n", rgbColorToString(track->color)); //struct rgbColor color; /* Main color. */ dyStringPrintf(dy, "ixColor: %u\n", track->ixColor); //Color ixColor; /* Index of main color. */ dyStringPrintf(dy, "altColorShades: %lu\n", (unsigned long)track->altColorShades); //Color *altColorShades; /* optional alternate color scale */ dyStringPrintf(dy, "altColor: %s\n", rgbColorToString(track->altColor)); //struct rgbColor altColor; /* Secondary color. */ dyStringPrintf(dy, "ixAltColor: %u\n", track->ixAltColor); //Color ixAltColor; //void (*loadItems)(struct track *tg); /* loadItems loads up items for the chromosome range indicated. */ dyStringPrintf(dy, "items: %lu\n", (unsigned long) track->items); //void *items; /* Some type of slList of items. */ //char *(*itemName)(struct track *tg, void *item); /* Return name of one of an item to display on left side. */ //char *(*mapItemName)(struct track *tg, void *item); /* Return name to associate on map. */ //int (*totalHeight)(struct track *tg, enum trackVisibility vis); /* Return total height. Called before and after drawItems. * Must set the following variables. */ dyStringPrintf(dy, "height: %d\n", track->height); //int height; /* Total height - must be set by above call. */ dyStringPrintf(dy, "lineHeight: %d\n", track->lineHeight); //int lineHeight; /* Height per item line including border. */ dyStringPrintf(dy, "heightPer: %d\n", track->heightPer); //int heightPer; /* Height per item line minus border. */ //int (*itemHeight)(struct track *tg, void *item); /* Return height of one item. */ //int (*itemRightPixels)(struct track *tg, void *item); /* Return number of pixels needed to right of item for additional labeling. (Optional) */ //void (*drawItems)(struct track *tg, int seqStart, int seqEnd, //struct hvGfx *hvg, int xOff, int yOff, int width, //MgFont *font, Color color, enum trackVisibility vis); /* Draw item list, one per track. */ //void (*drawItemAt)(struct track *tg, void *item, struct hvGfx *hvg, //int xOff, int yOff, double scale, //MgFont *font, Color color, enum trackVisibility vis); /* Draw a single option. This is optional, but if it's here * then you can plug in genericDrawItems into the drawItems, * which takes care of all sorts of things including packing. */ //int (*itemStart)(struct track *tg, void *item); /* Return start of item in base pairs. */ //int (*itemEnd)(struct track *tg, void *item); /* Return end of item in base pairs. */ //void (*freeItems)(struct track *tg); /* Free item list. */ //Color (*itemColor)(struct track *tg, void *item, struct hvGfx *hvg); /* Get color of item (optional). */ //Color (*itemNameColor)(struct track *tg, void *item, struct hvGfx *hvg); /* Get color for the item's name (optional). */ //Color (*itemLabelColor)(struct track *tg, void *item, struct hvGfx *hvg); /* Get color for the item's label (optional). */ //void (*mapItem)(struct track *tg, struct hvGfx *hvg, void *item, //char *itemName, char *mapItemName, int start, int end, //int x, int y, int width, int height); /* Write out image mapping for a given item */ dyStringPrintf(dy, "hasUi: %d\n", track->hasUi); //boolean hasUi; /* True if has an extended UI page. */ dyStringPrintf(dy, "wigCartData: %lu\n", (unsigned long)track->wigCartData); //void *wigCartData; /* pointer to wigCart */ dyStringPrintf(dy, "extraUiData: %lu\n", (unsigned long)track->extraUiData); //void *extraUiData; /* Pointer for track specific filter etc. data. */ //void (*trackFilter)(struct track *tg); /* Stuff to handle user interface parts. */ dyStringPrintf(dy, "customPt: %lu\n", (unsigned long)track->customPt); //void *customPt; /* Misc pointer variable unique to track. */ dyStringPrintf(dy, "customInt: %d\n", track->customInt); //int customInt; /* Misc int variable unique to track. */ dyStringPrintf(dy, "subType: %d\n", track->subType); //int subType; /* Variable to say what subtype this is for similar tracks to share code. */ /* Stuff for the various wig incarnations - sample, wig, bigWig */ dyStringPrintf(dy, "minRange: %.2f, maxRange: %.2f\n", track->minRange, track->maxRange); //float minRange, maxRange; /*min and max range for sample tracks 0.0 to 1000.0*/ dyStringPrintf(dy, "scaleRange: %.2f\n", track->scaleRange); //float scaleRange; /* What to scale samples by to get logical 0-1 */ dyStringPrintf(dy, "graphUpperLimit: %e, grapLowerLimit: %e\n", track->graphUpperLimit, track->graphLowerLimit); //double graphUpperLimit, graphLowerLimit; /* Limits of actual data in window for wigs. */ dyStringPrintf(dy, "preDrawContainer: %lu\n", (unsigned long)track->preDrawContainer); //struct preDrawContainer *preDrawContainer; /* Numbers to graph in wig, one per pixel */ //struct preDrawContainer *(*loadPreDraw)(struct track *tg, int seqStart, int seqEnd, int width); dyStringPrintf(dy, "wigGraphOutput: %lu\n", (unsigned long)track->wigGraphOutput); //struct wigGraphOutput *wigGraphOutput; /* Where to draw wig - different for transparency */ /* Do bits that load the predraw buffer. Called to set preDrawContainer */ dyStringPrintf(dy, "bbiFile: %lu\n", (unsigned long)track->bbiFile); //struct bbiFile *bbiFile; /* Associated bbiFile for bigWig or bigBed. */ dyStringPrintf(dy, "bedSize: %d\n", track->bedSize); //int bedSize; /* Number of fields if a bed file. */ dyStringPrintf(dy, "isBigBed: %d\n", track->isBigBed); //boolean isBigBed; /* If a bed, is it a bigBed? */ dyStringPrintf(dy, "isRemoteSql: %d\n", track->isRemoteSql); //boolean isRemoteSql; /* Is using a remote mySQL connection. */ dyStringPrintf(dy, "remoteSqlHost: %s\n", track->remoteSqlHost); //char *remoteSqlHost; /* Host machine name for remote DB. */ dyStringPrintf(dy, "remoteSqlUser: %s\n", track->remoteSqlUser); //char *remoteSqlUser; /* User name for remote DB. */ dyStringPrintf(dy, "remoteSqlPassword: %s\n", track->remoteSqlPassword); //char *remoteSqlPassword; /* Password for remote DB. */ dyStringPrintf(dy, "remoteSqlDatabase: %s\n", track->remoteSqlDatabase); //char *remoteSqlDatabase; /* Database in remote DB. */ dyStringPrintf(dy, "remoteSqlTable: %s\n", track->remoteSqlTable); //char *remoteSqlTable; /* Table name in remote DB. */ dyStringPrintf(dy, "otherDb: %s\n", track->otherDb); //char *otherDb; /* Other database for an axt track. */ dyStringPrintf(dy, "private: %d\n", track->private); //unsigned short private; /* True(1) if private, false(0) otherwise. */ dyStringPrintf(dy, "priority: %.2f\n", track->priority); //float priority; /* Tracks are drawn in priority order. */ dyStringPrintf(dy, "defaultPriority: %.2f\n", track->defaultPriority); //float defaultPriority; /* Tracks are drawn in priority order. */ dyStringPrintf(dy, "groupName: %s\n", track->groupName); //char *groupName; /* Name of group if any. */ dyStringPrintf(dy, "group: %lu\n", (unsigned long)track->group); //struct group *group; /* Group this track is associated with. */ dyStringPrintf(dy, "defaultGroupName: %s\n", track->defaultGroupName); //char *defaultGroupName; /* default Group this track is associated with. */ dyStringPrintf(dy, "canPack: %d\n", track->canPack); //boolean canPack; /* Can we pack the display for this track? */ dyStringPrintf(dy, "spaceSaver: %lu\n", (unsigned long)track->ss); //struct spaceSaver *ss; /* Layout when packed. */ dyStringPrintf(dy, "tdb: %lu\n", (unsigned long)track->tdb); //struct trackDb *tdb; /*todo:change visibility, etc. to use this */ // ADDED by GALT if (track->tdb) dyStringPrintf(dy, "tdb settings:\n%s\n", track->tdb->settings); dyStringPrintf(dy, "expScale: %.2f\n", track->expScale); //float expScale; /* What to scale expression tracks by. */ dyStringPrintf(dy, "expTable: %s\n", track->expTable); //char *expTable; /* Expression table in hgFixed. */ // factorSource dyStringPrintf(dy, "sourceCount: %d\n", track->sourceCount); //int sourceCount; /* Number of sources for factorSource tracks. */ dyStringPrintf(dy, "sources: %lu\n", (unsigned long)track->sources); //struct expRecord **sources; /* Array of sources */ dyStringPrintf(dy, "sourceRightPixels: %d\n", track->sourceRightPixels); //int sourceRightPixels; /* Number of pixels to right we'll need. */ // exon/Next dyStringPrintf(dy, "exonArrows: %d\n", track->exonArrows); //boolean exonArrows; /* Draw arrows on exons? */ dyStringPrintf(dy, "exonArrowsAlways: %d\n", track->exonArrowsAlways); //boolean exonArrowsAlways; /* Draw arrows on exons even with introns showing? */ dyStringPrintf(dy, "nextExonButtonable: %d\n", track->nextExonButtonable); //boolean nextExonButtonable; /* Use the next-exon buttons? */ dyStringPrintf(dy, "nextItemButtonable: %d\n", track->nextItemButtonable); //boolean nextItemButtonable; /* Use the next-gene buttons? */ dyStringPrintf(dy, "itemAttrTbl: %lu\n", (unsigned long)track->itemAttrTbl); //struct itemAttrTbl *itemAttrTbl; /* relational attributes for specific items (color) */ /* fill in left label drawing area */ dyStringPrintf(dy, "labelColor: %u\n", track->labelColor); //Color labelColor; /* Fixed color for the track label (optional) */ //void (*drawLeftLabels)(struct track *tg, int seqStart, int seqEnd, // struct hvGfx *hvg, int xOff, int yOff, int width, int height, // boolean withCenterLabels, MgFont *font, // Color color, enum trackVisibility vis); dyStringPrintf(dy, "subtracks: %lu\n", (unsigned long)track->subtracks); //struct track *subtracks; /* list of subsidiary tracks that are //loaded and drawn by this track. This //is used for "composite" tracks, such //as "mafWiggle */ dyStringPrintf(dy, "parent: %lu\n", (unsigned long)track->parent); //struct track *parent; /* Parent track if any */ dyStringPrintf(dy, "prevTrack: %lu\n", (unsigned long)track->prevTrack); //struct track *prevTrack; // if not NULL, points to track immediately above in the image. // Needed by ConditionalCenterLabel logic //void (*nextPrevExon)(struct track *tg, struct hvGfx *hvg, void *item, int x, int y, int w, int h, boolean next); /* Function will draw the button on a track item and assign a map */ /* box to it as well, so that a click will move the browser window */ /* to the next (or previous if next==FALSE) item. This is meant to */ /* jump to parts of an item already partially in the window but is */ /* hanging off the edge... e.g. the next exon in a gene. */ //void (*nextPrevItem)(struct track *tg, boolean next); /* If this function is given, it can dictate where the browser loads */ /* up based on whether a next-item button on the longLabel line of */ /* the track was pressed (as opposed to the next-item buttons on the */ /* track items themselves... see nextPrevExon() ). This is meant for */ /* going to the next/previous item currently unseen in the browser, */ /* e.g. the next gene. SO FAR THIS IS UNIMPLEMENTED. */ //char *(*itemDataName)(struct track *tg, char *itemName); /* If not NULL, function to translated an itemName into a data name. * This is can be used for looking up sequence, CDS, etc. It is used * to support item names that have uniqueness identifiers added to deal * with multiple alignments. The resulting value should *not* be freed, * and it should be assumed that it might only remain valid for a short * period of time.*/ dyStringPrintf(dy, "loadTime: %d\n", track->loadTime); //int loadTime; /* Time it takes to load (for performance tuning) */ dyStringPrintf(dy, "drawTime: %d\n", track->drawTime); //int drawTime; /* Time it takes to draw (for performance tuning) */ dyStringPrintf(dy, "remoteDataSource: %d\n", track->remoteDataSource); //enum enumBool remoteDataSource; /* The data for this track is from a remote source */ /* Slow retrieval means image can be rendered via an AJAX callback. */ dyStringPrintf(dy, "customTrack: %d\n", track->customTrack); //boolean customTrack; /* Need to explicitly declare this is a custom track */ dyStringPrintf(dy, "syncChildVisToSelf: %d\n", track->syncChildVisToSelf); //boolean syncChildVisToSelf; /* If TRUE sync visibility to of children to self. */ dyStringPrintf(dy, "networkErrMsg: %s\n", track->networkErrMsg); //char *networkErrMsg; /* Network layer error message */ dyStringPrintf(dy, "parallelLoading: %d\n", track->parallelLoading); //boolean parallelLoading; /* If loading in parallel, usually network resources. */ dyStringPrintf(dy, "summary: %lu\n", (unsigned long) track->summary); //struct bbiSummaryElement *summary; /* for bigBed */ dyStringPrintf(dy, "summAll: %lu\n", (unsigned long) track->sumAll); //struct bbiSummaryElement *sumAll; /* for bigBed */ dyStringPrintf(dy, "drawLabelInBox: %d\n", track->drawLabelInBox); //boolean drawLabelInBox; /* draw labels into the features instead of next to them */ //}; return dyStringCannibalize(&dy); } char *makeDumpURL(char *text) /* Make a temp file to hold big dump. Return URL to it. */ { // trackDump output is quite large struct tempName trkDmp; trashDirFile(&trkDmp, "hgt", "hgt", ".txt"); FILE *f = mustOpen(trkDmp.forCgi, "w"); fprintf(f, "%s", text); carefulClose(&f); return cloneString(trkDmp.forHtml); } char *makeTrackDumpLink(struct track *track) /* Make a track dump to trash, and return html link to it */ { char *tds = trackDumpString(track); char *url = makeDumpURL(tds); char buf[1024]; safef(buf, sizeof buf, "URL", url); freeMem(tds); freeMem(url); return cloneString(buf); } boolean regionsAreInOrder(struct virtRegion *virtRegion1, struct virtRegion *virtRegion2) /* Return TRUE if the regions are on the same chrom and non-overlapping * and are in-order, i.e. region 1 appears before region2. */ { if (sameString(virtRegion1->chrom, virtRegion2->chrom) && virtRegion1->end <= virtRegion2->start) return TRUE; return FALSE; } /* --- Virtual Chromosome Functions --- */ boolean virtualSingleChrom() /* Return TRUE if using virtual single chromosome mode */ { return (sameString(virtModeType,"exonMostly") || sameString(virtModeType,"geneMostly")); } void parseVPosition(char *position, char **pChrom, long *pStart, long *pEnd) /* parse Virt position */ { if (!position) { errAbort("position NULL"); } char *vPos = cloneString(position); char *colon = strchr(vPos, ':'); if (!colon) errAbort("position has no colon"); char *dash = strchr(vPos, '-'); if (!dash) errAbort("position has no dash"); *colon = 0; *dash = 0; *pChrom = cloneString(vPos); *pStart = atol(colon+1) - 1; *pEnd = atol(dash+1); } void parseNVPosition(char *position, char **pChrom, int *pStart, int *pEnd) /* parse NonVirt position */ { if (!position) { errAbort("position NULL"); } char *vPos = cloneString(position); char *colon = strchr(vPos, ':'); if (!colon) errAbort("position has no colon"); char *dash = strchr(vPos, '-'); if (!dash) errAbort("position has no dash"); *colon = 0; *dash = 0; *pChrom = cloneString(vPos); *pStart = atoi(colon+1) - 1; *pEnd = atoi(dash+1); } char *disguisePositionVirtSingleChrom(char *position) // DISGUISE VMODE /* Hide the virt position, convert to real single chrom span. * position should be virt chrom span. * Can handle anything in the virt single chrom. */ { /* parse Virt position */ char *chrom = NULL; long start = 0; long end = 0; parseVPosition(position, &chrom, &start, &end); if (!sameString(chrom, "virt")) return position; // return original struct window *windows = makeWindowListFromVirtChrom(start, end); char *nonVirtChromName = windows->chromName; if (!sameString(nonVirtChromName, chromName)) return position; // return original int nonVirtWinStart = windows->winStart; int nonVirtWinEnd = windows->winEnd; struct window *w; for (w=windows->next; w; w=w->next) { if (!sameString(w->chromName, nonVirtChromName)) return position; // return original if (w->winEnd < nonVirtWinEnd) return position; // return original nonVirtWinEnd = w->winEnd; } char nvPos[256]; safef(nvPos, sizeof nvPos, "%s:%d-%d", nonVirtChromName, nonVirtWinStart+1, nonVirtWinEnd); slFreeList(&windows); return cloneString(nvPos); } char *undisguisePosition(char *position) // UN-DISGUISE VMODE /* Find the virt position * position should be real chrom span. * Limitation: can only convert things in the current windows set. */ { /* parse NonVirt position */ char *chrom = NULL; int start = 0; int end = 0; parseNVPosition(position, &chrom, &start, &end); if (!sameString(chrom, chromName)) return position; // return original long newStart = -1; long newEnd = -1; struct window *lastW = NULL; struct window *w = NULL; for (w = windows; w; w=w->next) { // double check chrom is same thoughout all windows, otherwise warning, return original value if (!sameString(w->chromName, chromName)) { return position; // return original } // check that the regions are ascending and non-overlapping if (lastW && w->winStart < lastW->winEnd) { return position; // return original } // overlap with position? // if intersection, if (w->winEnd > start && end > w->winStart) { int s = max(start, w->winStart); int e = min(end, w->winEnd); long cs = s - w->winStart + w->virtStart; long ce = e - w->winStart + w->virtStart; if (newStart == -1) newStart = cs; newEnd = ce; } lastW = w; } if (newStart == -1) // none of the windows intersected with the position return position; // return original // return new virt undisguised position as a string char newPos[1024]; safef (newPos, sizeof newPos, "virt:%ld-%ld", (newStart+1), newEnd); return cloneString(newPos); } char *windowsSpanPosition() /* Return a position string that spans all the windows. * Windows should be on same chrom and ascending and non-overlapping.*/ { char buf[256]; char *chromName = windows->chromName; int start = windows->winStart; struct window *w = windows, *last = NULL; while(w->next) // find last window { last = w; w = w->next; if (!sameString(chromName, w->chromName)) errAbort("windowsSpanPosition: expected all windows to be on the same chrom but found %s and %s", chromName, w->chromName); if (w->winStart < last->winEnd) errAbort("windowsSpanPosition: expected all windows to be ascending non-overlapping, found %d < %d", w->winStart, last->winEnd); } int end = w->winEnd; safef(buf, sizeof buf, "%s:%d-%d", chromName, start+1, end); return cloneString(buf); } void padVirtRegions(int windowPadding) /* Pad virt regions with windowPadding bases * * NOTE a simple padding would not worry about merging or order. Just expand the beginning and end of each region. * NOTE this assumes that the regions are in order, but tolerates hiccups in order. * DONE make it handle multiple chromosomes * * TODO what about just modifying the original list directly? * I do not know if this is handling merging correctly. * DONE Maybe I should just add the padding directly into the exon-fetch-merge code. * I have looked at that earlier, and it should work easily. * It might also have the advantage of not having to create a duplicate list? * * TODO how do I test that the output is correct. * if the input has ordered non-duplicate regions, then the output should be likewise. * */ { int regionCount = 0; long regionBases=0; struct virtRegion *virtRegion, *lastVirtRegion = NULL; int leftWindowPadding = 0; int rightWindowPadding = 0; struct virtRegion *v, *newList = NULL; char *lastChrom = NULL; int chromSize = -1; for(virtRegion=virtRegionList; virtRegion; virtRegion = virtRegion->next) { AllocVar(v); if (!sameOk(virtRegion->chrom, lastChrom)) { chromSize = hChromSize(database, virtRegion->chrom); } v->chrom = virtRegion->chrom; // TODO is cloning the string needed? leftWindowPadding = windowPadding; rightWindowPadding = windowPadding; if (lastVirtRegion && regionsAreInOrder(lastVirtRegion, virtRegion)) { int distToPrevRegion = virtRegion->start - lastVirtRegion->end; if (distToPrevRegion < (2*windowPadding)) { leftWindowPadding = distToPrevRegion/2; } } if (virtRegion->next && regionsAreInOrder(virtRegion, virtRegion->next)) { int distToNextRegion = virtRegion->next->start - virtRegion->end; if (distToNextRegion < (2*windowPadding)) { rightWindowPadding = (distToNextRegion+1)/2; // +1 to balance for odd number of bases between, arbitrarily adding it to right side } } v->start = virtRegion->start - leftWindowPadding; v->end = virtRegion->end + rightWindowPadding; if (v->start < 0) v->start = 0; if (v->end >= chromSize) v->end = chromSize; regionBases += (v->end - v->start); slAddHead(&newList, v); lastVirtRegion = virtRegion; lastChrom = virtRegion->chrom; ++regionCount; } slReverse(&newList); virtRegionList = newList; // update new list -- // TODO should the old one be freed? if so, the chrom name should use cloneString } void makeVirtChrom() /* build virtual chrom array from virt region list */ { virtRegionCount = slCount(virtRegionList); AllocArray(virtChrom, virtRegionCount); struct virtRegion *v; int i = 0; long totalBases = 0; for(v=virtRegionList;v;v=v->next,++i) { virtChrom[i].virtPos = totalBases; virtChrom[i].virtRegion = v; totalBases += (v->end - v->start); } virtSeqBaseCount = totalBases; } void virtChromBinarySearch(long target, long *resultIndex, int *resultOffset) /* Do a binary search for the target position in the virtual chrom. * Return virtChrom Index and Offset if found. * Return -1 if target out of range. TODO maybe change to errAbort*/ { //The binary search will either return match or out-of-range. long index = -1; int offset = -1; long a = 0; // start of the array long b = virtRegionCount - 1; // end of array where N = count of regions if (target >=0 && target < virtSeqBaseCount) { while(1) { long c = (a + b) / 2; if (a > b) { index = b; break; } else if (target == virtChrom[c].virtPos) { // (exact match) index = c; break; } else if (target > virtChrom[c].virtPos) { a = c + 1; } else if (target < virtChrom[c].virtPos) { b = c - 1; } else { // probably should not happen errAbort("Unexpected outcome in virtChromBinarySearch."); } } } else if (target == virtSeqBaseCount) { // tolerate this special case index = virtRegionCount - 1; } if (index >= 0) offset = target - virtChrom[index].virtPos; *resultIndex = index; *resultOffset = offset; } void testVirtChromBinarySearch() /* test virt chrom binary search */ { warn("about to test virt chrom binary search: virtSeqBaseCount=%ld virtRegionCount=%d", virtSeqBaseCount, virtRegionCount); long i; int o; long target = -1; virtChromBinarySearch(target, &i, &o); if (i != -1) errAbort("target %ld result %ld %d ", target, i, o); for(target=0; target < virtSeqBaseCount; ++target) { virtChromBinarySearch(target, &i, &o); if (virtChrom[i].virtPos + o != target) errAbort("target = %ld result %ld %d ", target, i, o); struct virtRegion *v = virtChrom[i].virtRegion; int size = v->end - v->start; if (o > size) errAbort("target = %ld result %ld %d > size=%d", target, i, o, size); } target = virtSeqBaseCount; virtChromBinarySearch(target, &i, &o); if (i != virtRegionCount - 1) errAbort("target = virtSeqBaseCount = %ld result %ld %d ", target, i, o); target = virtSeqBaseCount+1; virtChromBinarySearch(target, &i, &o); if (i != -1) errAbort("target = virtSeqBaseCount = %ld result %ld %d ", target, i, o); warn("Got past test virt chrom binary search"); } struct window *makeWindowListFromVirtChrom(long virtWinStart, long virtWinEnd) /* make list of windows from virtual position on virtualChrom */ { // quick check of virt win start and end if (virtWinEnd == virtWinStart) return NULL; if (virtWinStart < 0 || virtWinStart >= virtSeqBaseCount) errAbort("unexpected error. virtWinStart=%ld out of range. virtSeqBaseCount=%ld", virtWinStart, virtSeqBaseCount); if (virtWinEnd < 0 || virtWinEnd > virtSeqBaseCount) errAbort("unexpected error. virtWinEnd=%ld out of range. virtSeqBaseCount=%ld", virtWinEnd, virtSeqBaseCount); if (virtWinEnd - virtWinStart < 1) errAbort("unexpected error. virtual window size < 1 base. virtWinStart=%ld virtWinEnd=%ld virtSeqBaseCount=%ld", virtWinStart, virtWinEnd, virtSeqBaseCount); long virtIndexStart; int virtOffsetStart; virtChromBinarySearch(virtWinStart, &virtIndexStart, &virtOffsetStart); if (virtIndexStart == -1) errAbort("unexpected failure to find target virtWinStart %ld in virtChrom Array", virtWinStart); long virtIndexEnd; int virtOffsetEnd; virtChromBinarySearch(virtWinEnd, &virtIndexEnd, &virtOffsetEnd); if (virtIndexEnd == -1) errAbort("unexpected failure to find target virtWinEnd %ld in virtChrom Array", virtWinEnd); // create new windows list from virt chrom struct window *windows = NULL; long i = virtIndexStart; int winCount = 0; long basesInWindows = 0; // TODO not actually using this variable for(i=virtIndexStart; i <= virtIndexEnd; ++i) { struct window *w; AllocVar(w); w->organism = organism; // obsolete w->database = database; // obsolete struct virtRegion *v = virtChrom[i].virtRegion; long virtPos = virtChrom[i].virtPos; w->chromName = v->chrom; if (i == virtIndexStart) { w->winStart = v->start + virtOffsetStart; w->virtStart = virtPos + virtOffsetStart; } else { w->winStart = v->start; w->virtStart = virtPos; } if (i == virtIndexEnd) { if (virtOffsetEnd == 0) continue; w->winEnd = v->start + virtOffsetEnd; } else { w->winEnd = v->end; } w->virtEnd = w->virtStart + (w->winEnd - w->winStart); w->regionOdd = i % 2; basesInWindows += (w->winEnd - w->winStart); slAddHead(&windows, w); ++winCount; } slReverse(&windows); if (basesInWindows != (virtWinEnd-virtWinStart)) // was virtWinBaseCount but now I call this routine for highlight pos as well as virt pos. errAbort("makeWindowListFromVirtChrom: unexpected error basesInWindows(%ld) != virtWinBaseCount(%ld)", basesInWindows, virtWinEnd-virtWinStart); return windows; } char *nonVirtPositionFromWindows() /* Must have created the virtual chrom first. * Currently just a hack to use windows, * use the (first) window(s) from that to get real chrom name start end */ { // assumes makeWindowListFromVirtChrom() has already been called. if (!windows) errAbort("nonVirtPositionFromWindows() unexpected error, windows list not initialized yet"); char *nonVirtChromName = windows->chromName; int nonVirtWinStart = windows->winStart; int nonVirtWinEnd = windows->winEnd; // Extending this through more of the windows, // if it is on the same chrom and maybe not too far separated. struct window *w; for (w=windows->next; w; w=w->next) { if (sameString(w->chromName, nonVirtChromName) && w->winEnd > nonVirtWinEnd) nonVirtWinEnd = w->winEnd; } // TODO Also consider preserving the original bases in windows width (with clipping). char nvPos[256]; safef(nvPos, sizeof nvPos, "%s:%d-%d", nonVirtChromName, nonVirtWinStart+1, nonVirtWinEnd); return cloneString(nvPos); } char *nonVirtPositionFromHighlightPos() /* Must have created the virtual chrom first. * Currently just a hack to use windows, * use the (first) window(s) from that to get real chrom name start end */ { struct highlightVar *h = parseHighlightInfo(); if (!(h && h->db && sameString(h->db, database) && sameString(h->chrom,"virt"))) return NULL; struct window *windows = makeWindowListFromVirtChrom(h->chromStart, h->chromEnd); char *nonVirtChromName = windows->chromName; int nonVirtWinStart = windows->winStart; int nonVirtWinEnd = windows->winEnd; // Extending this through more of the windows, // if it is on the same chrom and maybe not too far separated. struct window *w; for (w=windows->next; w; w=w->next) { if (sameString(w->chromName, nonVirtChromName) && w->winEnd > nonVirtWinEnd) nonVirtWinEnd = w->winEnd; } // TODO Also consider preserving the original bases in windows width (with clipping). char nvPos[256]; safef(nvPos, sizeof nvPos, "%s.%s:%d-%d#%s", h->db, nonVirtChromName, nonVirtWinStart+1, nonVirtWinEnd, h->hexColor); return cloneString(nvPos); } void allocPixelsToWindows() /* Allocate pixels to windows, sets insideWidth and insideX * * TODO currently uses a strategy that places a window at a pixel location * directly, because of round-off and missing small windows this can occasionally * lead to gaps not covered by any pixel. Consider replacing it with something * that tries not to leave many gaps -- but how to do it without distortion of some window sizes? * */ { double pixelsPerBase = (double)fullInsideWidth / virtWinBaseCount; long basesUsed = 0; int windowsTooSmall = 0; struct window **pWindows = &windows; struct window *window; int winCount = slCount(windows); for(window=windows;window;window=window->next) { int basesInWindow = window->winEnd - window->winStart; int pixelsInWindow = 0.5 + (double)basesInWindow * pixelsPerBase; // should this round up ? + 0.5? window->insideWidth = pixelsInWindow; window->insideX = fullInsideX + basesUsed * pixelsPerBase; basesUsed += basesInWindow; if (pixelsInWindow < 1) // remove windows less than one pixel from the list { *pWindows = window->next; --winCount; ++windowsTooSmall; } else { pWindows = &window->next; } } } struct positionMatch *virtChromSearchForPosition(char *chrom, int start, int end, boolean findNearest) /* Search the virtual chrom for the query chrom, start, end position * * TODO GALT: Intially this can be a simple brute-force search of the entire virtChrom array. * * However, this will need to be upgraded to using a rangeTree or similar structure * to rapidly return multiple regions that overlap the query position. * */ { struct positionMatch *list=NULL, *p; int nearestRegion = -1; boolean nearestAfter = TRUE; int nearestDistance = INT_MAX; int i; struct virtRegion *v = NULL; long virtPos = 0; for(i=0; i < virtRegionCount; ++i) { v = virtChrom[i].virtRegion; virtPos = virtChrom[i].virtPos; if (sameString(v->chrom,chrom)) { // TODO will we need to support finding closest misses too? if (v->end > start && end > v->start) // overlap { int s = max(start, v->start); int e = min(end , v->end ); AllocVar(p); p->virtStart = virtPos + (s - v->start); p->virtEnd = virtPos + (e - v->start); slAddHead(&list, p); } else if (findNearest && (!list)) { int thisDist = v->start - start; boolean thisAfter = TRUE; if (thisDist < 0) // absolute value { thisDist = -thisDist; thisAfter = FALSE; } if (thisDist < nearestDistance) { nearestRegion = i; nearestDistance = thisDist; nearestAfter = thisAfter; } } } } if (findNearest && (!list) && nearestRegion != -1) { i = nearestRegion; v = virtChrom[i].virtRegion; virtPos = virtChrom[i].virtPos; AllocVar(p); if (nearestAfter) { p->virtStart = virtPos; p->virtEnd = p->virtStart + (end - start); } else { p->virtEnd = virtPos + (v->end - v->start); p->virtStart = p->virtEnd - (end - start); } if (p->virtEnd > virtSeqBaseCount) p->virtEnd = virtSeqBaseCount; slAddHead(&list, p); } slReverse(&list); return list; } int matchVPosCompare(const void *elem1, const void *elem2) /* compare elements based on vPos */ { const struct positionMatch *a = *((struct positionMatch **)elem1); const struct positionMatch *b = *((struct positionMatch **)elem2); if (a->virtStart == b->virtStart) errAbort("matchVPosCompare error should not happen: 2 elements being sorted have the same virtStart=%ld", a->virtStart); else if (a->virtStart > b->virtStart) return 1; return -1; } void matchSortOnVPos(struct positionMatch **pList) /* Sort positions by virtPos * pList will be sorted by chrom, start, end but we want it ordered by vPos */ { slSort(pList, matchVPosCompare); } struct positionMatch *matchMergeContiguousVPos(struct positionMatch *list) /* Merge contiguous matches spanning multiple touching windows */ { struct positionMatch *newList = NULL; struct positionMatch *m; long lastStart = -1; long lastEnd = -1; struct positionMatch *lastM = NULL; boolean inMerge = FALSE; long mergeStart = -1; long mergeEnd = -1; if (!list) return NULL; for(m=list; 1; m=m->next) // special loop condition allows it to stop AFTER it goes thru the loop once as m=NULL. // this flushes out the last value. { if (inMerge) { if (m && m->virtStart == lastEnd) { // continue merging, do nothing. // maybe could be freeing a skipped node here. } else { inMerge = FALSE; mergeEnd = lastEnd; // create new merged node and add it to new list struct positionMatch *n; AllocVar(n); n->virtStart = mergeStart; n->virtEnd = mergeEnd; slAddHead(&newList, n); } } else { if (m && m->virtStart == lastEnd) { inMerge = TRUE; mergeStart = lastStart; } else if (lastM) // just transfer the last node unmodified to the new list. { slAddHead(&newList, lastM); } } if (!m) break; lastStart = m->virtStart; lastEnd = m->virtEnd; lastM = m; } slReverse(&newList); return newList; } // ---------------------------------- void initVirtRegionsFromSOD1Hardwired() /* create a testing regionlist from SOD1 (uc002ypa.3) hardwired */ { virtRegionList = NULL; char *chrom="chr21"; //int exonStarts[] = {33031934,33036102,33038761,33039570,33040783}; //int exonEnds[] = {33032154,33036199,33038831,33039688,33041243}; // BASE-level zoom in two 20 bp exons and their on-screen junction int exonStarts[] = {33032134,33036102}; int exonEnds[] = {33032154,33036122}; int i; for(i=0; ichrom = cloneString(chrom); v->start = exonStarts[i]; v->end = exonEnds[i]; v->strand[0] = '+'; v->strand[1] = 0; slAddHead(&virtRegionList, v); } slReverse(&virtRegionList); } void initVirtRegionsFromEmGeneTable(char *ucscTranscriptId) /* create a testing regionlist from knownGene transcript with given id */ { struct sqlConnection *conn = hAllocConn(database); virtRegionList = NULL; struct sqlResult *sr; char **row; int rowOffset = 0; if (hIsBinned(database, emGeneTable)) // skip first bin column if any ++rowOffset; char query[256]; sqlSafef(query, sizeof(query), "select * from %s where name='%s'", emGeneTable, ucscTranscriptId); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { struct genePred *gene = genePredLoad(row+rowOffset); int i; for(i=0; i< gene->exonCount; ++i) { struct virtRegion *v; AllocVar(v); v->chrom = cloneString(gene->chrom); v->start = gene->exonStarts[i]; v->end = gene->exonEnds[i]; v->strand[0] = gene->strand[0]; v->strand[1] = 0; slAddHead(&virtRegionList, v); } genePredFree(&gene); } sqlFreeResult(&sr); slReverse(&virtRegionList); hFreeConn(&conn); } boolean initSingleAltHaplotype(char *haplotypeId) /* create a testing regionlist from haplotype with given id */ { struct sqlConnection *conn = hAllocConn(database); if (!virtRegionList) // this should already contain allchroms. errAbort("unexpected error in initSingleAltHaplotype: virtRegionList is NULL, should contain all chroms"); struct virtRegion *after = virtRegionList; virtRegionList = NULL; struct sqlResult *sr; char **row; char *table = NULL; if (sameString(database,"hg17")) table = "altLocations"; // was haplotypeLocations which I made with BLAT and self-chains ; // bin+bed4 else if (sameString(database,"hg18")) table = "altLocations"; // was haplotypeLocationsEnsembl which got renamed; // bin+bed4 else if (sameString(database,"hg19")) table = "altLocations"; // created from hapRegions, was "altSeqHaplotypes"; // was bin+bed6, now bin+bed4 else if (sameString(database,"hg38")) table = "altLocations"; // bin+bed4 else { warn("initSingleAltHaplotype() was expecting database to be hg17, hg18, hg19, or hg38"); return FALSE; } // where is the alt haplo placed? char query[256]; sqlSafef(query, sizeof(query), "select chrom, chromStart, chromEnd from %s where name='%s'", table, haplotypeId); sr = sqlGetResult(conn, query); row = sqlNextRow(sr); if (!row) { warn("no haplotype found for [%s]", haplotypeId); return FALSE; } char *haploChrom = cloneString(row[0]); int haploStart = sqlUnsigned(row[1]); int haploEnd = sqlUnsigned(row[2]); sqlFreeResult(&sr); // what is the size of the alt haplo? int haploSize = hChromSize(database, haplotypeId); // hopefully this will work // insert into list replacing original haploChrom record struct virtRegion *before = NULL; boolean found = FALSE; long offset = 0; struct virtRegion *v; while ((v=slPopHead(&after))) { if (sameString(haploChrom, v->chrom)) { found = TRUE; break; } offset += (v->end - v->start); slAddHead(&before, v); } if (!found) { warn("initSingleAltHaplotype: chrom %s on which alt %s is placed is not found in all chroms list!", haploChrom, haplotypeId); return FALSE; } slReverse(&before); // for now, make virtchrom with just one chrom plus its haplo in the middle defaultVirtWinStart = 0; defaultVirtWinEnd = 0;; AllocVar(v); v->chrom = haploChrom; v->start = 0; v->end = haploStart; defaultVirtWinStart = v->end - haploSize; if (defaultVirtWinStart < 0) defaultVirtWinStart = 0; defaultVirtWinEnd = v->end; slAddHead(&virtRegionList, v); AllocVar(v); v->chrom = haplotypeId; v->start = 0; v->end = haploSize; defaultVirtWinEnd += haploSize; slAddHead(&virtRegionList, v); AllocVar(v); v->chrom = haploChrom; v->start = haploEnd; int chromSize = hChromSize(database, haploChrom); // size of the regular chrom v->end = chromSize; defaultVirtWinEnd += haploSize; if (defaultVirtWinEnd > chromSize) defaultVirtWinEnd = chromSize; slAddHead(&virtRegionList, v); slReverse(&virtRegionList); hFreeConn(&conn); defaultVirtWinStart += offset; defaultVirtWinEnd += offset; // concatenate the 3 lists. virtRegionList = slCat(before,slCat(virtRegionList,after)); return TRUE; } void initVirtRegionsFromKnownCanonicalGenes(char *table) // OBSOLETED by initVirtRegionsFromEMGeneTableExons() /* Create a regionlist from knownCanonical genes (not exons) */ // I was not expecting it, but 20% of the KC genes overlap with others. // Some are cDNAs, some are non-coding RNAs, some just look like junk. // But I have to add special logic to accomodate them. // Currently I just merge until there is no more overlap, // and then I start a new region. So 30K regions should reduce to about 24K regions or less. { struct sqlConnection *conn = hAllocConn(database); virtRegionList = NULL; struct sqlResult *sr; char **row; char query[256]; sqlSafef(query, sizeof(query), "select chrom, chromStart, chromEnd from %s where chrom not like '%%_hap_%%' and chrom not like '%%_random'", table); sr = sqlGetResult(conn, query); char chrom[256] = ""; int start = -1; int end = -1; char lastChrom[256] = ""; int lastStart = -1; int lastEnd = -1; boolean firstTime = TRUE; boolean isEOF = FALSE; while (1) { boolean printIt = FALSE; row = sqlNextRow(sr); if (row) { safecpy(chrom, sizeof chrom, row[0]); start = sqlUnsigned(row[1]); end = sqlUnsigned(row[2]); if (sameString(chrom, lastChrom)) { if (start <= lastEnd) { // overlap detected in knownCanonical // extend current region if (end > lastEnd) lastEnd = end; } else { printIt = TRUE; } } else { printIt = TRUE; } } else { printIt = TRUE; isEOF = TRUE; } if (printIt) { if (firstTime) { firstTime = FALSE; } else { struct virtRegion *v; AllocVar(v); v->chrom = cloneString(lastChrom); v->start = lastStart; v->end = lastEnd; v->strand[0] = '.'; // TODO we should probably just remove the strand field v->strand[1] = 0; slAddHead(&virtRegionList, v); } } if (isEOF) break; if (printIt) { safecpy(lastChrom, sizeof lastChrom, chrom); lastStart = start; lastEnd = end; } } sqlFreeResult(&sr); slReverse(&virtRegionList); hFreeConn(&conn); } struct kce // keep list of overlapping genes and their exons { struct kce *next; struct genePred *gene; int exonNumber; }; int findBestKce(struct kce *list, struct kce **pBestKce, struct kce **pPrevKce) // find best kce by having minimum exon start // TODO could replace this with a heap or a doubly-linked list { int best = -1; struct kce *e, *prev = NULL; for(e=list; e; prev=e, e=e->next) { int start = e->gene->exonStarts[e->exonNumber]; if ((start < best) || (best == -1)) { best = start; *pBestKce = e; *pPrevKce = prev; } } return best; } static void padExons(struct genePred *gene, int chromSize, int padding) /* pad all of the exons */ { int i; for(i=0; i < gene->exonCount; ++i) { gene->exonStarts[i] -= padding; if (gene->exonStarts[i] < 0) gene->exonStarts[i] = 0; gene->exonEnds[i] += padding; if (gene->exonEnds[i] > chromSize) gene->exonEnds[i] = chromSize; } } static void convertGenePredGeneToExon(struct genePred *gene) /* convert gene into a gene with just one exon that spans the entire gene */ { if (gene->exonCount < 1) errAbort("unexpected input in convertGenePredGeneToExon(), gene->exonCount=%d < 1", gene->exonCount); gene->exonEnds[0] = gene->exonEnds[gene->exonCount - 1]; gene->exonCount = 1; } void initVirtRegionsFromEMGeneTableExons(boolean showNoncoding, char *knownCanonical, char *knownToTag, boolean geneMostly) /* Create a regionlist from knownGene exons. */ // Merge exon regions that overlap. // DONE Jim indicated that he would prefer it to include all transcripts, not just knownCanonical. // DONE Jim also suggested that we might want to handle padding right here in this step. // After thinking about it, I do not think it would be very hard because we are merging already. // Basically, just take the record from the db table row, add padding to start and end, // and clip for chromosome size. // TODO If we keep it at full genome level (instead of single chrom), then there is an apparent // sorting issue because although they are sorted on disk, they are usually sorted by chrom alphabetically // so that chr11 (not chr2) comes after chr1. Instead of trying to specify the sort order in the query, // which is slow, or trying to read one chrom at a time in the sorted order which is also slow, we can instead // just fetch them in their native order, and then create a duplicate array and copy the contents // to it in memory, one chunk per chrom, which would be very fast, but temporarily require duplicate vchrom array mem. // Not sure what to do about assemblies with many scaffolds. // // Adding support for extra options from Gencode hg38 so we can filter for // comprehensive, splice-variants, non-coding subsets. { struct sqlConnection *conn = hAllocConn(database); virtRegionList = NULL; struct sqlResult *sr; char **row; int rowOffset = 0; char query[256]; int padding = emPadding; if (sameString(virtModeType, "geneMostly")) padding = gmPadding; // knownCanonical Hash struct hash *kcHash = NULL; if (knownCanonical) // filter out alt splicing variants { // load up hash of canonical transcriptIds sqlSafef(query, sizeof(query), "select transcript from %s" //" where chrom not like '%%_hap_%%' and chrom not like '%%_random'" , knownCanonical); if (virtualSingleChrom()) sqlSafefAppend(query, sizeof(query), " where chrom='%s'", chromName); kcHash = newHash(10); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { hashAdd(kcHash, row[0], NULL); } sqlFreeResult(&sr); } // knownToTag basic hash struct hash *ktHash = NULL; if (knownToTag) // filter out all but Basic { // load up hash of canonical transcriptIds sqlSafef(query, sizeof(query), "select name from %s where value='basic'", knownToTag); ktHash = newHash(10); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { hashAdd(ktHash, row[0], NULL); } sqlFreeResult(&sr); } setEMGeneTrack(); if (!emGeneTable) errAbort("Unexpected error, emGeneTable=NULL in initVirtRegionsFromEMGeneTableExons"); if (hIsBinned(database, emGeneTable)) // skip first bin column if any ++rowOffset; sqlSafef(query, sizeof(query), "select * from %s", emGeneTable); if (virtualSingleChrom()) sqlSafefAppend(query, sizeof(query), " where chrom='%s'", chromName); // TODO GALT may have to change this to in-memory sorting? // refGene is out of order because of genbank continuous loading // also, using where chrom= causes it to use indexes which disturb order returned. sqlSafefAppend(query, sizeof(query), " order by chrom, txStart"); sr = sqlGetResult(conn, query); char chrom[256] = ""; int start = -1; int end = -1; char lastChrom[256] = ""; int lastStart = -1; int lastEnd = -1; int chromSize = -1; char lastChromSizeChrom[256] = ""; boolean firstTime = TRUE; boolean isEOF = FALSE; struct kce *kceList = NULL, *bestKce, *prevKce; struct genePred *gene = NULL; while (1) { while(1) // get input if possible { boolean readIt = FALSE; if (!gene) readIt = TRUE; if (isEOF) readIt = FALSE; if (readIt) { row = sqlNextRow(sr); if (row) { gene = genePredLoad(row+rowOffset); if (geneMostly) convertGenePredGeneToExon(gene); if (!sameString(lastChromSizeChrom, gene->chrom)) { chromSize = hChromSize(database, gene->chrom); safecpy(lastChromSizeChrom, sizeof lastChromSizeChrom, gene->chrom); } if (padding > 0) padExons(gene, chromSize, padding); // handle padding } else { isEOF = TRUE; } } if (gene && !showNoncoding && (gene->cdsStart == gene->cdsEnd)) { //skip non-coding gene genePredFree(&gene); } if (gene && knownCanonical && !hashLookup(kcHash, gene->name)) { //skip gene not in knownCanonical hash genePredFree(&gene); } if (gene && knownToTag && !hashLookup(ktHash, gene->name)) { // skip gene not in knownToTag Basic hash genePredFree(&gene); } boolean transferIt = FALSE; if (gene && !kceList) { transferIt = TRUE; } else if (gene && kceList) { // TODO need to check the chrom equality first int best = findBestKce(kceList, &bestKce, &prevKce); if (sameString(gene->chrom, chrom)) { if (gene->exonStarts[0] < best) transferIt = TRUE; } } if (transferIt) { // add gene to kce list struct kce *kce; AllocVar(kce); kce->gene = gene; kce->exonNumber = 0; slAddHead(&kceList, kce); safecpy(chrom, sizeof chrom, gene->chrom); gene = NULL; // do not free since it is still in use } if (gene && kceList && !transferIt) break; if (isEOF && !gene) { if (kceList) // flush out the last of the items in kcelist findBestKce(kceList, &bestKce, &prevKce); break; } } boolean printIt = FALSE; if (kceList) { safecpy(chrom, sizeof chrom, bestKce->gene->chrom); start = bestKce->gene->exonStarts[bestKce->exonNumber]; end = bestKce->gene->exonEnds[bestKce->exonNumber]; if (sameString(chrom, lastChrom)) { if (start <= lastEnd) { // overlap detected, extend current region if (end > lastEnd) { lastEnd = end; } } else { printIt = TRUE; } } else { printIt = TRUE; } } else { printIt = TRUE; isEOF = TRUE; } if (printIt) { if (firstTime) { firstTime = FALSE; } else { struct virtRegion *v; AllocVar(v); v->chrom = cloneString(lastChrom); v->start = lastStart; v->end = lastEnd; v->strand[0] = '.'; // TODO we should probably just remove the strand field v->strand[1] = 0; slAddHead(&virtRegionList, v); } } if (isEOF && !kceList && !gene) break; if (printIt) { safecpy(lastChrom, sizeof lastChrom, chrom); lastStart = start; lastEnd = end; } ++bestKce->exonNumber; if (bestKce->exonNumber >= bestKce->gene->exonCount) { // remove from kceList genePredFree(&bestKce->gene); if (prevKce) prevKce->next = bestKce->next; else kceList = bestKce->next; freeMem(bestKce); } } sqlFreeResult(&sr); slReverse(&virtRegionList); hashFree(&kcHash); hashFree(&ktHash); hFreeConn(&conn); } void testRegionList() /* check if it is ascending non-overlapping regions. (this is not always a requirement in the most general case, i.e. user-regions) */ { char lastChrom[256]; int lastEnd = -1; struct virtRegion *v; for (v=virtRegionList; v; v=v->next) { if (sameString(v->chrom,lastChrom)) { if (v->end < v->start) errAbort("check of region list reveals invalid region %s:%d-%d", v->chrom, v->start, v->end); if (lastEnd > v->start) errAbort("check of region list reveals overlapping regions region %s:%d-%d lastEnd=%d", v->chrom, v->start, v->end, lastEnd); } else { safecpy(lastChrom, sizeof lastChrom, v->chrom); } lastEnd = v->end; } } // multi-window variables global to hgTracks void setGlobalsFromWindow(struct window *window) /* set global window values */ { currentWindow = window; organism = window->organism; database = window->database; chromName = window->chromName; winStart = window->winStart; winEnd = window->winEnd; insideX = window->insideX; insideWidth = window->insideWidth; winBaseCount = winEnd - winStart; } void initExonStep() /* create exon-like pattern with exonSize and stepSize */ { int winCount = cartUsualInt(cart, "demo2NumWindows", demo2NumWindows); int i; int exonSize = cartUsualInt(cart, "demo2WindowSize", demo2WindowSize); //200; //9974; //200; int intronSize = cartUsualInt(cart, "demo2StepSize", demo2StepSize); //200; //15000; // really using it like stepSize as that allows overlapping windows. struct virtRegion *v; for(i=0;ichrom = "chr21"; v->start = 33031597 - 1 + i*(intronSize); v->end = v->start + exonSize; //33041570; slAddHead(&virtRegionList, v); } slReverse(&virtRegionList); //if (winCount >= 2) // withNextExonArrows = FALSE; /* Display next exon navigation buttons near center labels? */ //warn("winCount=%d, exonSize=%d, intronSize=%d", winCount, exonSize, intronSize); } void initAllChroms() /* initialize virt region list for main chromosomes */ { struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr; char **row; int winCount = 0; char *query = NOSQLINJ "select chrom, size from chromInfo" " where chrom like 'chr%'" " and chrom not like '%_random'" " and chrom not like 'chrUn%'"; // allow alternate haplotypes for now //" and chrom not like '%_hap%'" //" and chrom not like '%_alt'" //warn("%s",query); struct virtRegion *v; sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { unsigned chromSize = sqlUnsigned(row[1]); AllocVar(v); v->chrom = cloneString(row[0]); v->start = 1 - 1; v->end = chromSize; slAddHead(&virtRegionList, v); ++winCount; } sqlFreeResult(&sr); hFreeConn(&conn); slReverse(&virtRegionList); } void initWindowsAltLoci() /* initialize window list showing alt (alternate haplotype)*/ { struct virtRegion *v; //chr1:153520530-153700530 AllocVar(v); v->chrom = "chr1"; v->start = 153520530 - 1; v->end = 153700530; //chr1:153,520,529-154,045,739 as a single window slAddHead(&virtRegionList, v); //chr1_GL383518v1_alt:1-182439 AllocVar(v); v->chrom = "chr1_GL383518v1_alt"; v->start = 1 - 1; v->end = 182439; slAddHead(&virtRegionList, v); //chr1:153865739-154045739 AllocVar(v); v->chrom = "chr1"; v->start = 153865739 - 1; v->end = 154045739; slAddHead(&virtRegionList, v); slReverse(&virtRegionList); } boolean initVirtRegionsFromBedUrl(time_t *bedDateTime) /* Read custom regions from BED URL */ { multiRegionsBedUrl = cartUsualString(cart, "multiRegionsBedUrl", multiRegionsBedUrl); int bedPadding = 0; // default no padding // TODO add some checks for db change? save in cart var? if (sameString(multiRegionsBedUrl,"")) { warn("No BED URL specified."); return FALSE; } if (!strstr(multiRegionsBedUrl,"://")) { warn("No protocol specified in BED URL %s", multiRegionsBedUrl); return FALSE; } struct lineFile *lf = lineFileUdcMayOpen(multiRegionsBedUrl, FALSE); if (!lf) { warn("Unable to open [%s] with udc", multiRegionsBedUrl); return FALSE; } *bedDateTime = udcTimeFromCache(multiRegionsBedUrl, NULL); char *line; int lineSize; int expectedFieldCount = -1; struct bed *bed, *bedList = NULL; while (lineFileNext(lf, &line, &lineSize)) { // Process comments for keywords like database, shortDesc, and maybe others if (startsWith("#",line)) { if (startsWith("#database ",line)) { char *dbFromBed = line+strlen("#database "); if (!sameString(database,dbFromBed)) { warn("Multi-Region BED URL error: The database (%s) specified in input does not match current database %s", dbFromBed, database); return FALSE; } } if (startsWith("#shortDesc ",line)) { virtModeShortDescr = cloneString(line+strlen("#shortDesc ")); } if (startsWith("#padding ",line)) { bedPadding = sqlSigned(line+strlen("#padding ")); } continue; } char *row[15]; int numFields = chopByWhite(line, row, ArraySize(row)); if (numFields < 3) { warn("%s doesn't appear to be in BED format. 3 or more fields required, got %d", multiRegionsBedUrl, numFields); return FALSE; } if (expectedFieldCount == -1) { expectedFieldCount = numFields; } else { if (numFields != expectedFieldCount) errAbort("Multi-Region BED was detected to have %d columns. But this row has %d columns. " "All rows except comment lines should have the same number of columns", numFields, expectedFieldCount); } AllocVar(bed); // All fields are standard BED fields, no bedplus fields supported at this time. // note: this function does not validate chrom name or end beyond chrom size loadAndValidateBed(row, numFields, numFields+0, lf, bed, NULL, TRUE); bed->chrom=cloneString(bed->chrom); // loadAndValidateBed does not do it for speed. but bedFree needs it. struct chromInfo *ci = hGetChromInfo(database, bed->chrom); if (ci == NULL) { warn("Couldn't find chromosome/scaffold %s in database", bed->chrom); return FALSE; } if (bed->chromEnd > ci->size) { warn("BED chromEnd %u > size %u for chromosome/scaffold %s", bed->chromEnd, ci->size, bed->chrom); return FALSE; } slAddHead(&bedList, bed); struct virtRegion *v; if (numFields < 12) { AllocVar(v); v->chrom = cloneString(bed->chrom); v->start = bed->chromStart; v->end = bed->chromEnd; slAddHead(&virtRegionList, v); } else { int e; for (e = 0; e < bed->blockCount; ++e) { AllocVar(v); v->chrom = cloneString(bed->chrom); v->start = bed->chromStart + bed->chromStarts[e]; v->end = v->start + bed->blockSizes[e]; slAddHead(&virtRegionList, v); } } } lineFileClose(&lf); bedFreeList(&bedList); slReverse(&virtRegionList); if (bedPadding > 0) padVirtRegions(bedPadding); return TRUE; } void restoreSavedVirtPosition() /* Set state from lastDbPosCart. * This involves parsing the extra state that was saved.*/ { struct hashEl *el, *elList = hashElListHash(lastDbPosCart->hash); for (el = elList; el != NULL; el = el->next) { char *cartVar = el->name; char *cartVal = cartOptionalString(lastDbPosCart, cartVar); if (cartVal) { /* do we need this feature? if (sameString(cartVal,"(null)")) cartRemove(cart, cartVar); else */ cartSetString(cart, cartVar, cartVal); } } hashElFreeList(&elList); } void lastDbPosSaveCartSetting(char *cartVar) /* Save var and value from cart into lastDbPosCart. */ { cartSetString(lastDbPosCart, cartVar, cartUsualString(cart, cartVar, NULL)); } void dySaveCartSetting(struct dyString *dy, char *cartVar, boolean saveBoth) /* Grab var and value from cart, save as var=val to dy string. */ { if (dy->stringSize > 0) dyStringAppend(dy, " "); dyStringPrintf(dy, "%s=%s", cartVar, cartUsualString(cart, cartVar, NULL)); if (saveBoth) lastDbPosSaveCartSetting(cartVar); } boolean initRegionList() /* initialize window list */ { struct virtRegion *v; virtRegionList = NULL; virtModeExtraState = ""; // This is state that determines if the virtChrom has changed lastDbPosCart = cartOfNothing(); // USED to store and restore cart settings related to position and virtMode struct dyString *dy = dyStringNew(256); // used to build virtModeExtraState if (sameString(virtModeType, "default")) { // Single window same as normal window // mostly good to test nothing was broken with single window AllocVar(v); v->chrom = chromName; v->start = 0; v->end = hChromSize(database, chromName); virtWinStart = winStart; virtWinEnd = winEnd; slAddHead(&virtRegionList, v); slReverse(&virtRegionList); } else if (sameString(virtModeType, "exonMostly") || sameString(virtModeType, "geneMostly")) { // Gencode settings: comprehensive, alt-splice, non-coding char *knownCanonical = NULL; // show splice-variants, not filtered out via knownCanonical boolean showNoncoding = TRUE; // show non-coding where cdsStart==cdsEnd char *knownToTag = NULL; // show comprehensive set not filtered by knownToTag char varName[SMALLBUF]; boolean geneMostly = FALSE; lastDbPosSaveCartSetting("emGeneTable"); //DISGUISE makes obsolete dySaveCartSetting(dy, "emGeneTable"); //DISGUISE makes obsolete dySaveCartSetting(dy, "emPadding"); if (sameString(virtModeType, "geneMostly")) geneMostly = TRUE; if (sameString(emGeneTable, "knownGene")) { // test cart var knownGene.show.noncoding // check for alternate table name. // if found, set and pass to gene-table reading routine // Some code borrowed from simpleTracks.c::loadKnownGene() safef(varName, sizeof(varName), "%s.show.noncoding", emGeneTable); showNoncoding = cartUsualBoolean(cart, varName, TRUE); //DISGUISE makes obsolete dySaveCartSetting(dy, varName); safef(varName, sizeof(varName), "%s.show.spliceVariants", emGeneTable); boolean showSpliceVariants = cartUsualBoolean(cart, varName, TRUE); //DISGUISE makes obsolete dySaveCartSetting(dy, varName); if (!showSpliceVariants) { char *canonicalTable = trackDbSettingOrDefault(emGeneTrack->tdb, "canonicalTable", "knownCanonical"); if (hTableExists(database, canonicalTable)) knownCanonical = canonicalTable; } safef(varName, sizeof(varName), "%s.show.comprehensive", emGeneTable); boolean showComprehensive = cartUsualBoolean(cart, varName, FALSE); //DISGUISE makes obsolete dySaveCartSetting(dy, varName); if (!showComprehensive) { if (hTableExists(database, "knownToTag")) { knownToTag = "knownToTag"; } } } if (sameString(emGeneTable, "refGene")) { char varName[SMALLBUF]; safef(varName, sizeof(varName), "%s.hideNoncoding", emGeneTable); showNoncoding = !cartUsualBoolean(cart, varName, FALSE); //DISGUISE makes obsolete dySaveCartSetting(dy, varName); } initVirtRegionsFromEMGeneTableExons(showNoncoding, knownCanonical, knownToTag, geneMostly); if (!virtRegionList) { warn("No genes found on chrom %s for track %s, returning to default view", chromName, emGeneTrack->shortLabel); return FALSE; // regionList is empty, nothing found. } if (geneMostly) virtModeShortDescr = "genes"; else virtModeShortDescr = "exons"; // DISGUISE makes obsolete dyStringPrintf(dy," %s %s", dy->string, knownCanonical, knownToTag); } else if (sameString(virtModeType, "kcGenes")) // TODO obsolete { initVirtRegionsFromKnownCanonicalGenes("knownCanonical"); virtModeShortDescr = "genes"; } else if (sameString(virtModeType, "customUrl")) { // custom regions from BED URL virtModeShortDescr = "customUrl"; // can be overridden by comment in input bed file time_t bedDateTime = 0; if (!initVirtRegionsFromBedUrl(&bedDateTime)) { return FALSE; // return to default mode } dySaveCartSetting(dy, "multiRegionsBedUrl", TRUE); dyStringPrintf(dy, " %ld", (long)bedDateTime); } else if (sameString(virtModeType, "singleTrans")) { singleTransId = cartUsualString(cart, "singleTransId", singleTransId); if (sameString(singleTransId, "")) { warn("Single transcript Id should not be blank"); return FALSE; // return to default mode } setEMGeneTrack(); dySaveCartSetting(dy, "singleTransId", TRUE); } else if (sameString(virtModeType, "singleAltHaplo")) { singleAltHaploId = cartUsualString(cart, "singleAltHaploId", singleAltHaploId); // default is chr6_cox_hap2 initAllChroms(); // we want to default to full genome view. if (!initSingleAltHaplotype(singleAltHaploId)) { virtRegionList = NULL; return FALSE; // return to default mode } virtModeShortDescr = "alt haplo"; // was "single haplo" but that might confuse some users. dySaveCartSetting(dy, "singleAltHaploId", TRUE); } else if (sameString(virtModeType, "allChroms")) { // TODO more work on this mode //warn("show all regular chromosomes (not alts)\n" //"Warning must turn off all tracks except big*"); initAllChroms(); } else if (sameString(virtModeType, "demo1")) { // demo two windows on two chroms (default posn chr21, and same loc on chr22 //chr21:33,031,597-33,041,570 AllocVar(v); //chr21:33,031,597-33,041,570 v->chrom = "chr21"; v->start = 33031597 - 1; v->end = 33041570; slAddHead(&virtRegionList, v); struct virtRegion *v2; AllocVar(v2); //chr22:33,031,597-33,041,570 v2->chrom = "chr22"; v2->start = 33031597 - 1; v2->end = 33041570; slAddHead(&virtRegionList, v2); slReverse(&virtRegionList); } else if (sameString(virtModeType, "demo2")) { // demo multiple (70) windows on one chrom chr21 def posn, window size and step exon-like initExonStep(); } else if (sameString(virtModeType, "demo4")) { // demo multiple (20) windows showing exons from TITIN gene uc031rqd.1. initVirtRegionsFromEmGeneTable("uc031rqd.1"); // TITIN // "uc002ypa.3"); // SOD1 } else if (sameString(virtModeType, "demo5")) { // demo alt locus on hg38 // Shows alt chrom surrounded by preceding and following regions of same size from reference genome. initWindowsAltLoci(); } else if (sameString(virtModeType, "demo6")) { // demo SOD1 // Shows zoomed in exon-exon junction from SOD1 gene, between exon1 and exon2. initVirtRegionsFromSOD1Hardwired(); } else { // Unrecognized virtModeType return FALSE; // return to default mode } virtModeExtraState = dyStringCannibalize(&dy); if (!virtRegionList) return FALSE; // regionList is empty, nothing found. return TRUE; } boolean isLimitedVisHiddenForAllWindows(struct track *track) /* Check if track limitedVis == hidden for all windows. * Return true if all are hidden */ { boolean result = TRUE; for(;track;track=track->nextWindow) if (track->limitedVis != tvHide) result = FALSE; return result; } boolean isTypeBedLike(struct track *track) /* Check if track type is BED-like packable thing (but not rmsk or joinedRmsk) */ { // TODO GALT do we have all the types needed? // TODO could it be as simple as whether track->items exists? char *typeLine = track->tdb->type, *words[8], *type; int wordCount; if (typeLine == NULL) return FALSE; wordCount = chopLine(cloneString(typeLine), words); if (wordCount <= 0) return FALSE; type = words[0]; if ( ( sameWord(type, "bed") || sameWord(type, "bed5FloatScore") || sameWord(type, "bed6FloatScore") || sameWord(type, "bedDetail") || sameWord(type, "bigBed") || sameWord(type, "bigGenePred") || sameWord(type, "broadPeak") || sameWord(type, "chain") || sameWord(type, "factorSource") || sameWord(type, "genePred") || sameWord(type, "gvf") || sameWord(type, "narrowPeak") || sameWord(type, "psl") //|| track->loadItems == loadSimpleBed //|| track->bedSize >= 3 // should pick up several ENCODE BED-Plus types. ) && track->canPack ) { return TRUE; } return FALSE; } boolean isTypeUseItemNameAsKey(struct track *track) /* Check if track type is like expRatio and key is just item name. */ { char *typeLine = track->tdb->type, *words[8], *type; int wordCount; if (typeLine == NULL) return FALSE; wordCount = chopLine(cloneString(typeLine), words); if (wordCount <= 0) return FALSE; type = words[0]; if (sameWord(type, "expRatio")) { // track is like expRatio, needs one row per item return TRUE; } return FALSE; } void setFlatTrackMaxHeight(struct flatTracks *flatTrack, int fontHeight) /* for each flatTrack, figure out maximum height needed from all windows */ { struct track *track = flatTrack->track; int maxHeight = 0; struct track *winTrack; struct window *window; for (window=windows, winTrack=track; window; window=window->next, winTrack=winTrack->nextWindow) { setGlobalsFromWindow(window); int trackHeight = trackPlusLabelHeight(winTrack, fontHeight); if (trackHeight > maxHeight) maxHeight = trackHeight; } setGlobalsFromWindow(windows); // first window flatTrack->maxHeight = maxHeight; } void makeActiveImage(struct track *trackList, char *psOutput) /* Make image and image map. */ { struct track *track; MgFont *font = tl.font; struct hvGfx *hvg; struct tempName pngTn; 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 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) // if theImgBox == NULL then we are rendering a simple single image such as right-click View image. // theImgBox is a global for now to avoid huge rewrite of hgTracks. It is started // prior to this in doTrackForm() { rulerTtl = "drag select or click to zoom"; hPrintf("\n", database); hPrintf("\n", chromName); hPrintf("\n", winStart); hPrintf("\n", winEnd); hPrintf("\n", tl.picWidth); // If a portal was established, then set the global dimensions to the entire expanded image size if (imgBoxPortalDimensions(theImgBox,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL,NULL,NULL,NULL,NULL)) { pixWidth = tl.picWidth; virtWinBaseCount = virtWinEnd - virtWinStart; fullInsideWidth = tl.picWidth - gfxBorder - fullInsideX; } memset((char *)sliceWidth, 0,sizeof(sliceWidth)); memset((char *)sliceOffsetX,0,sizeof(sliceOffsetX)); if (withLeftLabels) { sliceWidth[stButton] = trackTabWidth + 1; sliceWidth[stSide] = leftLabelWidth - sliceWidth[stButton] + 1; 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; // figure out height of ruler 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; } } /* Hash tracks/subtracks, limit visibility and calculate total image height: */ // For multiple windows, sets height and visibility // as well as making the flatTrack list. // TODO there is a chance that for pack and squish // I might need to trigger a track-height check here // since it is after all items are loaded for all windows // but before things are checked for overflow or limitedVis? // The fixed non-overflow tracks like knownGene used to initialize // ss and track height during loadItems(). That was delayed // because we now need all windows to be fully loaded before // calculating their joint ss layout and height. // set heights and visibilities. struct window *window = NULL; for(window=windows;window;window=window->next) { setGlobalsFromWindow(window); trackList = window->trackList; for (track = trackList; track != NULL; track = track->next) { if (tdbIsCompositeChild(track->tdb)) // When single track is requested via AJAX, limitedVisFromComposite(track); // it could be a subtrack else { limitVisibility(track); } if (tdbIsComposite(track->tdb)) { struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (!isSubtrackVisible(subtrack)) continue; // subtrack vis can be explicit or inherited from composite/view. // Then it could be limited because of pixel height limitedVisFromComposite(subtrack); if (subtrack->limitedVis != tvHide) { subtrack->hasUi = track->hasUi; } } } } } trackList = windows->trackList; setGlobalsFromWindow(windows); // first window // Construct flatTracks for (track = trackList; track != NULL; track = track->next) { if (tdbIsComposite(track->tdb)) { struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (!isSubtrackVisible(subtrack)) continue; if (!isLimitedVisHiddenForAllWindows(subtrack)) { flatTracksAdd(&flatTracks,subtrack,cart); } } } else { if (!isLimitedVisHiddenForAllWindows(track)) { flatTracksAdd(&flatTracks,track,cart); } } } flatTracksSort(&flatTracks); // Now we should have a perfectly good flat track list! // for each track, figure out maximum height needed from all windows for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next) { track = flatTrack->track; if (track->limitedVis == tvHide) { continue; } setFlatTrackMaxHeight(flatTrack, fontHeight); } // fill out track->prevTrack, and check for maxSafeHeight boolean safeHeight = TRUE; /* firefox on Linux worked almost up to 34,000 at the default 620 width */ #define maxSafeHeight 32000 struct track *prevTrack = NULL; for (flatTrack = flatTracks,prevTrack=NULL; flatTrack != NULL; flatTrack = flatTrack->next) { track = flatTrack->track; assert(track->limitedVis != tvHide); // ORIG int totalHeight = pixHeight+trackPlusLabelHeight(track,fontHeight); int totalHeight = pixHeight+flatTrack->maxHeight; if (totalHeight > maxSafeHeight) { char numBuf[SMALLBUF]; sprintLongWithCommas(numBuf, maxSafeHeight); if (safeHeight) // Only one message warn("Image is over %s pixels high (%d pix) at the following track which is now " "hidden:
      \"%s\".%s", numBuf, totalHeight, track->tdb->longLabel, (flatTrack->next != NULL ? "
      Additional tracks may have also been hidden at this zoom level." : "")); safeHeight = FALSE; struct track *winTrack; for(winTrack=track;winTrack;winTrack=winTrack->nextWindow) { track->limitedVis = tvHide; track->limitedVisSet = TRUE; } } if (!isLimitedVisHiddenForAllWindows(track)) { struct track *winTrack; for(winTrack=track;winTrack;winTrack=winTrack->nextWindow) { // TODO this is currently still only using one prev track value. winTrack->prevTrack = prevTrack; // Important for keeping track of conditional center labels! } // ORIG pixHeight += trackPlusLabelHeight(track, fontHeight); if (!theImgBox) // prevTrack may have altered the height, so recalc height setFlatTrackMaxHeight(flatTrack, fontHeight); pixHeight += flatTrack->maxHeight; prevTrack = track; } } // allocate hvg png of pixWidth, pixHeight 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 if (measureTiming) measureTime("Time at start of obtaining trash hgt png image file"); trashDirFile(&pngTn, "hgt", "hgt", ".png"); hvg = hvGfxOpenPng(pixWidth, pixHeight, pngTn.forCgi, transparentImage); if (theImgBox) { // Adds one single image for all tracks (COULD: build the track by track images) theOneImg = imgBoxImageAdd(theImgBox,pngTn.forHtml,NULL,pixWidth, pixHeight,FALSE); theSideImg = theOneImg; // Unlkess this is overwritten below, there is a single image } hvgSide = hvg; // Unless this is overwritten below, there is a single image if (theImgBox && theImgBox->showPortal && withLeftLabels) { // TODO: It would be great to make the two images smaller, // but keeping both the same full size for now struct tempName pngTnSide; trashDirFile(&pngTnSide, "hgtSide", "side", ".png"); hvgSide = hvGfxOpenPng(pixWidth, pixHeight, pngTnSide.forCgi, transparentImage); // Also add the side image theSideImg = imgBoxImageAdd(theImgBox,pngTnSide.forHtml,NULL,pixWidth, pixHeight,FALSE); hvgSide->rc = revCmplDisp; initColors(hvgSide); } } hvg->rc = revCmplDisp; initColors(hvg); /* Start up client side map. */ hPrintf("\n", mapName); if (theImgBox == NULL) // imageV2 highlighting is done by javascript. This does pdf and view-image highlight highlightRegions(cart, hvg, imagePixelHeight); for (window=windows; window; window=window->next) { /* Find colors to draw in. */ findTrackColors(hvg, window->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 centerLbl, not reorderable } for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next) { track = flatTrack->track; if (!isLimitedVisHiddenForAllWindows(track)) { struct track *winTrack; for (winTrack=track; winTrack; winTrack=winTrack->nextWindow) { if (winTrack->labelColor == winTrack->ixColor && winTrack->ixColor == 0) { winTrack->ixColor = hvGfxFindRgb(hvg, &winTrack->color); } } int order = flatTrack->order; curImgTrack = imgBoxTrackFindOrAdd(theImgBox,track->tdb,NULL,track->limitedVis, isCenterLabelIncluded(track),order); if (trackShouldUseAjaxRetrieval(track)) imgTrackMarkForAjaxRetrieval(curImgTrack,TRUE); } } } /* 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); } else if (!trackImgOnly) // Side buttons only need to be drawn when drawing page with js { // advanced features off // TODO: Should remove wasted pixels too 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) { // ORIG y += trackPlusLabelHeight(track, fontHeight); y += flatTrack->maxHeight; yEnd = y; h = yEnd - yStart - 1; /* alternate button colors for track groups*/ if (track->group != lastGroup) grayButtonGroup = !grayButtonGroup; lastGroup = track->group; 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); } else if (!trackImgOnly) // Side buttons only need to be drawn when drawing page { // with js advanced features off if (grayButtonGroup) drawGrayButtonBox(hvgSide, trackTabX, yStart, trackTabWidth, h, track->hasUi); else drawBlueButtonBox(hvgSide, trackTabX, yStart, trackTabWidth, h, track->hasUi); } 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; } /* Draw left labels. */ if (withLeftLabels) { if (theImgBox == NULL) { 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); (void) 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 //ORIG sliceHeight = trackPlusLabelHeight(track, fontHeight); sliceHeight = flatTrack->maxHeight; sliceOffsetY = y; curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL); curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stSide,theSideImg,NULL, sliceWidth[stSide],sliceHeight, sliceOffsetX[stSide],sliceOffsetY); (void) sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot } if (trackShouldUseAjaxRetrieval(track)) y += REMOTE_TRACK_HEIGHT; else { boolean doWiggle = cartOrTdbBoolean(cart, track->tdb, "doWiggle" , FALSE); if (doWiggle) track->drawLeftLabels = wigLeftLabels; #ifdef IMAGEv2_NO_LEFTLABEL_ON_FULL if (theImgBox && track->limitedVis != tvDense) y += sliceHeight; else #endif ///def IMAGEv2_NO_LEFTLABEL_ON_FULL { setGlobalsFromWindow(windows); // use GLOBALS from first window int ynew = doLeftLabels(track, hvgSide, font, y); y += flatTrack->maxHeight; if ((ynew - y) > flatTrack->maxHeight) { // TODO should be errAbort? warn("doLeftLabels(y=%d) returned new y value %d that is too high - should be %d at most.", y, ynew, flatTrack->maxHeight); } } } } } else { leftLabelX = leftLabelWidth = 0; } /* Draw guidelines. */ if (virtMode && emAltHighlight) withGuidelines = TRUE; // we cannot draw the alternating backgrounds without guidelines layer if (withGuidelines) { struct hvGfx *bgImg = hvg; // Default to the one image boolean exists = FALSE; if (theImgBox) { struct tempName gifBg; char base[64]; if (virtMode) // window separators { safecpy(base,sizeof(base),"winSeparators"); // non-reusable temp file trashDirFile(&gifBg, "hgt", base, ".png"); exists = FALSE; } else // re-usable guidelines { safef(base,sizeof(base),"blueLines%d-%s%d-%d",pixWidth,(revCmplDisp?"r":""),fullInsideX, guidelineSpacing); // reusable file needs width, leftLabel start and guidelines exists = trashDirReusableFile(&gifBg, "hgt", base, ".png"); if (exists && cgiVarExists("hgt.reset")) // exists means don't remake bg image. However, exists = FALSE; // for now, rebuild on "default tracks" request } if (!exists) { bgImg = hvGfxOpenPng(pixWidth, pixHeight, gifBg.forCgi, TRUE); bgImg->rc = revCmplDisp; } imgBoxImageAdd(theImgBox,gifBg.forHtml,NULL,pixWidth, pixHeight,TRUE); // Adds BG image } if (!exists) { hvGfxSetClip(bgImg, fullInsideX, 0, fullInsideWidth, pixHeight); y = gfxBorder; if (virtMode) { // vertical windows separators if (emAltHighlight) { // light blue alternating backgrounds Color lightBlue = hvGfxFindRgb(bgImg, &guidelineColor); for (window=windows; window; window=window->next) // background under every other window { if (window->regionOdd) hvGfxBox(bgImg, window->insideX, 0, window->insideWidth, pixHeight, lightBlue); } } else { // red vertical lines Color lightRed = hvGfxFindRgb(bgImg, &vertWindowSeparatorColor); for (window=windows->next; window; window=window->next) // skip first window, not needed hvGfxBox(bgImg, window->insideX, 0, 1, pixHeight, lightRed); } } else { int x; Color lightBlue = hvGfxFindRgb(bgImg, &guidelineColor); for (x = fullInsideX+guidelineSpacing-1; xnext) { setGlobalsFromWindow(window); if (theImgBox) { // Show window positions as mouseover if (virtMode) { char position[256]; safef(position, sizeof position, "%s:%d-%d", window->chromName, window->winStart+1, window->winEnd); int x = window->insideX; if (revCmplDisp) x = tl.picWidth - (x + window->insideWidth); imgTrackAddMapItem(curImgTrack, "#", position, x, sliceOffsetY, x+window->insideWidth, sliceOffsetY+sliceHeight, RULER_TRACK_NAME); } } y = doDrawRuler(hvg,&rulerClickHeight,rulerHeight,yAfterRuler,yAfterBases,font, fontHeight,rulerCds, scaleBarTotalHeight, window); } setGlobalsFromWindow(windows); // first window } /* Draw center labels. */ if (withCenterLabels) { setGlobalsFromWindow(windows); // first window hvGfxSetClip(hvg, fullInsideX, gfxBorder, fullInsideWidth, pixHeight - 2*gfxBorder); y = yAfterRuler; for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next) { 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); (void) sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot if (isCenterLabelConditional(track)) // sometimes calls track height, especially when no data there { imgTrackUpdateCenterLabelSeen(curImgTrack,isCenterLabelConditionallySeen(track) ? clNowSeen : clNotSeen); } } if (trackShouldUseAjaxRetrieval(track)) { y += REMOTE_TRACK_HEIGHT; } else { int savey = y; // GALT y = doCenterLabels(track, track, hvg, font, y, fullInsideWidth); // calls track height // TODO GALT why do I just pass track here instead of parentTrack? Did I lose something? // have to look at old code to see. y = savey + flatTrack->maxHeight; } } hvGfxUnclip(hvg); setGlobalsFromWindow(windows); // first window } /* Draw tracks. */ { // brace allows local vars long lastTime = 0; y = yAfterRuler; if (measureTiming) lastTime = clock1000(); for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next) { track = flatTrack->track; // parallelize more this?: //ORIG if (track->limitedVis == tvHide) if (isLimitedVisHiddenForAllWindows(track)) continue; int centerLabelHeight = (isCenterLabelIncluded(track) ? fontHeight : 0); int yStart = y + centerLabelHeight; // ORIG int yEnd = y + trackPlusLabelHeight(track, fontHeight); int yEnd = y + flatTrack->maxHeight; 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); (void) sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot } } if (trackShouldUseAjaxRetrieval(track)) y += REMOTE_TRACK_HEIGHT; else { int savey = y; struct track *winTrack; // do preDraw if (track->preDrawItems) { for (window=windows, winTrack=track; window; window=window->next, winTrack=winTrack->nextWindow) { setGlobalsFromWindow(window); if (winTrack->limitedVis == tvHide) { warn("Draw tracks skipping %s because winTrack->limitedVis=hide", winTrack->track); continue; } if (insideWidth >= 1) // do not try to draw if width < 1. { doPreDrawItems(winTrack, hvg, font, y, &lastTime); } } } setGlobalsFromWindow(windows); // first window // do preDrawMultiRegion across all windows, e.g. wig autoScale if (track->preDrawMultiRegion) { track->preDrawMultiRegion(track); } // doDrawItems for (window=windows, winTrack=track; window; window=window->next, winTrack=winTrack->nextWindow) { setGlobalsFromWindow(window); if (winTrack->limitedVis == tvHide) { warn("Draw tracks skipping %s because winTrack->limitedVis=hide", winTrack->track); continue; } if (insideWidth >= 1) // do not try to draw if width < 1. { int ynew = doDrawItems(winTrack, hvg, font, y, &lastTime); if ((ynew-y) > flatTrack->maxHeight) // so compiler does not complain ynew is not used. errAbort("oops track too high!"); } } setGlobalsFromWindow(windows); // first window y = savey + flatTrack->maxHeight; } if (theImgBox && track->limitedVis == tvDense && tdbIsCompositeChild(track->tdb)) mapBoxToggleVis(hvg, 0, yStart,tl.picWidth, sliceHeight,track); // Strange mapBoxToggleLogic handles reverse complement itself so x=0,width=tl.picWidth if (yEnd != y) warn("Slice height for track %s does not add up. Expecting %d != %d actual", track->shortLabel, yEnd - yStart - 1, y - yStart); } y++; } /* post draw tracks leftLabels */ /* if a track can draw its left labels, now is the time since it * knows what exactly happened during drawItems */ // TODO GALT Parellelize or not? 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 // ORIG sliceHeight = trackPlusLabelHeight(track, fontHeight); sliceHeight = flatTrack->maxHeight; sliceOffsetY = y; curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL); curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stSide,theSideImg,NULL, sliceWidth[stSide],sliceHeight, sliceOffsetX[stSide],sliceOffsetY); (void) sliceMapFindOrStart(curSlice,track->tdb->track,NULL); // No common linkRoot } if (trackShouldUseAjaxRetrieval(track)) y += REMOTE_TRACK_HEIGHT; #ifdef IMAGEv2_NO_LEFTLABEL_ON_FULL else if (track->drawLeftLabels != NULL && (theImgBox == NULL || track->limitedVis == tvDense)) #else ///ndef IMAGEv2_NO_LEFTLABEL_ON_FULL else if (track->drawLeftLabels != NULL) #endif ///ndef IMAGEv2_NO_LEFTLABEL_ON_FULL { // TODO parallelize? setGlobalsFromWindow(windows); y = doOwnLeftLabels(track, hvgSide, font, y); setGlobalsFromWindow(windows); // first window } else // ORIG y += trackPlusLabelHeight(track, fontHeight); y += flatTrack->maxHeight; } } /* 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 // ORIG sliceHeight = trackPlusLabelHeight(track, fontHeight); sliceHeight = flatTrack->maxHeight; sliceOffsetY = y; curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL); } // TODO Parallelize? setGlobalsFromWindow(windows); // first window doTrackMap(track, hvg, y, fontHeight, trackPastTabX, trackPastTabWidth); // ORIG y += trackPlusLabelHeight(track, fontHeight); y += flatTrack->maxHeight; } } /* Finish map. */ hPrintf("\n"); // turn off inPlaceUpdate when rows in imgTbl can arbitrarily reappear and disappear (see redmine #7306 and #6944) jsonObjectAdd(jsonForClient, "inPlaceUpdate", newJsonBoolean(withLeftLabels && withCenterLabels)); jsonObjectAdd(jsonForClient, "rulerClickHeight", newJsonNumber(rulerClickHeight)); if(newWinWidth) { jsonObjectAdd(jsonForClient, "newWinWidth", newJsonNumber(newWinWidth)); } /* Save out picture and tell html file about it. */ if (hvgSide != hvg) hvGfxClose(&hvgSide); hvGfxClose(&hvg); if (measureTiming) measureTime("Time completed writing trash hgt png image file"); #ifdef SUPPORT_CONTENT_TYPE char *type = cartUsualString(cart, "hgt.contentType", "html"); if(sameString(type, "jsonp")) { struct jsonElement *json = newJsonObject(newHash(8)); printf("Content-Type: application/json\n\n"); errAbortSetDoContentType(FALSE); jsonObjectAdd(json, "track", newJsonString(cartString(cart, "hgt.trackNameFilter"))); jsonObjectAdd(json, "height", newJsonNumber(pixHeight)); jsonObjectAdd(json, "width", newJsonNumber(pixWidth)); jsonObjectAdd(json, "img", newJsonString(pngTn.forHtml)); printf("%s(", cartString(cart, "jsonp")); hPrintEnable(); jsonPrint((struct jsonElement *) json, NULL, 0); hPrintDisable(); printf(")\n"); return; } else if(sameString(type, "png") || sameString(type, "pdf") || sameString(type, "eps")) { // following code bypasses html and return png's directly - see redmine 4888 char *file; if(sameString(type, "pdf")) { printf("Content-Disposition: filename=hgTracks.pdf\nContent-Type: application/pdf\n\n"); file = convertEpsToPdf(psOutput); unlink(psOutput); } else if(sameString(type, "eps")) { printf("Content-Disposition: filename=hgTracks.eps\nContent-Type: application/eps\n\n"); file = psOutput; } else { printf("Content-Disposition: filename=hgTracks.png\nContent-Type: image/png\n\n"); file = pngTn.forCgi; } char buf[4096]; FILE *fd = fopen(file, "r"); if(fd == NULL) // fail some other way (e.g. HTTP 500)? errAbort("Couldn't open png for reading"); while (TRUE) { size_t n = fread(buf, 1, sizeof(buf), fd); if(n) fwrite(buf, 1, n, stdout); else break; } fclose(fd); unlink(file); return; } #endif///def SUPPORT_CONTENT_TYPE if (theImgBox) { imageBoxDraw(theImgBox); // If a portal was established, then set the global dimensions back to the portal size if (imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL)) { pixWidth = tl.picWidth; virtWinBaseCount = virtWinEnd - virtWinStart; fullInsideWidth = tl.picWidth - gfxBorder - fullInsideX; } imgBoxFree(&theImgBox); } else { char *titleAttr = "title='click or drag mouse in base position track to zoom in'"; hPrintf("
      \n"); } flatTracksFree(&flatTracks); } 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; if (sameString(tdb->type, "downloadsOnly")) // These tracks should not even be seen by hgTracks. 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 = lookupTrackHandlerClosestToHome(tdb); 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); 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[512]; if (strlen(itemName) > 0) safef(buf, sizeof(buf), "%s %s", ctFileName, itemName); else safef(buf, sizeof(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; } void dontLoadItems(struct track *tg) /* No-op loadItems when we aren't going to try. */ { } 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; if (trackShouldUseAjaxRetrieval(tg)) tg->loadItems = dontLoadItems; } else if (sameString(type, "bigBed")|| sameString(type, "bigGenePred") || sameString(type, "bigPsl") || sameString(type, "bigMaf")|| sameString(type, "bigChain")) { 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]; if (sameString(type, "bigGenePred")) safef(typeBuf, sizeof(typeBuf), "bigGenePred"); else if (sameString(type, "bigChain")) safef(typeBuf, sizeof(typeBuf), "bigChain"); else if (sameString(type, "bigMaf")) safef(typeBuf, sizeof(typeBuf), "bigMaf"); else if (sameString(type, "bigPsl")) safef(typeBuf, sizeof(typeBuf), "bigPsl"); else 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 = TRUE; if (trackShouldUseAjaxRetrieval(tg)) tg->loadItems = dontLoadItems; } 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); if (trackShouldUseAjaxRetrieval(tg)) tg->loadItems = dontLoadItems; tg->mapItemName = ctMapItemName; } else if (sameString(type, "vcfTabix")) { tg = trackFromTrackDb(tdb); tg->customPt = ct; vcfTabixMethods(tg); if (trackShouldUseAjaxRetrieval(tg)) tg->loadItems = dontLoadItems; tg->mapItemName = ctMapItemName; } else if (sameString(type, "vcf")) { tg = trackFromTrackDb(tdb); tg->customPt = ct; vcfMethods(tg); tg->mapItemName = ctMapItemName; } else if (sameString(type, "makeItems")) { tg = trackFromTrackDb(tdb); makeItemsMethods(tg); tg->nextItemButtonable = TRUE; tg->customPt = ct; } else if (sameString(type, "bedTabix") || sameString(type, "longTabix")) { knetUdcInstall(); tg = trackFromTrackDb(tdb); tg->customPt = ct; tg->mapItemName = ctMapItemName; /* must be here to see ctMapItemName */ } else if (sameString(type, "bedDetail")) { tg = trackFromTrackDb(tdb); bedDetailCtMethods(tg, ct); tg->mapItemName = ctMapItemName; /* must be here to see ctMapItemName */ } else if (sameString(type, "adjacency")) { extern void adjacencyMethods(struct track *track); tg = trackFromTrackDb(tdb); adjacencyMethods(tg); //tg->mapItemName = ctMapItemName; tg->customPt = ct; } else if (sameString(type, "pgSnp")) { tg = trackFromTrackDb(tdb); pgSnpCtMethods(tg); //tg->mapItemName = ctMapItemName; tg->customPt = ct; } else { errAbort("Unrecognized custom track type %s", type); } tg->hasUi = TRUE; tg->customTrack = TRUE;// Explicitly declare this a custom track for flatTrack ordering 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 loadTrackHubs(struct track **pTrackList, struct grp **pGrpList) /* Load up stuff from data hubs and append to lists. */ { struct trackDb *tdbList = hubCollectTracks(database, pGrpList); addTdbListToTrackList(tdbList, NULL, pTrackList); } boolean restrictionEnzymesOk() /* Check to see if it's OK to do restriction enzymes. */ { return (sqlDatabaseExists("hgFixed") && hTableExists("hgFixed", "cutters") && hTableExists("hgFixed", "rebaseRefs") && hTableExists("hgFixed", "rebaseCompanies")); } 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); } int hubCmpAlpha(const void *va, const void *vb) /* Compare to sort hubs based on name */ { const struct trackHub *a = *((struct trackHub **)va); const struct trackHub *b = *((struct trackHub **)vb); return strcmp(a->shortLabel, b->shortLabel); } static void rPropagateGroup(struct track *track, struct group *group) // group should spread to multiple levels of children. { struct track *subtrack = track->subtracks; for ( ;subtrack != NULL;subtrack = subtrack->next) { subtrack->group = group; rPropagateGroup(subtrack, group); } } static void groupTracks(struct track **pTrackList, struct group **pGroupList, struct grp *grpList, int vis) /* Make up groups and assign tracks to groups. * If vis is -1, restore default groups to tracks. */ { struct group *unknown = NULL; struct group *group, *list = NULL; struct hash *hash = newHash(8); struct track *track; struct trackRef *tr; struct grp* grps = hLoadGrps(database); struct grp *grp; float minPriority = 100000; // something really large boolean foundMap = FALSE; /* build group objects from database. */ for (grp = grps; grp != NULL; grp = grp->next) { if (sameString(grp->name, "map")) foundMap = TRUE; /* deal with group reordering */ float priority = grp->priority; // we want to get the minimum priority over 1 (which is custom tracks) if ((priority > 1.0) && (priority < minPriority)) minPriority = 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); double priorityInc; double priority = 1.00001; if (grpList) { minPriority -= 1.0; // priority is 1-based // the idea here is to get enough room between priority 1 // (which is custom tracks) and the group with the next // priority number, so that the hub nestle inbetween the // custom tracks and everything else at the top of the list // of track groups priorityInc = (0.9 * minPriority) / slCount(grpList); priority = 1.0 + priorityInc; } for(; grpList; grpList = grpList->next) { AllocVar(group); group->name = cloneString(grpList->name); group->label = cloneString(grpList->label); group->defaultPriority = group->priority = priority; priority += priorityInc; slAddHead(&list, group); hashAdd(hash, group->name, group); } // // If there isn't a map group, make one and set the priority so it will be right after custom tracks // and hub groups. if (!foundMap) { AllocVar(group); group->name = cloneString("map"); group->label = cloneString("Mapping and Sequencing"); group->defaultPriority = priority; group->priority = priority; group->defaultIsClosed = FALSE; slAddHead(&list, group); hashAdd(hash, "map", group); } /* Loop through tracks and fill in their groups. * If necessary make up an unknown group. */ for (track = *pTrackList; track != NULL; track = track->next) { /* handle track reordering feature -- change group assigned to track */ if (withPriorityOverride) { char *groupName = NULL; char cartVar[256]; /* belt and suspenders -- accomodate inconsistent track/trackDb * creation. Note -- with code cleanup, these default variables * could be retired, and the tdb versions used as defaults */ if (!track->defaultGroupName) { if (track->tdb && track->tdb->grp) track->defaultGroupName = cloneString(track->tdb->grp); else track->defaultGroupName = cloneString("other"); } if (tdbIsSuperTrackChild(track->tdb)) { assert(track->tdb->parentName != NULL); /* supertrack member must be in same group as its super */ /* determine supertrack group */ safef(cartVar, sizeof(cartVar), "%s.group",track->tdb->parentName); groupName = cloneString( //1 cartUsualString(cart, cartVar, track->tdb->parent->grp)); track->tdb->parent->grp = cloneString(groupName); //2 } else { /* get group */ safef(cartVar, sizeof(cartVar), "%s.group",track->track); groupName = cloneString( //1 cartUsualString(cart, cartVar, track->defaultGroupName)); } if (vis == -1) groupName = track->defaultGroupName; //0 track->groupName = cloneString(groupName); // wasting a few clones! //3 if (sameString(groupName, track->defaultGroupName)) cartRemove(cart, cartVar); /* get priority */ safef(cartVar, sizeof(cartVar), "%s.priority",track->track); float priority = (float)cartUsualDouble(cart, cartVar, track->defaultPriority); /* remove cart variables that are the same as the trackDb settings */ /* UGLY - add me back when tdb->priority is no longer pre-clobbered by cart var value if (priority == track->defaultPriority) cartRemove(cart, cartVar); */ track->priority = priority; } /* assign group object to track */ if (track->groupName == NULL) group = NULL; else group = hashFindVal(hash, track->groupName); if (group == NULL) { if (unknown == NULL) { AllocVar(unknown); unknown->name = cloneString("other"); unknown->label = cloneString("other"); unknown->priority = 1000000; slAddHead(&list, unknown); } group = unknown; } track->group = group; rPropagateGroup(track, group); } /* Sort tracks by combined group/track priority, and * then add references to track to group. */ slSort(pTrackList, tgCmpPriority); for (track = *pTrackList; track != NULL; track = track->next) { AllocVar(tr); tr->track = track; slAddHead(&track->group->trackList, tr); } /* Straighten things out, clean up, and go home. */ for (group = list; group != NULL; group = group->next) slReverse(&group->trackList); slSort(&list, gCmpPriority); hashFree(&hash); *pGroupList = list; } void groupTrackListAddSuper(struct cart *cart, struct group *group) /* Construct a new track list that includes supertracks, sort by priority, * and determine if supertracks have visible members. * Replace the group track list with this new list. * Shared by hgTracks and configure page to expand track list, * in contexts where no track display functions (which don't understand * supertracks) are invoked. */ { struct trackRef *newList = NULL, *tr, *ref; struct hash *superHash = hashNew(8); if (!group || !group->trackList) return; for (tr = group->trackList; tr != NULL; tr = tr->next) { struct track *track = tr->track; AllocVar(ref); ref->track = track; slAddHead(&newList, ref); if (tdbIsSuperTrackChild(track->tdb)) { assert(track->tdb->parentName != NULL); if (hTvFromString(cartUsualString(cart, track->track, hStringFromTv(track->tdb->visibility))) != tvHide) setSuperTrackHasVisibleMembers(track->tdb->parent); assert(track->parent == NULL); track->parent = hashFindVal(superHash, track->tdb->parentName); if (track->parent) continue; /* create track and reference for the supertrack */ struct track *superTrack = track->parent = trackFromTrackDb(track->tdb->parent); track->parent = superTrack; if (trackHash != NULL) hashAddUnique(trackHash,superTrack->track,superTrack); superTrack->hasUi = TRUE; superTrack->group = group; superTrack->groupName = cloneString(group->name); superTrack->defaultGroupName = cloneString(group->name); /* handle track reordering */ char cartVar[256]; safef(cartVar, sizeof(cartVar), "%s.priority",track->tdb->parentName); float priority = (float)cartUsualDouble(cart, cartVar, track->tdb->parent->priority); /* remove cart variables that are the same as the trackDb settings */ if (priority == track->tdb->parent->priority) cartRemove(cart, cartVar); superTrack->priority = priority; AllocVar(ref); ref->track = superTrack; slAddHead(&newList, ref); hashAdd(superHash, track->tdb->parentName, superTrack); } } slSort(&newList, trackRefCmpPriority); hashFree(&superHash); /* we could free the old track list here, but it's a trivial amount of mem */ group->trackList = newList; } void topButton(char *var, char *label) /* create a 3 or 4-char wide button for top line of display. * 3 chars wide for odd-length labels, 4 for even length. * Pad with spaces so label is centered */ { char paddedLabel[5] = " "; int len = strlen(label); if (len > 4) { /* truncate */ /* or maybe errabort ? */ label[3] = 0; len = 4; } if (len % 2 != 0) paddedLabel[3] = 0; if (len == strlen(paddedLabel)) strcpy(paddedLabel, label); else { int i; for (i=0; itdb)) { assert(track->tdb->parent != NULL); if (sameString("hide", cartUsualString(cart, track->tdb->parent->track, track->tdb->parent->isShow ? "show" : "hide"))) track->visibility = tvHide; } } struct track *rFindTrackWithTable(char *tableName, struct track *trackList) /* Recursively search through track list looking for first one that matches table. */ { struct track *track; for (track = trackList; track != NULL; track = track->next) { if (sameString(tableName, track->table)) return track; struct track *subTrack = rFindTrackWithTable(tableName, track->subtracks); if (subTrack != NULL) return subTrack; } return NULL; } static void setSearchedTrackToPackOrFull(struct track *trackList) // Open track associated with search position if any. Also open its parents if any. { if (NULL != hgp && NULL != hgp->tableList && NULL != hgp->tableList->name) { char *tableName = hgp->tableList->name; struct track *matchTrack = rFindTrackWithTable(tableName, trackList); if (matchTrack != NULL) tdbSetCartVisibility(matchTrack->tdb, cart, hCarefulTrackOpenVis(database, matchTrack->track)); } } struct track *getTrackList( struct group **pGroupList, int vis) /* Return list of all tracks, organizing by groups. * If vis is -1, restore default groups to tracks. * Shared by hgTracks and configure page. */ { struct track *track, *trackList = NULL; registerTrackHandlers(); /* Load regular tracks, blatted tracks, and custom tracks. * Best to load custom last. */ loadFromTrackDb(&trackList); if (pcrResultParseCart(database, cart, NULL, NULL, NULL)) slSafeAddHead(&trackList, pcrResultTg()); if (userSeqString != NULL) slSafeAddHead(&trackList, userPslTg()); slSafeAddHead(&trackList, oligoMatchTg()); if (restrictionEnzymesOk()) { slSafeAddHead(&trackList, cuttersTg()); } if (wikiTrackEnabled(database, NULL)) { addWikiTrack(&trackList); struct sqlConnection *conn = wikiConnect(); if (sqlTableExists(conn, "variome")) addVariomeWikiTrack(&trackList); wikiDisconnect(&conn); } struct grp *grpList = NULL; if (cartOptionalString(cart, "hgt.trackNameFilter") == NULL) { // If a single track was asked for and it is from a hub, then it is already in trackList loadTrackHubs(&trackList, &grpList); } loadCustomTracks(&trackList); groupTracks( &trackList, pGroupList, grpList, 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 (startsWith("hub_", track->track) && (s == NULL)) s = cartOptionalString(cart, trackHubSkipHubName(track->track)); if (cgiOptionalString("hideTracks")) { if (tdbIsSuperTrackChild(track->tdb)) { s = cgiOptionalString(track->tdb->parent->track); if (s) { cartSetString(cart, track->tdb->parent->track, s); track->tdb->parent->visibility = hTvFromString(s) ; } } s = cgiOptionalString(track->track); if (s != NULL) { if (hTvFromString(s) == track->tdb->visibility) cartRemove(cart, track->track); else cartSetString(cart, track->track, s); } } if (s != NULL && !track->limitedVisSet) track->visibility = hTvFromString(s); if (tdbIsCompositeChild(track->tdb)) track->visibility = tdbVisLimitedByAncestry(cart, track->tdb, FALSE); else if (tdbIsComposite(track->tdb) && track->visibility != tvHide) { struct trackDb *parent = track->tdb->parent; char *parentShow = NULL; if (parent) parentShow = cartUsualString(cart, parent->track, parent->isShow ? "show" : "hide"); if (!parent || sameString(parentShow, "show")) compositeTrackVis(track); } } return trackList; } 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; 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); } } static int getMaxWindowToDraw(struct trackDb *tdb) /* If trackDb setting maxWindowToDraw exists and is a sensible size, return it, else 0. */ { if (tdb == NULL) return 0; char *maxWinToDraw = trackDbSettingClosestToHome(tdb, "maxWindowToDraw"); if (isNotEmpty(maxWinToDraw)) { unsigned maxWTD = sqlUnsigned(maxWinToDraw); if (maxWTD > 1) return maxWTD; } return 0; } static void drawMaxWindowWarning(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* This is a stub drawItems handler to be swapped in for the usual drawItems when the window * size is larger than the threshold specified by trackDb setting maxWindowToDraw. */ { int maxWinToDraw = getMaxWindowToDraw(tg->tdb); char commafied[256]; sprintLongWithCommas(commafied, maxWinToDraw); char message[512]; safef(message, sizeof(message), "zoom in to <= %s bases to view items", commafied); Color yellow = hvGfxFindRgb(hvg, &undefinedYellowColor); hvGfxBox(hvg, xOff, yOff, width, tg->heightPer, yellow); hvGfxTextCentered(hvg, xOff, yOff, width, tg->heightPer, MG_BLACK, font, message); } static void checkMaxWindowToDraw(struct track *tg) /* If (winEnd - winStart) > trackDb setting maxWindowToDraw, force track to a dense line * that will ask the user to zoom in closer to see track items and return TRUE so caller * can skip loading items. */ { int maxWinToDraw = getMaxWindowToDraw(tg->tdb); if (tdbIsComposite(tg->tdb)) { struct track *subtrack; for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (!isSubtrackVisible(subtrack)) continue; maxWinToDraw = getMaxWindowToDraw(subtrack->tdb); if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw) { subtrack->loadItems = dontLoadItems; subtrack->drawItems = drawMaxWindowWarning; subtrack->limitedVis = tvDense; subtrack->limitedVisSet = TRUE; } } } else if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw) { tg->loadItems = dontLoadItems; tg->drawItems = drawMaxWindowWarning; tg->limitedVis = tvDense; tg->limitedVisSet = TRUE; } } void printTrackInitJavascript(struct track *trackList) { hPrintf("\n", hgtJsCommand, hgtJsCommand); } void jsCommandDispatch(char *command, struct track *trackList) /* Dispatch a command sent to us from some javaScript event. * This gets executed after the track list is built, but before * the track->loadItems methods are called. */ { if (startsWithWord("makeItems", command)) makeItemsJsCommand(command, trackList, trackHash); else warn("Unrecognized jsCommand %s", command); } void parentChildCartCleanup(struct track *trackList,struct cart *newCart,struct hash *oldVars) /* When composite/view settings changes, remove subtrack specific vis When superTrackChild is found and selected, shape superTrack to match. */ { struct lm *lm = lmInit(0); /* Speed tweak cleanup with scatch memory pool. */ struct track *track = trackList; for (;track != NULL; track = track->next) { boolean shapedByubtrackOverride = FALSE; boolean cleanedByContainerSettings = FALSE; // Top-down 'cleanup' MUST GO BEFORE bottom up reshaping. cleanedByContainerSettings = cartTdbTreeCleanupOverrides(track->tdb,newCart,oldVars, lm); if (tdbIsContainer(track->tdb)) { shapedByubtrackOverride = cartTdbTreeReshapeIfNeeded(cart,track->tdb); if (shapedByubtrackOverride) track->visibility = tdbVisLimitedByAncestors(cart,track->tdb,TRUE,TRUE); } if ((shapedByubtrackOverride || cleanedByContainerSettings) && tdbIsSuperTrackChild(track->tdb)) // Either cleanup may require supertrack intervention { // Need to update track visibility // Unfortunately, since supertracks are not in trackList, this occurs on superChildren, // So now we need to find the supertrack and take changed cart values of its children struct slRef *childRef; for (childRef = track->tdb->parent->children;childRef != NULL;childRef = childRef->next) { struct trackDb * childTdb = childRef->val; struct track *child = hashFindVal(trackHash, childTdb->track); if (child != NULL && child->track!=NULL) { char *cartVis = cartOptionalString(cart,child->track); if (cartVis) child->visibility = hTvFromString(cartVis); } } } } lmCleanup(&lm); } struct paraFetchData { struct paraFetchData *next; struct track *track; boolean done; }; static boolean isTrackForParallelLoad(struct track *track) /* Is this a track that should be loaded in parallel ? */ { char *bdu = trackDbSetting(track->tdb, "bigDataUrl"); return (startsWithWord("bigWig" , track->tdb->type) || startsWithWord("bigBed" , track->tdb->type) || startsWithWord("bigPsl" , track->tdb->type) || startsWithWord("bigGenePred" , track->tdb->type) || startsWithWord("bigChain" , track->tdb->type) || startsWithWord("bam" , track->tdb->type) || startsWithWord("halSnake", track->tdb->type) || startsWithWord("vcfTabix", track->tdb->type)) // XX code-review: shouldn't we error abort if the URL is not valid? && (bdu && isValidBigDataUrl(bdu, FALSE)) && !(containsStringNoCase(bdu, "dl.dropboxusercontent.com")) && (track->subtracks == NULL); } static void findLeavesForParallelLoad(struct track *trackList, struct paraFetchData **ppfdList) /* Find leaves of track tree that are remote network resources for parallel-fetch loading */ { struct track *track; if (!trackList) return; for (track = trackList; track != NULL; track = track->next) { if (track->visibility != tvHide) { if (isTrackForParallelLoad(track)) { struct paraFetchData *pfd; AllocVar(pfd); pfd->track = track; // need pointer to be stable slAddHead(ppfdList, pfd); track->parallelLoading = TRUE; } struct track *subtrack; for (subtrack=track->subtracks; subtrack; subtrack=subtrack->next) { if (isTrackForParallelLoad(subtrack)) { if (tdbVisLimitedByAncestors(cart,subtrack->tdb,TRUE,TRUE) != tvHide) { struct paraFetchData *pfd; AllocVar(pfd); pfd->track = subtrack; // need pointer to be stable slAddHead(ppfdList, pfd); subtrack->parallelLoading = TRUE; } } } } } } static pthread_mutex_t pfdMutex = PTHREAD_MUTEX_INITIALIZER; static struct paraFetchData *pfdList = NULL, *pfdRunning = NULL, *pfdDone = NULL, *pfdNeverStarted = NULL; static void *remoteParallelLoad(void *threadParam) /* Each thread loads tracks in parallel until all work is done. */ { pthread_t *pthread = threadParam; struct paraFetchData *pfd = NULL; pthread_detach(*pthread); // this thread will never join back with it's progenitor // Canceled threads that might leave locks behind, // so the theads are detached and will be neither joined nor canceled. boolean allDone = FALSE; while(1) { pthread_mutex_lock( &pfdMutex ); if (!pfdList) { allDone = TRUE; } else { // move it from the waiting queue to the running queue pfd = slPopHead(&pfdList); slAddHead(&pfdRunning, pfd); } pthread_mutex_unlock( &pfdMutex ); if (allDone) return NULL; long thisTime = 0, lastTime = 0; if (measureTiming) lastTime = clock1000(); /* protect against errAbort */ struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { pfd->done = FALSE; checkMaxWindowToDraw(pfd->track); pfd->track->loadItems(pfd->track); pfd->done = TRUE; } errCatchEnd(errCatch); if (errCatch->gotError) { pfd->track->networkErrMsg = cloneString(errCatch->message->string); pfd->done = TRUE; } errCatchFree(&errCatch); if (measureTiming) { thisTime = clock1000(); pfd->track->loadTime = thisTime - lastTime; } pthread_mutex_lock( &pfdMutex ); slRemoveEl(&pfdRunning, pfd); // this list will not be huge slAddHead(&pfdDone, pfd); pthread_mutex_unlock( &pfdMutex ); } } static int remoteParallelLoadWait(int maxTimeInSeconds) /* Wait, checking to see if finished (completed or errAborted). * If timed-out or never-ran, record error status. * Return error count. */ { int maxTimeInMilliseconds = 1000 * maxTimeInSeconds; struct paraFetchData *pfd; int errCount = 0; int waitTime = 0; while(1) { sleep1000(50); // milliseconds waitTime += 50; boolean done = TRUE; pthread_mutex_lock( &pfdMutex ); if (pfdList || pfdRunning) done = FALSE; pthread_mutex_unlock( &pfdMutex ); if (done) break; if (waitTime >= maxTimeInMilliseconds) break; } pthread_mutex_lock( &pfdMutex ); pfdNeverStarted = pfdList; pfdList = NULL; // stop the workers from starting any more waiting track loads for (pfd = pfdNeverStarted; pfd; pfd = pfd->next) { // track was never even started char temp[256]; safef(temp, sizeof temp, "Ran out of time (%d milliseconds) unable to process %s", maxTimeInMilliseconds, pfd->track->track); pfd->track->networkErrMsg = cloneString(temp); ++errCount; } for (pfd = pfdRunning; pfd; pfd = pfd->next) { // unfinished track char temp[256]; safef(temp, sizeof temp, "Timeout %d milliseconds exceeded processing %s", maxTimeInMilliseconds, pfd->track->track); pfd->track->networkErrMsg = cloneString(temp); ++errCount; } for (pfd = pfdDone; pfd; pfd = pfd->next) { // some done tracks may have errors if (pfd->track->networkErrMsg) ++errCount; } pthread_mutex_unlock( &pfdMutex ); return errCount; } static int avgLoadTime(struct track *track) /* calculate average loadtime across all windows */ { int totalLoadTime = 0; int winCount = 0; while(track) { ++winCount; totalLoadTime += track->loadTime; track = track->nextWindow; } return (((float)totalLoadTime / winCount) + 0.5); } static int avgDrawTime(struct track *track) /* calculate average drawtime across all windows */ { int totalDrawTime = 0; int winCount = 0; while(track) { ++winCount; totalDrawTime += track->drawTime; track = track->nextWindow; } return (((float)totalDrawTime / winCount) + 0.5); } static int avgWigMafLoadTime(struct track *track) /* calculate average wigMaf loadtime across all windows */ { int totalLoadTime = 0; int winCount = 0; while(track) { ++winCount; if (startsWith("wigMaf", track->tdb->type)) if (track->subtracks) if (track->subtracks->loadTime) totalLoadTime += track->subtracks->loadTime; track = track->nextWindow; } return (((float)totalLoadTime / winCount) + 0.5); } static void printTrackTiming() { hPrintf("track, load time, draw time, total (first window)
      \n"); if (virtMode) hPrintf("average for all windows in red
      \n"); struct track *track; for (track = trackList; track != NULL; track = track->next) { if (track->visibility == tvHide) continue; if (trackIsCompositeWithSubtracks(track)) //TODO: Change when tracks->subtracks are always set for composite { struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) if (isSubtrackVisible(subtrack)) { hPrintf("%s, %d, %d, %d
      \n", subtrack->shortLabel, subtrack->loadTime, subtrack->drawTime, subtrack->loadTime + subtrack->drawTime); if (virtMode) { int avgLoad = avgLoadTime(subtrack); int avgDraw = avgDrawTime(subtrack); hPrintf("%s, %d, %d, %d
      \n", subtrack->shortLabel, avgLoad, avgDraw, avgLoad + avgDraw); } } } else { hPrintf("%s, %d, %d, %d
      \n", track->shortLabel, track->loadTime, track->drawTime, track->loadTime + track->drawTime); if (startsWith("wigMaf", track->tdb->type)) if (track->subtracks) if (track->subtracks->loadTime) hPrintf("    %s wiggle, load %d
      \n", track->shortLabel, track->subtracks->loadTime); } if (virtMode) { int avgLoad = avgLoadTime(track); int avgDraw = avgDrawTime(track); hPrintf("%s, %d, %d, %d
      \n", track->shortLabel, avgLoad, avgDraw, avgLoad + avgDraw); int avgWigMafLoad = avgWigMafLoadTime(track); if (avgWigMafLoad > 0) { hPrintf("    %s wiggle, load %d
      \n", track->shortLabel, avgWigMafLoad); } } } hPrintf("
      \n"); } void initTrackList() /* need to init tracklist, sometimes early */ { if (!trackList) { if (measureTiming) measureTime("Time before getTrackList"); boolean defaultTracks = cgiVarExists("hgt.reset"); trackList = getTrackList(&groupList, defaultTracks ? -1 : -2); if (measureTiming) measureTime("getTrackList"); makeGlobalTrackHash(trackList); } } struct track *getTrackListForOneTrack(char *trackName) /* Fetch trackList for a single trackName using hgt.trackNameFilter. */ { struct track *saveTrackList = trackList; struct group *saveGroupList = groupList; char *saveTrackNameFilter = cloneString(cartOptionalString(cart, "hgt.trackNameFilter")); // This is an attempt to both get around the limitation imposed by ajax callback hgt.trackNameFilter, // and also to try to optimize it a little so that callbacks only have to load a trackList containing // only the emGeneTable. cartSetString(cart, "hgt.trackNameFilter", trackName); initTrackList(); // initialize trackList early if needed struct track *returnTrackList = trackList; cartRemove(cart, "hgt.trackNameFilter"); // restore if (saveTrackNameFilter) { cartSetString(cart, "hgt.trackNameFilter", saveTrackNameFilter); } trackList = saveTrackList; groupList = saveGroupList; return returnTrackList; } 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 */ { // create new trackList with just trackName struct track *myTrackList = getTrackListForOneTrack(trackName); struct track *track = trackFindByName(myTrackList, trackName); if ((track != NULL) && (track->nextPrevItem != NULL)) { // custom track big* tracks have pre-opened handle which we should not use // because that same bbiFile will get used later in the full track list track->bbiFile = NULL; track->nextPrevItem(track, goNext); } } void findBestEMGeneTable(struct track *myTrackList) /* Find the best gene table to use for exonMostly and geneMostly. */ { // TODO add support for choosing any gene table of type genePred, genePreExt, bigGenePred // TODO add support for assembly hubs if (trackHubDatabase(database)) // assembly hub? not supported yet return; // any table-name matches might just be coincidence in an assembly hub // although the hub_ prefix on the track name would help prevent name collisions. char *orderedTables[] = {"knownGene", "refGene", "ensGene", "flybaseGene", "sangerGene", "augustusGene", "genscan"}; int i, len; for(i=0, len=ArraySize(orderedTables); i next; window; window=window->next) { if (!sameString(window->chromName,windows->chromName)) return TRUE; } return FALSE; } static void setSharedErrorsAcrossWindows(struct track *track) /* Look for network errors across all windows * if found, set all windows to same errMsg and set bigWarn track handlers. */ { char *sharedErrMsg = NULL; struct track *tg; for (tg=track; tg; tg=tg->nextWindow) { if (!sharedErrMsg && tg->networkErrMsg) { sharedErrMsg = tg->networkErrMsg; break; } } if (sharedErrMsg) { for (tg=track; tg; tg=tg->nextWindow) { tg->networkErrMsg = sharedErrMsg; tg->drawItems = bigDrawWarning; tg->totalHeight = bigWarnTotalHeight; } } } 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; basesPerPixel = ((float)virtWinBaseCount) / ((float)fullInsideWidth); zoomedToBaseLevel = (virtWinBaseCount <= fullInsideWidth / tl.mWidth); zoomedToCodonLevel = (ceil(virtWinBaseCount/3) * tl.mWidth) <= fullInsideWidth; zoomedToCdsColorLevel = (virtWinBaseCount <= fullInsideWidth*3); if (psOutput != NULL) { hPrintDisable(); hideControls = TRUE; withNextItemArrows = FALSE; withNextExonArrows = FALSE; hgFindMatches = NULL; } /* Tell browser where to go when they click on image. */ hPrintf("
      \n\n", hgTracksName()); jsonObjectAdd(jsonForClient, "insideX", newJsonNumber(insideX)); // TODO GALT fullInsideX? or does not matter? jsonObjectAdd(jsonForClient, "revCmplDisp", newJsonBoolean(revCmplDisp)); if (hPrintStatus()) cartSaveSession(cart); /* See if want to include sequence search results. */ userSeqString = cartOptionalString(cart, "ss"); if (userSeqString && !ssFilesExist(userSeqString)) { userSeqString = NULL; cartRemove(cart, "ss"); } if (!hideControls) hideControls = cartUsualBoolean(cart, "hideControls", FALSE); initTrackList(); //warn("slCount(trackList) after getTrackList: %d", slCount(trackList)); /* Tell tracks to load their items. */ // honor defaultImgOrder if (cgiVarExists("hgt.defaultImgOrder")) { char wildCard[32]; safef(wildCard,sizeof(wildCard),"*_%s",IMG_ORDER_VAR); cartRemoveLike(cart, wildCard); } // Subtrack settings must be removed when composite/view settings are updated parentChildCartCleanup(trackList,cart,oldVars); if (measureTiming) measureTime("parentChildCartCleanup"); /* Honor hideAll and visAll variables */ if (hideAll || defaultTracks) { int vis = (hideAll ? tvHide : -1); changeTrackVis(groupList, NULL, vis); } if(!psOutput && !cartUsualBoolean(cart, "hgt.imageV1", FALSE)) { // re-establish the enlarged portal if (imgBoxPortalDimensions(theImgBox,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL,NULL,NULL,NULL,NULL)) { virtWinBaseCount = virtWinEnd - virtWinStart; fullInsideWidth = tl.picWidth - gfxBorder - fullInsideX; } } char *jsCommand = cartCgiUsualString(cart, hgtJsCommand, ""); if (!isEmpty(jsCommand)) { cartRemove(cart, hgtJsCommand); jsCommandDispatch(jsCommand, trackList); } /* adjust visibility */ for (track = trackList; track != NULL; track = track->next) { /* adjust track visibility based on supertrack just before load loop */ if (tdbIsSuperTrackChild(track->tdb)) limitSuperTrackVis(track); /* hide tracks not on any windows chromNames */ boolean hideIt = TRUE; struct window *w; for (w = windows; w; w=w->next) { if (hTrackOnChrom(track->tdb, w->chromName)) hideIt = FALSE; } if (hideIt) { track->limitedVis = tvHide; track->limitedVisSet = TRUE; } } if (sameString(cfgOptionDefault("trackLog", "off"), "on")) logTrackVisibilities(cartSessionId(cart), trackList); ///////////////// // NEED TO LOAD ALL WINDOWS NOW // // Need to load one window at a time! // // The use of the global values for a window // means that differerent threads cannot use different global window values. // Threads must run on just one window value at a time. // // Begin by making a copy of the track structure for visible tracks // for all windows. // COPY TRACK STRUCTURES for other windows. // TODO: due to an issue where some loading code is modifying the visibility // of subtracks from hide to visible, I am forced to remove the optimization // of cloning ONLY non-hidden tracks and subtracks. If the offending code // can be identified and moved into a step proceding the track cloning, // then we can return to that optimization. // if (track->visibility != tvHide) // if (subtrack->visibility != tvHide) windows->trackList = trackList; // save current track list in window struct window *window; for (window=windows; window->next; window=window->next) { struct track *newTrackList = NULL; for (track = trackList; track != NULL; track = track->next) { track->nextWindow = NULL; //if (track->visibility != tvHide) // Unable to use this optimization at present { struct track *copy; AllocVar(copy); memmove(copy,track,sizeof(struct track)); copy->next = NULL; copy->bbiFile = NULL; // bigDataUrl custom tracks have already been opened, will re-open for other windows. copy->nextWindow = NULL; copy->prevWindow = track; slAddHead(&newTrackList, copy); track->nextWindow = copy; // copy subtracks. copy->subtracks = NULL; struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) { //if (subtrack->visibility != tvHide) // Unable to use this optimization at present { struct track *subcopy; AllocVar(subcopy); memmove(subcopy,subtrack,sizeof(struct track)); subcopy->next = NULL; subcopy->bbiFile = NULL; // bigDataUrl custom tracks have already been opened, will re-open for other windows. subcopy->nextWindow = NULL; subcopy->prevWindow = subtrack; slAddHead(©->subtracks, subcopy); subtrack->nextWindow = subcopy; } } slReverse(©->subtracks); } } slReverse(&newTrackList); trackList = newTrackList; window->next->trackList = trackList; // save new track list in window } trackList = windows->trackList; // restore original track list // Loop over each window loading all tracks trackLoadingInProgress = TRUE; // LOAD OPTIMIZATION HACK GALT // This is an attempt to try to optimize loading by having multiple regions // treated as a single span. The hack just grabs the dimensions of the first and last windows // and uses the loader in the first window to load them, then copies the results to all tracks. // This basically has only been tried on BED-like tracks, and only for exon/gene-mostly vmodes. // I am not re-partitioning the results after the load, so this means all windows see all items. // The reason that tends to work is that by luck most BED handlers have code to check if the item // overlaps the current window and to skip it if it does not. // I do not expect something so simple would work with wigs and other track types. // Even if we do want to optimize the BED-like tracks (which are already the fastest loading type), // to handle all of the virtmodes properly, this would have be be done differently. // Instead of just lumping them all into a single range, you would have to cluster together // ranges that are close together and on the same chromosome. // Clearly this was just to test an idea for optimizing. // NOT FINISHED. bool loadHack = FALSE; //TRUE; // probably should only be tried on non-wiggle tracks //warn ("loadHack = %d", loadHack); // TODO int lastWinEnd = 0; for (window=windows; window; window=window->next) lastWinEnd = window->winEnd; for (window=windows; window; window=window->next) { trackList = window->trackList; // set track list setGlobalsFromWindow(window); // TEMP HACK GALT REMOVE if (loadHack) { if (currentWindow == windows) // first window winEnd = lastWinEnd; // so now we load the entire span inside the first window. } /* pre-load remote tracks in parallel */ int ptMax = atoi(cfgOptionDefault("parallelFetch.threads", "20")); // default number of threads for parallel fetch. int pfdListCount = 0; pthread_t *threads = NULL; if (ptMax > 0) // parallelFetch.threads=0 to disable parallel fetch { findLeavesForParallelLoad(trackList, &pfdList); pfdListCount = slCount(pfdList); /* launch parallel threads */ ptMax = min(ptMax, pfdListCount); if (ptMax > 0) { AllocArray(threads, ptMax); /* Create threads */ int pt; for (pt = 0; pt < ptMax; ++pt) { int rc = pthread_create(&threads[pt], NULL, remoteParallelLoad, &threads[pt]); if (rc) { errAbort("Unexpected error %d from pthread_create(): %s",rc,strerror(rc)); } } } } // TODO GALT /* load regular tracks */ for (track = trackList; track != NULL; track = track->next) { if (track->visibility != tvHide) { if (!track->parallelLoading) { if (measureTiming) lastTime = clock1000(); checkMaxWindowToDraw(track); checkIfWiggling(cart, track); if (!loadHack) { track->loadItems(track); } else { // TEMP HACK GALT REMOVE if (currentWindow == windows) // first window { track->loadItems(track); } else { track->items = track->prevWindow->items; // just point to the previous windows items (faster than loading) // apparently loadItems is setting some other fields that we want, but which ones? track->visibility = track->prevWindow->visibility; track->limitedVis = track->prevWindow->limitedVis; track->limitedVisSet = track->prevWindow->limitedVisSet; track->height = track->prevWindow->height; track->lineHeight = track->prevWindow->lineHeight; track->heightPer = track->prevWindow->heightPer; // TODO does this work for subtracks or parents/children? } } if (measureTiming) { thisTime = clock1000(); track->loadTime = thisTime - lastTime; } } } } if (ptMax > 0) { // TODO GALT parallel actually not sure if anything to worry about here /* wait for remote parallel load to finish */ remoteParallelLoadWait(atoi(cfgOptionDefault("parallelFetch.timeout", "90"))); // wait up to default 90 seconds. if (measureTiming) measureTime("Waiting for parallel (%d threads for %d tracks) remote data fetch", ptMax, pfdListCount); } } trackLoadingInProgress = FALSE; setGlobalsFromWindow(windows); // first window // restore globals trackList = windows->trackList; // restore track list // Look for network errors across all windows // if found, set all windows to same errMsg and set bigWarn track handlers. for (track = trackList; track != NULL; track = track->next) { setSharedErrorsAcrossWindows(track); struct track *sub; for (sub=track->subtracks; sub; sub=sub->next) { setSharedErrorsAcrossWindows(sub); } } ////////////// END OF MULTI-WINDOW LOOP printTrackInitJavascript(trackList); /* Generate two lists of hidden variables for track group visibility. Kludgy, but required b/c we have two different navigation forms on this page, but we want open/close changes in the bottom form to be submitted even if the user submits via the top form. */ struct dyString *trackGroupsHidden1 = newDyString(1000); struct dyString *trackGroupsHidden2 = newDyString(1000); for (group = groupList; group != NULL; group = group->next) { if (group->trackList != NULL) { int looper; for (looper=1;looper<=2;looper++) { boolean isOpen = !isCollapsedGroup(group); char buf[1000]; safef(buf, sizeof(buf), "\n", collapseGroupVar(group->name), collapseGroupVar(group->name), looper, isOpen ? "0" : "1"); dyStringAppend(looper == 1 ? trackGroupsHidden1 : trackGroupsHidden2, buf); } } } if (theImgBox) { // If a portal was established, then set the global dimensions back to the portal size if (imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL)) { virtWinBaseCount = virtWinEnd - virtWinStart; fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX; } } /* Center everything from now on. */ hPrintf("
      \n"); jsonObjectAdd(jsonForClient, "winStart", newJsonNumber(virtWinStart)); jsonObjectAdd(jsonForClient, "winEnd", newJsonNumber(virtWinEnd)); jsonObjectAdd(jsonForClient, "chromName", newJsonString(virtChromName)); // Tell javascript about multiple windows info if (virtMode) { // pre windows long preVWinStart = virtWinStart - virtWinBaseCount; if (preVWinStart < 0) preVWinStart = 0; long preVWinEnd = virtWinStart; struct window *preWindows = makeWindowListFromVirtChrom(preVWinStart, preVWinEnd); struct jsonElement *jsonForList = newJsonList(NULL); for(window=preWindows;window;window=window->next) { struct jsonElement *jsonForWindow = NULL; jsonForWindow = newJsonObject(newHash(8)); jsonObjectAdd(jsonForWindow, "chromName", newJsonString(window->chromName)); jsonObjectAdd(jsonForWindow, "winStart", newJsonNumber(window->winStart)); jsonObjectAdd(jsonForWindow, "winEnd", newJsonNumber(window->winEnd)); jsonObjectAdd(jsonForWindow, "insideX", newJsonNumber(window->insideX)); jsonObjectAdd(jsonForWindow, "insideWidth", newJsonNumber(window->insideWidth)); jsonObjectAdd(jsonForWindow, "virtStart", newJsonNumber(window->virtStart)); jsonObjectAdd(jsonForWindow, "virtEnd", newJsonNumber(window->virtEnd)); jsonListAdd(jsonForList, jsonForWindow); } slReverse(&jsonForList->val.jeList); jsonObjectAdd(jsonForClient, "windowsBefore", jsonForList); jsonForList = newJsonList(NULL); for(window=windows;window;window=window->next) { struct jsonElement *jsonForWindow = NULL; jsonForWindow = newJsonObject(newHash(8)); jsonObjectAdd(jsonForWindow, "chromName", newJsonString(window->chromName)); jsonObjectAdd(jsonForWindow, "winStart", newJsonNumber(window->winStart)); jsonObjectAdd(jsonForWindow, "winEnd", newJsonNumber(window->winEnd)); jsonObjectAdd(jsonForWindow, "insideX", newJsonNumber(window->insideX)); jsonObjectAdd(jsonForWindow, "insideWidth", newJsonNumber(window->insideWidth)); jsonObjectAdd(jsonForWindow, "virtStart", newJsonNumber(window->virtStart)); jsonObjectAdd(jsonForWindow, "virtEnd", newJsonNumber(window->virtEnd)); jsonListAdd(jsonForList, jsonForWindow); } slReverse(&jsonForList->val.jeList); jsonObjectAdd(jsonForClient, "windows", jsonForList); // post windows long postVWinStart = virtWinEnd; long postVWinEnd = virtWinEnd + virtWinBaseCount; if (postVWinEnd > virtSeqBaseCount) postVWinEnd = virtSeqBaseCount; struct window *postWindows = makeWindowListFromVirtChrom(postVWinStart, postVWinEnd); jsonForList = newJsonList(NULL); for(window=postWindows;window;window=window->next) { struct jsonElement *jsonForWindow = NULL; jsonForWindow = newJsonObject(newHash(8)); jsonObjectAdd(jsonForWindow, "chromName", newJsonString(window->chromName)); jsonObjectAdd(jsonForWindow, "winStart", newJsonNumber(window->winStart)); jsonObjectAdd(jsonForWindow, "winEnd", newJsonNumber(window->winEnd)); jsonObjectAdd(jsonForWindow, "insideX", newJsonNumber(window->insideX)); jsonObjectAdd(jsonForWindow, "insideWidth", newJsonNumber(window->insideWidth)); jsonObjectAdd(jsonForWindow, "virtStart", newJsonNumber(window->virtStart)); jsonObjectAdd(jsonForWindow, "virtEnd", newJsonNumber(window->virtEnd)); jsonListAdd(jsonForList, jsonForWindow); } slReverse(&jsonForList->val.jeList); jsonObjectAdd(jsonForClient, "windowsAfter", jsonForList); jsonForList = newJsonList(NULL); // also store js nonVirtPosition jsonObjectAdd(jsonForClient, "nonVirtPosition", newJsonString(cartString(cart, "nonVirtPosition"))); jsonObjectAdd(jsonForClient, "virtChromChanged", newJsonBoolean(virtChromChanged)); jsonObjectAdd(jsonForClient, "virtualSingleChrom", newJsonBoolean(virtualSingleChrom())); // DISGUISE POS } char dbPosKey[256]; safef(dbPosKey, sizeof(dbPosKey), "position.%s", database); jsonObjectAdd(jsonForClient, "lastDbPos", newJsonString(cartString(cart, dbPosKey))); // hide chromIdeo if ((trackImgOnly && !ideogramToo) || (sameString(virtModeType, "customUrl") && windowsHaveMultipleChroms()) // Special case hide by request ) { for(window=windows;window;window=window->next) { struct track *ideoTrack = chromIdeoTrack(window->trackList); if (ideoTrack) { ideoTrack->limitedVisSet = TRUE; ideoTrack->limitedVis = tvHide; /* Don't draw in main gif. */ } } } if (trackImgOnly && !ideogramToo) { makeActiveImage(trackList, psOutput); fflush(stdout); return; // bail out b/c we are done } if (!hideControls) { /* set white-space to nowrap to prevent buttons from wrapping when screen is * narrow */ hPrintf("
      \n"); printMenuBar(); //menuBarAppendExtTools(); /* Show title . */ freezeName = hFreezeFromDb(database); if(freezeName == NULL) freezeName = "Unknown"; hPrintf(""); if (startsWith("zoo",database) ) { hPrintf("%s %s on %s June 2002 Assembly %s target1", organization, browserName, organism, freezeName); } else { if (sameString(organism, "Archaea")) { hPrintf("%s %s on Archaeon %s Assembly", organization, browserName, freezeName); } else { if (stringIn(database, freezeName)) hPrintf("%s %s on %s %s Assembly", organization, browserName, organism, freezeName); else hPrintf("%s %s on %s %s Assembly (%s)", organization, browserName, trackHubSkipHubName(organism), freezeName, trackHubSkipHubName(database)); } } hPrintf("
      \n"); /* This is a clear submit button that browsers will use by default when enter is pressed in position box. */ hPrintf(""); /* Put up scroll and zoom controls. */ #ifndef USE_NAVIGATION_LINKS hWrites("move "); hButtonWithOnClick("hgt.left3", "<<<", "move 95% to the left", "return imageV2.navigateButtonClick(this);"); hButtonWithOnClick("hgt.left2", " <<", "move 47.5% to the left", "return imageV2.navigateButtonClick(this);"); hButtonWithOnClick("hgt.left1", " < ", "move 10% to the left", "return imageV2.navigateButtonClick(this);"); hButtonWithOnClick("hgt.right1", " > ", "move 10% to the right", "return imageV2.navigateButtonClick(this);"); hButtonWithOnClick("hgt.right2", ">> ", "move 47.5% to the right", "return imageV2.navigateButtonClick(this);"); hButtonWithOnClick("hgt.right3", ">>>", "move 95% to the right", "return imageV2.navigateButtonClick(this);"); hWrites(" zoom in "); /* use button maker that determines padding, so we can share constants */ topButton("hgt.in1", ZOOM_1PT5X); topButton("hgt.in2", ZOOM_3X); topButton("hgt.in3", ZOOM_10X); topButton("hgt.inBase", ZOOM_BASE); hWrites(" zoom out "); topButton("hgt.out1", ZOOM_1PT5X); topButton("hgt.out2", ZOOM_3X); topButton("hgt.out3", ZOOM_10X); topButton("hgt.out4", ZOOM_100X); hWrites("
      \n"); #endif//ndef USE_NAVIGATION_LINKS if (showTrackControls) { /* Break into a second form so that zooming and scrolling * can be done with a 'GET' so that user can back up from details * page without Internet Explorer popping up an annoying dialog. * Do rest of page as a 'POST' so that the ultra-long URL from * all the track controls doesn't break things. IE URL limit * is 2000 bytes, but some firewalls impose a ~1000 byte limit. * As a side effect of breaking up the page into two forms * we need to repeat the position in a hidden variable here * so that zoom/scrolling always has current position to work * from. */ // This 'dirty' field is used to check if js/ajax changes to the page have occurred. // If so and it is reached by the back button, a page reload will occur instead. char buf[256]; if (virtualSingleChrom()) // DISGUISE VMODE safef(buf, sizeof buf, "%s", windowsSpanPosition()); else safef(buf, sizeof buf, "%s:%ld-%ld", virtChromName, virtWinStart+1, virtWinEnd); hPrintf("\n"); hPrintf("", buf); hPrintf("\n%s", trackGroupsHidden1->string); hPrintf("
      \n"); hPrintf("
      \n\n", hgTracksName()); hPrintf("%s", trackGroupsHidden2->string); freeDyString(&trackGroupsHidden1); freeDyString(&trackGroupsHidden2); if (!psOutput) cartSaveSession(cart); /* Put up hgsid= as hidden variable. */ hPrintf("
      "); } /* Make line that says position. */ { char buf[256]; char *survey = cfgOptionEnv("HGDB_SURVEY", "survey"); char *surveyLabel = cfgOptionEnv("HGDB_SURVEY_LABEL", "surveyLabel"); char *javascript = "onchange=\"document.location = '/cgi-bin/hgTracks?db=' + document.TrackForm.db.options[document.TrackForm.db.selectedIndex].value;\""; if (containsStringNoCase(database, "zoo")) { hPuts("Organism "); printAssemblyListHtmlExtra(database, javascript); } if (virtualSingleChrom()) // DISGUISE VMODE safef(buf, sizeof buf, "%s", windowsSpanPosition()); else safef(buf, sizeof buf, "%s:%ld-%ld", virtChromName, virtWinStart+1, virtWinEnd); position = cloneString(buf); hPrintf("%s", addCommasToPos(database, position)); hPrintf("\n", buf); sprintLongWithCommas(buf, virtWinEnd - virtWinStart); hPrintf(" %s bp. ", buf); hPrintf("\n"); hWrites(" "); hButton("hgt.jump", "go"); if (!trackHubDatabase(database)) { jsonObjectAdd(jsonForClient, "assemblySupportsGeneSuggest", newJsonBoolean(assemblySupportsGeneSuggest(database))); if (assemblySupportsGeneSuggest(database)) hPrintf("\n", assemblyGeneSuggestTrack(database)); } if (survey && differentWord(survey, "off")) hPrintf("  " "%s\n", survey, surveyLabel ? surveyLabel : "Take survey"); hPutc('\n'); } } // TODO GALT how to handle ideos? boolean nukeIdeoFromList = FALSE; for(window=windows;window;window=window->next) { setGlobalsFromWindow(window); if (window == windows) // first window { /* Make chromosome ideogram gif and map. */ nukeIdeoFromList = makeChromIdeoImage(&trackList, psOutput, ideoTn); window->trackList = trackList; // the variable may have been updated. // TODO make this not just be centered over the entire image, // but rather centered over the individual chromosome. // notice that it modifies trackList, and visibility settings potentially need parallelization for windows } else { // TODO should be more than this. But at least this makes the same trackList mods to the other windows. if (nukeIdeoFromList) { struct track *ideoTrack = chromIdeoTrack(window->trackList); if (ideoTrack) { slRemoveEl(&window->trackList, ideoTrack); } } } } setGlobalsFromWindow(windows); // first window // restore globals #ifdef USE_NAVIGATION_LINKS hPrintf("\n", tl.picWidth);//min(tl.picWidth, 800)); hPrintf("\n"); // Without width cell expands table with, forcing others to sides hPrintf("\n"); // Without width cell expands table with, forcing others to sides hPrintf("\n"); // Without width cell expands table width, forcing others to sides hPrintf("
      <<<\n"); hPrintf("<<\n"); hPrintf("<\n"); hPrintf(" > <\n"); hPrintf(">> <<\n"); hPrintf(">>> <<<\n"); hPrintf(">base<\n"); hPrintf(" < >\n"); hPrintf("<< >>\n"); hPrintf("<<< >>>\n"); hPrintf("<<< >>>\n"); hPrintf(" >\n"); hPrintf(">>\n"); hPrintf(">>>\n"); hPrintf("
      \n"); #endif///def USE_NAVIGATION_LINKS /* Make clickable image and map. */ makeActiveImage(trackList, psOutput); fflush(stdout); if(trackImgOnly) { // bail out b/c we are done if (measureTiming) { printTrackTiming(); } return; } if (!hideControls) { struct controlGrid *cg = NULL; /* note a trick of WIDTH=27 going on here. The 6,15,6 widths following * go along with this trick */ hPrintf("\n", tl.picWidth, 27); #ifndef USE_NAVIGATION_LINKS hPrintf(""); hPrintf("\n"); #endif//ndef USE_NAVIGATION_LINKS hPrintf(""); #ifndef USE_NAVIGATION_LINKS hPrintf("\n"); hPrintf(""); #endif//ndef USE_NAVIGATION_LINKS hPrintf("
      "); hPrintf("move start
      "); hButtonWithOnClick("hgt.dinkLL", " < ", "move start position to the left", "return imageV2.navigateButtonClick(this);"); hTextVar("dinkL", cartUsualString(cart, "dinkL", "2.0"), 3); hButtonWithOnClick("hgt.dinkLR", " > ", "move start position to the right", "return imageV2.navigateButtonClick(this);"); hPrintf("
       "); // allow this text to wrap hWrites("Click on a feature for details. "); hWrites("Click or drag in the base position track to zoom in. "); hWrites("Click side bars for track options. "); hWrites("Drag side bars or labels up or down to reorder tracks. "); hWrites("Drag tracks left or right to new position. "); hWrites("Press \"?\" for keyboard shortcuts. "); hPrintf(" "); hPrintf("move end
      "); hButtonWithOnClick("hgt.dinkRL", " < ", "move end position to the left", "return imageV2.navigateButtonClick(this);"); hTextVar("dinkR", cartUsualString(cart, "dinkR", "2.0"), 3); hButtonWithOnClick("hgt.dinkRR", " > ", "move end position to the right", "return imageV2.navigateButtonClick(this);"); hPrintf("
      \n"); /* Display bottom control panel. */ if (isSearchTracksSupported(database,cart)) { cgiMakeButtonWithMsg(TRACK_SEARCH, TRACK_SEARCH_BUTTON,TRACK_SEARCH_HINT); hPrintf(" "); } hButtonWithMsg("hgt.reset", "default tracks","Display only default tracks"); hPrintf(" "); hButtonWithMsg("hgt.defaultImgOrder", "default order", "Display current tracks in their default order"); // if (showTrackControls) - always show "hide all", Hiram 2008-06-26 { hPrintf(" "); hButtonWithMsg("hgt.hideAll", "hide all","Hide all currently visibile tracks"); } hPrintf(" "); hPrintf("", hasCustomTracks ? CT_MANAGE_BUTTON_LABEL : CT_ADD_BUTTON_LABEL, hasCustomTracks ? "Manage your custom tracks" : "Add your own custom tracks"); hPrintf(" "); if (hubConnectTableExists()) { hPrintf(""); hPrintf(" "); } hButtonWithMsg("hgTracksConfigPage", "configure","Configure image and track selection"); hPrintf(" "); hButtonWithOnClick("hgTracksConfigMultiRegionPage", "multi-region", "Configure multi-region display options", "popUpHgt.hgTracks('multi-region config'); return false;"); hPrintf(" "); if (!hIsGsidServer()) { hButtonWithMsg("hgt.toggleRevCmplDisp", "reverse", revCmplDisp ? "Show forward strand at this location" : "Show reverse strand at this location"); hPrintf(" "); } hButtonWithOnClick("hgt.setWidth", "resize", "Resize image width to browser window size", "hgTracksSetWidth()"); hPrintf(" "); hButtonWithMsg("hgt.refresh", "refresh","Refresh image"); hPrintf("
      \n"); if( chromosomeColorsMade ) { hPrintf("Chromosome Color Key:
      "); hPrintf("
      \n"); } if (showTrackControls) { /* Display viewing options for each track. */ /* Chuck: This is going to be wrapped in a table so that * the controls don't wrap around randomly */ hPrintf("\n", CONTROL_TABLE_WIDTH); hPrintf(""); hPrintf("\n", MAX_CONTROL_COLUMNS - 2); hPrintf(""); if (!hIsGsidServer()) { cg = startControlGrid(MAX_CONTROL_COLUMNS, "left"); } else { /* 4 cols fit GSID's display better */ cg = startControlGrid(4, "left"); } for (group = groupList; group != NULL; group = group->next) { if (group->trackList == NULL) continue; struct trackRef *tr; /* check if group section should be displayed */ char *otherState; char *indicator; char *indicatorImg; boolean isOpen = !isCollapsedGroup(group); collapseGroupGoodies(isOpen, TRUE, &indicatorImg, &indicator, &otherState); hPrintf(""); cg->rowOpen = TRUE; if (!hIsGsidServer()) hPrintf("\n"); controlGridEndRow(cg); /* Base Position track goes into map group, which will always exist. */ if (!showedRuler && sameString(group->name, "map") ) { char *url = trackUrl(RULER_TRACK_NAME, chromName); showedRuler = TRUE; myControlGridStartCell(cg, isOpen, group->name); hPrintf("", url); hPrintf(" %s
      ", RULER_TRACK_LABEL); hPrintf("
      "); hDropListClassWithStyle("ruler", rulerMenu, sizeof(rulerMenu)/sizeof(char *), rulerMenu[rulerMode], rulerMode == tvHide ? "hiddenText" : "normalText", TV_DROPDOWN_STYLE); controlGridEndCell(cg); freeMem(url); } /* Add supertracks to track list, sort by priority and * determine if they have visible member tracks */ groupTrackListAddSuper(cart, group); // TODO GALT probably nothing to do here /* Display track controls */ for (tr = group->trackList; tr != NULL; tr = tr->next) { struct track *track = tr->track; if (tdbIsSuperTrackChild(track->tdb)) /* don't display supertrack members */ continue; myControlGridStartCell(cg, isOpen, group->name); if (track->hasUi) { char *url = trackUrl(track->track, chromName); char *longLabel = replaceChars(track->longLabel, "\"", """); hPrintPennantIcon(track->tdb); // Print an icon before the title when one is defined hPrintf("", url, longLabel); freeMem(url); freeMem(longLabel); } hPrintf(" %s", track->shortLabel); if (tdbIsSuper(track->tdb)) hPrintf("..."); hPrintf("
      "); if (track->hasUi) hPrintf("
      "); if (hTrackOnChrom(track->tdb, chromName)) { if (tdbIsSuper(track->tdb)) superTrackDropDown(cart, track->tdb, superTrackHasVisibleMembers(track->tdb)); else { /* check for option of limiting visibility to one mode */ hTvDropDownClassVisOnly(track->track, track->visibility, rTdbTreeCanPack(track->tdb), (track->visibility == tvHide) ? "hiddenText" : "normalText", trackDbSetting(track->tdb, "onlyVisibility")); } } else /* If track is not on this chrom print an informational message for the user. */ hPrintf("[No data-%s]", chromName); controlGridEndCell(cg); } /* now finish out the table */ if (group->next != NULL) controlGridEndRow(cg); } endControlGrid(&cg); } if (measureTiming) printTrackTiming(); hPrintf("\n"); } if (showTrackControls) hButton("hgt.refresh", "refresh"); hPrintf("\n"); #ifdef SLOW /* We'll rely on the end of program to do the cleanup. * It turns out that the 'free' routine on Linux is * quite slow. For chromosome level views the browser * spends about 1/3 of it's time doing the cleanup * below if it's enabled. Since we really don't * need to reclaim this memory at this point I'm * taking this out. Please don't delete the code though. * I'll like to keep it for testing now and then. -jk. */ // TODO GALT cleanup sibs too? probably can do for window copies but low priority. /* Clean up. */ for (track = trackList; track != NULL; track = track->next) { if (track->visibility != tvHide) { if (track->freeItems != NULL) track->freeItems(track); lmCleanup(&track->lm); } } #endif /* SLOW */ hPrintf("\n"); /* hidden form for custom tracks CGI */ hPrintf("", hgCustomName()); cartSaveSession(cart); hPrintf("\n"); /* hidden form for track hub CGI */ hPrintf("", hgHubConnectName()); cartSaveSession(cart); hPrintf("\n"); // this is the form for the disconnect hub button hPrintf("\n", "../cgi-bin/hgTracks"); cgiMakeHiddenVar("hubId", ""); cgiMakeHiddenVar(hgHubDoDisconnect, "on"); cgiMakeHiddenVar(hgHubConnectRemakeTrackHub, "on"); cartSaveSession(cart); puts(""); // TODO GALT nothing to do here. pruneRedundantCartVis(trackList); if (measureTiming) measureTime("Done with trackForm"); } static void toggleRevCmplDisp() /* toggle the reverse complement display mode */ { // forces complement bases to match display revCmplDisp = !revCmplDisp; cartSetBooleanDb(cart, database, REV_CMPL_DISP, revCmplDisp); cartSetBooleanDb(cart, database, COMPLEMENT_BASES_VAR, revCmplDisp); } void zoomToSize(long newSize) /* Zoom so that center stays in same place, * but window is new size. If necessary move * center a little bit to keep it from going past * edges. */ { long center = ((long long)virtWinStart + (long long)virtWinEnd)/2; if (center < 0) errAbort("zoomToSize: error computing center: %ld = (%ld + %ld)/2\n", center, virtWinStart, virtWinEnd); if (newSize > virtSeqBaseCount) newSize = virtSeqBaseCount; virtWinStart = center - newSize/2; virtWinEnd = virtWinStart + newSize; if (virtWinStart <= 0) { virtWinStart = 0; virtWinEnd = newSize; } else if (virtWinEnd > virtSeqBaseCount) { virtWinEnd = virtSeqBaseCount; virtWinStart = virtWinEnd - newSize; } virtWinBaseCount = virtWinEnd - virtWinStart; } void zoomAroundCenter(double amount) /* Set ends so as to zoom around center by scaling amount. */ { double newSizeDbl = (virtWinBaseCount*amount + 0.5); long newSize; if (newSizeDbl > virtSeqBaseCount) newSize = virtSeqBaseCount; else if (newSizeDbl < 1.0) newSize = 1; else newSize = (long)newSizeDbl; zoomToSize(newSize); } void zoomToBaseLevel() /* Set things so that it's zoomed to base level. */ { zoomToSize(fullInsideWidth/tl.mWidth); if (rulerMode == tvHide) cartSetString(cart, "ruler", "dense"); } void relativeScroll(double amount) /* Scroll percentage of visible window. */ { long offset; long newStart, newEnd; if (revCmplDisp) amount = -amount; offset = (long)(amount * virtWinBaseCount); /* Make sure don't scroll of ends. */ newStart = virtWinStart + offset; newEnd = virtWinEnd + offset; if (newStart < 0) offset = -virtWinStart; else if (newEnd > virtSeqBaseCount) offset = virtSeqBaseCount - virtWinEnd; /* Move window. */ virtWinStart += offset; virtWinEnd += offset; } void dinkWindow(boolean start, long dinkAmount) /* Move one end or other of window a little. */ { if (revCmplDisp) { start = !start; dinkAmount = -dinkAmount; } if (start) { virtWinStart += dinkAmount; if (virtWinStart < 0) virtWinStart = 0; } else { virtWinEnd += dinkAmount; if (virtWinEnd > virtSeqBaseCount) virtWinEnd = virtSeqBaseCount; } } long dinkSize(char *var) /* Return size to dink. */ { char *stringVal = cartOptionalString(cart, var); double x; int fullInsideX = trackOffsetX(); /* The global versions of these are not yet set */ int fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX; double guideBases = (double)guidelineSpacing * (double)(virtWinEnd - virtWinStart) / ((double)fullInsideWidth); if (stringVal == NULL || !isdigit(stringVal[0])) { stringVal = "1"; cartSetString(cart, var, stringVal); } x = atof(stringVal); long ret = round(x*guideBases); return (ret == 0) ? 1 : ret; } void handlePostscript() /* Deal with Postscript output. */ { struct tempName psTn, ideoPsTn; char *pdfFile = NULL, *ideoPdfFile = NULL; ZeroVar(&ideoPsTn); trashDirFile(&psTn, "hgt", "hgt", ".eps"); if(!trackImgOnly) { printMenuBar(); printf("
      \n"); printf("

      PDF Output

      \n"); printf("PDF images can be printed with Acrobat Reader " "and edited by many drawing programs such as Adobe " "Illustrator or Inkscape.
      "); } doTrackForm(psTn.forCgi, &ideoPsTn); pdfFile = convertEpsToPdf(psTn.forCgi); if (strlen(ideoPsTn.forCgi)) ideoPdfFile = convertEpsToPdf(ideoPsTn.forCgi); if (pdfFile != NULL) { printf("\n"); freez(&pdfFile); freez(&ideoPdfFile); // postscript printf("EPS (Postscript) images are a variant of PDF and easier to import into some " "drawing programs.\n"); printf("\n"); // see redmine #1077 printf("
      Tips for producing quality images for publication:
      \n"); printf("
        \n"); printf("
      • Add assembly name and chromosome range to the image on the\n" "configuration page of the base position track.\n"); printf("
      • If using the UCSC Genes track, consider showing only one transcript per gene by turning off splice variants on the track configuration page.\n"); printf("
      • Increase the font size and remove the light blue vertical guidelines in the \n" "image configuration menu."); printf("
      • In the image configuration menu, change the size of the image,\n" "to make it look more square.\n"); printf("
      \n"); printf("
      \n"); } else printf("

      PDF format not available"); printf("\n", hgTracksName(), cartSessionVarName(), cartSessionId(cart)); } boolean isGenome(char *pos) /* Return TRUE if pos is genome. */ { pos = trimSpaces(pos); return(sameWord(pos, "genome") || sameWord(pos, "hgBatch")); } void setRulerMode() /* Set the rulerMode variable from cart. */ { char *s = cartUsualString(cart, RULER_TRACK_NAME, "dense"); if (sameWord(s, "full") || sameWord(s, "on")) rulerMode = tvFull; else if (sameWord(s, "dense")) rulerMode = tvDense; else rulerMode = tvHide; } void setLayoutGlobals() /* Figure out basic dimensions of display. */ { withIdeogram = cartUsualBoolean(cart, "ideogram", TRUE); withLeftLabels = cartUsualBoolean(cart, "leftLabels", TRUE); withCenterLabels = cartUsualBoolean(cart, "centerLabels", TRUE); withGuidelines = cartUsualBoolean(cart, "guidelines", TRUE); if (!cartUsualBoolean(cart, "hgt.imageV1", FALSE)) { withNextItemArrows = cartUsualBoolean(cart, "nextItemArrows", FALSE); withNextExonArrows = cartUsualBoolean(cart, "nextExonArrows", TRUE); } withExonNumbers = cartUsualBoolean(cart, "exonNumbers", TRUE); emAltHighlight = cartUsualBoolean(cart, "emAltHighlight", FALSE); if (!hIsGsidServer()) { revCmplDisp = cartUsualBooleanDb(cart, database, REV_CMPL_DISP, FALSE); } emPadding = cartUsualInt(cart, "emPadding", emPadding); gmPadding = cartUsualInt(cart, "gmPadding", gmPadding); withPriorityOverride = cartUsualBoolean(cart, configPriorityOverride, FALSE); fullInsideX = trackOffsetX(); fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX; } void parseVirtPosition(char *position) /* parse virtual position * TODO this is just temporary */ { if (!position) { errAbort("position NULL"); } char *vPos = cloneString(position); char *colon = strchr(vPos, ':'); if (!colon) errAbort("position has no colon"); char *dash = strchr(vPos, '-'); if (!dash) errAbort("position has no dash"); *colon = 0; *dash = 0; virtWinStart = atol(colon+1) - 1; virtWinEnd = atol(dash+1); } void parseNonVirtPosition(char *position) /* parse non-virtual position */ { if (!position) { errAbort("position NULL"); } char *vPos = cloneString(position); char *colon = strchr(vPos, ':'); if (!colon) errAbort("position has no colon"); char *dash = strchr(vPos, '-'); if (!dash) errAbort("position has no dash"); *colon = 0; *dash = 0; chromName = cloneString(vPos); winStart = atol(colon+1) - 1; winEnd = atol(dash+1); } boolean findNearestVirtMatch(char *chrom, int start, int end, boolean findNearest, long *retVirtStart, long *retVirtEnd) /* find nearest match on virt chrom. * findNearest flag means of no direct hits found, take the closest miss. */ { // search for one or more overlapping windows struct positionMatch *mList = virtChromSearchForPosition(chrom, start, end, findNearest); // sort positions by virtPos (will be sorted by chrom, start, end) matchSortOnVPos(&mList); // merge contiguous matches spanning multiple touching windows mList = matchMergeContiguousVPos(mList); // TODO search for the best match in pList // TODO this is crude, needs to fix, just finds the largest match: struct positionMatch *p, *best = NULL; long bigSpan = 0; for (p=mList; p; p=p->next) { long span = p->virtEnd - p->virtStart; if (span > bigSpan) { bigSpan = span; best = p; } } if (best) // TODO do something better { // return the new location *retVirtStart = best->virtStart; *retVirtEnd = best->virtEnd; } else { return FALSE; } return TRUE; } void remapHighlightPos() // Remap non-virt highlight position if any to new virtMode chrom. { if (virtualSingleChrom()) return; struct highlightVar *h = parseHighlightInfo(); if (h && h->db && sameString(h->db, database)) { long virtStart = 0, virtEnd = 0; if (findNearestVirtMatch(h->chrom, h->chromStart, h->chromEnd, FALSE, &virtStart, &virtEnd)) // try to find the nearest match { // save new highlight position to cart var char cartVar[1024]; safef(cartVar, sizeof cartVar, "%s.%s:%ld-%ld#%s", h->db, "virt", virtStart, virtEnd, h->hexColor); cartSetString(cart, "highlight", cartVar); } else { // erase the highlight cartvar if it has no overlap with the new virt chrom cartRemove(cart, "highlight"); } } } void tracksDisplay() /* Put up main tracks display. This routine handles zooming and * scrolling. */ { char *defaultPosition = hDefaultPos(database); char titleVar[256]; char *oldPosition = cartUsualString(cart, "oldPosition", ""); boolean findNearest = cartUsualBoolean(cart, "findNearest", FALSE); cartRemove(cart, "findNearest"); boolean positionIsVirt = FALSE; position = getPositionFromCustomTracks(); if (NULL == position) { position = cartGetPosition(cart, database, &lastDbPosCart); if (sameOk(cgiOptionalString("position"), "lastDbPos")) { restoreSavedVirtPosition(); } if (startsWith("virt:", position)) { position = stripCommas(position); // sometimes the position string arrives with commas in it. positionIsVirt = TRUE; } } if (sameString(position, "")) { hUserAbort("Please go back and enter a coordinate range or a search term in the \"search term\" field.
      For example: chr22:20100000-20200000.\n"); } if (!positionIsVirt) { chromName = NULL; winStart = 0; if (isGenome(position) || NULL == (hgp = findGenomePos(database, position, &chromName, &winStart, &winEnd, cart))) { if (winStart == 0) /* number of positions found */ { freeMem(position); position = cloneString(cartUsualString(cart, "lastPosition", defaultPosition)); hgp = findGenomePos(database, position, &chromName, &winStart, &winEnd,cart); if (hgp != NULL && differentString(position, defaultPosition)) cartSetString(cart, "position", position); } } /* After position is found set up hash of matches that should be drawn with names highlighted for easy identification. */ createHgFindMatchHash(); /* This means that no single result was found I.e., multiple results may have been found and are printed out prior to this code*/ if (NULL == chromName) { // In case user manually edits the browser location as described in #13009, // revert the position. If they instead choose from the list as we expect, // that will set the position to their choice. // lastPosition gets set in cart.c char *lastPosition = cartUsualString(cart, "lastPosition", hDefaultPos(database)); cartSetString(cart, "position", lastPosition); return; } } virtMode = cartUsualBoolean(cart, "virtMode", FALSE); /* Figure out basic dimensions of display. This * needs to be done early for the sake of the * zooming and dinking routines. */ setLayoutGlobals(); virtModeType = cartUsualString(cart, "virtModeType", virtModeType); if (positionIsVirt && virtualSingleChrom()) { // we need chromName to be set before initRegionList() gets called. position = cartUsualString(cart, "nonVirtPosition", ""); if (!sameString(position,"")) parseNonVirtPosition(position); } // TODO GALT do we need to add in other types that now depend on emGeneTable too? maybe singleTrans? // OR maybe this code should just be part of initRegionList() if (sameString(virtModeType, "exonMostly") || sameString(virtModeType, "geneMostly")) { setEMGeneTrack(); if (!emGeneTable) // there is no available gene table, undo exonMostly or geneMostly { //warn("setEMGeneTrack unable to find default gene track"); virtModeType = "default"; cartSetString(cart, "virtModeType", virtModeType); } } lastVirtModeType = cartUsualString(cart, "lastVirtModeType", lastVirtModeType); while(TRUE) { if (sameString(virtModeType, "default") && !(sameString(lastVirtModeType, "default"))) { // RETURNING TO DEFAULT virtModeType virtModeType = "default"; cartSetString(cart, "virtModeType", virtModeType); findNearest = TRUE; if (positionIsVirt) position = cartUsualString(cart, "nonVirtPosition", ""); char *nvh = cartUsualString(cart, "nonVirtHighlight", NULL); if (nvh) cartSetString(cart, "highlight", nvh); if (!sameString(position,"")) parseNonVirtPosition(position); } if (initRegionList()) // initialize the region list, sets virtModeExtraState { break; } else { // virt mode failed, forced to return to default virtModeType = "default"; cartSetString(cart, "virtModeType", virtModeType); position = cloneString(hDefaultPos(database)); hgp = findGenomePos(database, position, &chromName, &winStart, &winEnd, cart); cartSetString(cart, "position", position); positionIsVirt=FALSE; virtMode=FALSE; } } // PAD padding of exon regions is now being done inside the fetch/merge. //if (emPadding > 0) //padVirtRegions(emPadding); // this old routine does not handle multiple chroms yet //testRegionList(); // check if it is ascending non-overlapping regions. (this is not the case with custom user-defined-regions) makeVirtChrom(); //testVirtChromBinarySearch(); // ajax callback to convert chrom position to virt chrom position if (cartVarExists(cart, "hgt.convertChromToVirtChrom")) { position = cartString(cart, "hgt.convertChromToVirtChrom"); char nvh[256]; safef(nvh, sizeof nvh, "%s.%s", database, position); cartSetString(cart, "nonVirtHighlight", nvh); parseNonVirtPosition(position); if (findNearestVirtMatch(chromName, winStart, winEnd, FALSE, &virtWinStart, &virtWinEnd)) { struct jsonElement *jsonForConvert = NULL; jsonForConvert = newJsonObject(newHash(8)); jsonObjectAdd(jsonForConvert, "virtWinStart", newJsonNumber(virtWinStart)); jsonObjectAdd(jsonForConvert, "virtWinEnd", newJsonNumber(virtWinEnd)); hPrintf("\n"); } return; } lastVirtModeExtraState = cartUsualString(cart, "lastVirtModeExtraState", lastVirtModeExtraState); // DISGUISED POSITION if (!startsWith("virt:", position) && (virtualSingleChrom())) { // "virtualSingleChrom trying to find best vchrom location corresponding to chromName, winStart, winEnd findNearest = TRUE; // try to find the nearest match if (!(chromName && findNearestVirtMatch(chromName, winStart, winEnd, findNearest, &virtWinStart, &virtWinEnd))) { // create 1k window near middle of vchrom warn("Unable to find any region near the position on the chromosome in the multi-regions. Now using middle of view."); virtWinStart = virtSeqBaseCount / 2; virtWinEnd = virtWinStart + 1000; if (virtWinEnd > virtSeqBaseCount) virtWinEnd = virtSeqBaseCount; } virtMode = TRUE; } // when changing modes (or state like padding), first try to revert to plain non-virt position if (!sameString(virtModeType, "default") && !sameString(lastVirtModeType, "default") && !(sameString(virtModeType, lastVirtModeType) && sameString(virtModeExtraState, lastVirtModeExtraState))) { // CHANGE FROM ONE NON-DEFAULT virtMode to another. virtChromChanged = TRUE; // virtChrom changed lastVirtModeType = "default"; cartSetString(cart, "lastVirtModeType", lastVirtModeType); // I think I do not need this lastVirtModeExtraState = ""; findNearest = TRUE; position = cartUsualString(cart, "nonVirtPosition", ""); if (!sameString(position,"")) parseNonVirtPosition(position); char *nvh = cartUsualString(cart, "nonVirtHighlight", ""); if (!sameString(nvh, "")) // REMOVE? not needed probably { cartSetString(cart, "highlight", nvh); } } // virt mode has not changed if (sameString(virtModeType, lastVirtModeType) && sameString(virtModeExtraState, lastVirtModeExtraState)) { if (virtMode) { if (positionIsVirt) { parseVirtPosition(position); } else { // Is this a new position to navigate to // or just an old inherited position. position = stripCommas(position); // sometimes the position string arrives with commas in it. if (!sameString(position, oldPosition)) { if (!findNearestVirtMatch(chromName, winStart, winEnd, findNearest, &virtWinStart, &virtWinEnd)) { // errAbort has kind of harsh behavior, and does not work well with ajax anyways warn("Location not found in Multi-Region View. " "To return to default view at that location, " "click here.\n" , hgTracksName(), cartSessionVarName(), cartSessionId(cart), chromName, winStart+1, winEnd); // try to resume using oldPosition parseVirtPosition(oldPosition); } } } } else { if (positionIsVirt) errAbort("positionIsVirt=%d but virtMode=%d", positionIsVirt, virtMode); } } else { if (sameString(virtModeType,"default")) // we are leaving virtMode { virtMode = FALSE; } else { // ENTERING VIRTMODE // First time initialization findNearest = TRUE; // For now, do this manually here: // sets window to full genome size, which for these demos should be small except for allChroms if (sameString(virtModeType, "exonMostly") || sameString(virtModeType, "geneMostly") || sameString(virtModeType, "customUrl") || sameString(virtModeType, "kcGenes")) { // trying to find best vchrom location corresponding to chromName, winStart, winEnd); // try to find the nearest match if (!(chromName && findNearestVirtMatch(chromName, winStart, winEnd, findNearest, &virtWinStart, &virtWinEnd))) { // create 1k window near middle of vchrom warn("Unable to find any region near the position on the chromosome in the multi-regions. Now using middle of view."); virtWinStart = virtSeqBaseCount / 2; virtWinEnd = virtWinStart + 1000; if (virtWinEnd > virtSeqBaseCount) virtWinEnd = virtSeqBaseCount; } virtMode = TRUE; } else if (sameString(virtModeType, "singleAltHaplo")) { virtWinStart = defaultVirtWinStart; virtWinEnd = defaultVirtWinEnd; virtMode = TRUE; } else if (!sameString(virtModeType, "default")) { // try to set view to entire vchrom virtWinStart = 0; virtWinEnd = virtSeqBaseCount; virtMode = TRUE; // TODO what if the full-vchrom view has "too many windows" // check if virtRegionCount > 4000? } remapHighlightPos(); } } if (virtMode) virtChromName = "virt"; else virtChromName = chromName; virtWinBaseCount = virtWinEnd - virtWinStart; baseShowPos = cartUsualBoolean(cart, BASE_SHOWPOS, FALSE); baseShowAsm = cartUsualBoolean(cart, BASE_SHOWASM, FALSE); baseShowScaleBar = cartUsualBoolean(cart, BASE_SCALE_BAR, TRUE); baseShowRuler = cartUsualBoolean(cart, BASE_SHOWRULER, TRUE); safef(titleVar,sizeof(titleVar),"%s_%s", BASE_TITLE, database); baseTitle = cartUsualString(cart, titleVar, ""); if (sameString(baseTitle, "")) baseTitle = NULL; if (cgiVarExists("hgt.toggleRevCmplDisp")) toggleRevCmplDisp(); setRulerMode(); /* Do zoom/scroll if they hit it. */ if (cgiVarExists("hgt.left3")) relativeScroll(-0.95); else if (cgiVarExists("hgt.left2")) relativeScroll(-0.475); else if (cgiVarExists("hgt.left1")) relativeScroll(-0.1); else if (cgiVarExists("hgt.right1")) relativeScroll(0.1); else if (cgiVarExists("hgt.right2")) relativeScroll(0.475); else if (cgiVarExists("hgt.right3")) relativeScroll(0.95); else if (cgiVarExists("hgt.inBase")) zoomToBaseLevel(); else if (cgiVarExists("hgt.in3")) zoomAroundCenter(1.0/10.0); else if (cgiVarExists("hgt.in2")) zoomAroundCenter(1.0/3.0); else if (cgiVarExists("hgt.in1")) zoomAroundCenter(1.0/1.5); else if (cgiVarExists("hgt.out1")) zoomAroundCenter(1.5); else if (cgiVarExists("hgt.out2")) zoomAroundCenter(3.0); else if (cgiVarExists("hgt.out3")) zoomAroundCenter(10.0); else if (cgiVarExists("hgt.out4")) zoomAroundCenter(100.0); else if (cgiVarExists("hgt.dinkLL")) dinkWindow(TRUE, -dinkSize("dinkL")); else if (cgiVarExists("hgt.dinkLR")) dinkWindow(TRUE, dinkSize("dinkL")); else if (cgiVarExists("hgt.dinkRL")) dinkWindow(FALSE, -dinkSize("dinkR")); else if (cgiVarExists("hgt.dinkRR")) dinkWindow(FALSE, dinkSize("dinkR")); /* Before loading items, deal with the next/prev item arrow buttons if pressed. */ if (cgiVarExists("hgt.nextItem")) doNextPrevItem(TRUE, cgiUsualString("hgt.nextItem", NULL)); else if (cgiVarExists("hgt.prevItem")) doNextPrevItem(FALSE, cgiUsualString("hgt.prevItem", NULL)); /* Clip chromosomal position to fit. */ if (virtWinEnd < virtWinStart) { // swap start and end (user entered coordinates backwards) long temp = virtWinEnd; virtWinEnd = virtWinStart; virtWinStart = temp; } else if (virtWinStart == virtWinEnd) { // Size 0 window virtWinStart -= 1; virtWinEnd += 1; } if (virtWinStart < 0) { virtWinStart = 0; } if (virtWinEnd > virtSeqBaseCount) { virtWinEnd = virtSeqBaseCount; } if (virtWinStart > virtSeqBaseCount) { virtWinStart = virtSeqBaseCount - 1000; } virtWinBaseCount = virtWinEnd - virtWinStart; if (virtWinBaseCount <= 0) hUserAbort("Window out of range on %s", virtChromName); if (!cartUsualBoolean(cart, "hgt.psOutput", FALSE) && !cartUsualBoolean(cart, "hgt.imageV1" , FALSE)) { // TODO GALT Guidelines broken on virtChrom for 3X. // works in demo0 or real chrom. Only the guidelines seem to be messed up. // Other stuff works. 1X works too. // Since we are not using 3X for now, I will leave this for a future fix. // To test 3X, do make clean; make CFLAGS=-DIMAGEv2_DRAG_SCROLL_SZ=3 // Start an imagebox (global for now to avoid huge rewrite of hgTracks) // Set up imgBox dimensions int sideSliceWidth = 0; // Just being explicit if (withLeftLabels) sideSliceWidth = (fullInsideX - gfxBorder*3) + 2; // for the 3X expansion effect to work, this needs to happen BEFORE we create the windows list // in makeWindowListFromVirtChrom() theImgBox = imgBoxStart(database,virtChromName,virtWinStart,virtWinEnd, (!revCmplDisp),sideSliceWidth,tl.picWidth); // Define a portal with a default expansion size, // then set the global dimensions to the full image size if (imgBoxPortalDefine(theImgBox,&virtWinStart,&virtWinEnd,&(tl.picWidth),0)) { virtWinBaseCount = virtWinEnd - virtWinStart; fullInsideWidth = tl.picWidth - gfxBorder - fullInsideX; } } // For portal 3x expansion to work right, it would have to take effect, at least temporarily, // right here before we call makeWindowListFromVirtChrom(). windows = makeWindowListFromVirtChrom(virtWinStart, virtWinEnd); // creates windows, sets chrom, winStart, winEnd from virtual chrom if (slCount(windows) > 4000) // TODO a more graceful response errAbort("Too many windows in view. Unable to display image at requested zoom level."); allocPixelsToWindows(); // sets windows insideWidth and insideX if (theImgBox) { // If a portal was established, then set the global dimensions back to the portal size if (imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL)) { virtWinBaseCount = virtWinEnd - virtWinStart; fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX; } } setGlobalsFromWindow(windows); // first window seqBaseCount = hChromSize(database, chromName); /* Save computed position in cart. */ cartSetString(cart, "org", organism); cartSetString(cart, "db", database); char newPos[256]; // disguise the cart pos var if (virtualSingleChrom()) // DISGUISE VMODE safef(newPos, sizeof newPos, "%s", windowsSpanPosition()); else // usual safef(newPos, sizeof newPos, "%s:%ld-%ld", virtChromName, virtWinStart+1, virtWinEnd); position = cloneString(newPos); cartSetString(cart, "position", position); cartSetString(cart, "oldPosition", position); //cartSetString(cart, "lastPosition", position); // this is set in cart.c // TODO GALT is it possible and worthwhile to just use lastPosition instead of oldPosition? cartSetBoolean(cart, "virtMode", virtMode); cartSetString(cart, "virtModeType", virtModeType); virtModeType = cartString(cart, "virtModeType"); // refresh the pointer after changing hash lastVirtModeType=virtModeType; cartSetString(cart, "lastVirtModeType", lastVirtModeType); lastVirtModeType = cartString(cart, "lastVirtModeType"); // refresh lastVirtModeExtraState=virtModeExtraState; cartSetString(cart, "lastVirtModeExtraState", lastVirtModeExtraState); lastVirtModeExtraState = cartString(cart, "lastVirtModeExtraState"); // refresh // save a quick position to use if user leaves virtMode. if (virtMode) cartSetString(cart, "nonVirtPosition", nonVirtPositionFromWindows()); else cartRemove(cart, "nonVirtPosition"); // save a highlight position to use if user leaves virtMode. char *nvh = NULL; if (virtMode) nvh = nonVirtPositionFromHighlightPos(); if (virtMode && nvh) cartSetString(cart, "nonVirtHighlight", nvh); else cartRemove(cart, "nonVirtHighlight"); // save lastDbPos. save the current position and other important cart vars related to virtual view. lastDbPosSaveCartSetting("position"); lastDbPosSaveCartSetting("nonVirtPosition"); lastDbPosSaveCartSetting("virtMode"); lastDbPosSaveCartSetting("virtModeType"); lastDbPosSaveCartSetting("lastVirtModeType"); lastDbPosSaveCartSetting("lastVirtModeExtraState"); cartSetDbPosition(cart, database, lastDbPosCart); if (cartUsualBoolean(cart, "hgt.psOutput", FALSE)) handlePostscript(); else doTrackForm(NULL, NULL); } void chromInfoTotalRow(int count, long long total) /* Make table row with total number of sequences and size from chromInfo. */ { cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); printf("Total: %d", count); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); printLongWithCommas(stdout, total); cgiTableFieldEnd(); cgiTableRowEnd(); } void chromInfoRowsChromExt(char *sortType) /* Make table rows of chromosomal chromInfo name & size, sorted by name. */ { struct slName *chromList = hAllChromNames(database); struct slName *chromPtr = NULL; long long total = 0; if (sameString(sortType,"default")) slSort(&chromList, chrSlNameCmp); else if (sameString(sortType,"withAltRandom")) slSort(&chromList, chrSlNameCmpWithAltRandom); else errAbort("unknown sort type in chromInfoRowsChromExt: %s", sortType); for (chromPtr = chromList; chromPtr != NULL; chromPtr = chromPtr->next) { unsigned size = hChromSize(database, chromPtr->name); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); printf("%s", hgTracksName(), cartSessionVarName(), cartSessionId(cart), chromPtr->name, chromPtr->name); cgiTableFieldEnd(); cgiTableFieldStartAlignRight(); printLongWithCommas(stdout, size); puts("  "); cgiTableFieldEnd(); cgiTableRowEnd(); total += size; } chromInfoTotalRow(slCount(chromList), total); slFreeList(&chromList); } void chromInfoRowsChrom() /* Make table rows of chromosomal chromInfo name & size, sorted by name. */ { chromInfoRowsChromExt("default"); } static int chromInfoCmpSize(const void *va, const void *vb) /* Compare to sort based on chrom size */ { const struct chromInfo *a = *((struct chromInfo **)va); const struct chromInfo *b = *((struct chromInfo **)vb); return b->size - a->size; } void chromInfoRowsNonChromTrackHub(int limit) /* Make table rows of non-chromosomal chromInfo name & size */ /* leaks chromInfo list */ { struct chromInfo *chromInfo = trackHubAllChromInfo(database); slSort(&chromInfo, chromInfoCmpSize); int seqCount = slCount(chromInfo); long long total = 0; char msg1[512], msg2[512]; boolean truncating; int count = limit; truncating = (limit > 0) && (seqCount > limit); for(;count-- && (chromInfo != NULL); chromInfo = chromInfo->next) { unsigned size = chromInfo->size; cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); printf("%s", hgTracksName(), cartSessionVarName(), cartSessionId(cart), chromInfo->chrom,chromInfo->chrom); cgiTableFieldEnd(); cgiTableFieldStartAlignRight(); printLongWithCommas(stdout, size); puts("  "); cgiTableFieldEnd(); cgiTableRowEnd(); total += size; } if (!truncating) { chromInfoTotalRow(seqCount, total); } else { safef(msg1, sizeof(msg1), "Limit reached"); safef(msg2, sizeof(msg2), "%d rows displayed", limit); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); puts(msg1); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); puts(msg2); cgiTableFieldEnd(); for(;limit-- && (chromInfo != NULL); chromInfo = chromInfo->next) total += chromInfo->size; unsigned scafCount = seqCount; unsigned totalSize = total; cgiTableRowEnd(); safef(msg1, sizeof(msg1), "contig/scaffold
      count:"); safef(msg2, sizeof(msg2), "total size:"); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); puts(msg1); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); puts(msg2); cgiTableFieldEnd(); cgiTableRowEnd(); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); printLongWithCommas(stdout, scafCount); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); printLongWithCommas(stdout, totalSize); cgiTableFieldEnd(); cgiTableRowEnd(); } } void chromInfoRowsNonChrom(int limit) /* Make table rows of non-chromosomal chromInfo name & size, sorted by size. */ { if (trackHubDatabase(database)) { chromInfoRowsNonChromTrackHub(limit); return; } struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr = NULL; char **row = NULL; long long total = 0; char query[512]; char msg1[512], msg2[512]; int seqCount = 0; boolean truncating; seqCount = sqlQuickNum(conn, NOSQLINJ "select count(*) from chromInfo"); truncating = (limit > 0) && (seqCount > limit); if (!truncating) { sr = sqlGetResult(conn, NOSQLINJ "select chrom,size from chromInfo order by size desc"); } else { sqlSafef(query, sizeof(query), "select chrom,size from chromInfo order by size desc limit %d", limit); sr = sqlGetResult(conn, query); } while ((row = sqlNextRow(sr)) != NULL) { unsigned size = sqlUnsigned(row[1]); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); printf("%s", hgTracksName(), cartSessionVarName(), cartSessionId(cart), row[0], row[0]); cgiTableFieldEnd(); cgiTableFieldStartAlignRight(); printLongWithCommas(stdout, size); puts("  "); cgiTableFieldEnd(); cgiTableRowEnd(); total += size; } if (!truncating) { chromInfoTotalRow(seqCount, total); } else { safef(msg1, sizeof(msg1), "Limit reached"); safef(msg2, sizeof(msg2), "%d rows displayed", limit); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); puts(msg1); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); puts(msg2); cgiTableFieldEnd(); sqlFreeResult(&sr); sqlSafef(query, sizeof(query), "select count(*),sum(size) from chromInfo"); sr = sqlGetResult(conn, query); if ((row = sqlNextRow(sr)) != NULL) { unsigned scafCount = sqlUnsigned(row[0]); unsigned totalSize = sqlUnsigned(row[1]); cgiTableRowEnd(); safef(msg1, sizeof(msg1), "contig/scaffold
      count:"); safef(msg2, sizeof(msg2), "total size:"); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); puts(msg1); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); puts(msg2); cgiTableFieldEnd(); cgiTableRowEnd(); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); printLongWithCommas(stdout, scafCount); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); printLongWithCommas(stdout, totalSize); cgiTableFieldEnd(); } cgiTableRowEnd(); } sqlFreeResult(&sr); hFreeConn(&conn); } static void chromSizesDownloadRow() /* Show link to chrom.sizes file at end of chromInfo table (unless this is a hub) */ { if (! trackHubDatabase(database)) { cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); puts("Download as file"); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); printf("%s.chrom.sizes", hDownloadsServer(), database, database, database); cgiTableFieldEnd(); cgiTableRowEnd(); } } void chromInfoPage() /* Show list of chromosomes (or scaffolds, etc) on which this db is based. */ { char *position = cartUsualString(cart, "position", hDefaultPos(database)); char *defaultChrom = hDefaultChrom(database); char *freeze = hFreezeFromDb(database); struct dyString *title = dyStringNew(512); if (freeze == NULL) dyStringPrintf(title, "%s Browser Sequences", hOrganism(database)); else if (stringIn(database, freeze)) dyStringPrintf(title, "%s %s Browser Sequences", hOrganism(database), freeze); else dyStringPrintf(title, "%s %s (%s) Browser Sequences", trackHubSkipHubName(hOrganism(database)), freeze, trackHubSkipHubName(database)); webStartWrapperDetailedNoArgs(cart, database, "", title->string, FALSE, FALSE, FALSE, FALSE); printf("\n", hgTracksName()); cartSaveSession(cart); puts("Enter a position, or click on a sequence name to view the entire " "sequence in the genome browser.

      "); puts("position "); hTextVar("position", addCommasToPos(database, position), 30); cgiMakeButton("Submit", "submit"); puts("

      "); hTableStart(); cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); puts("Sequence name  "); cgiTableFieldEnd(); cgiSimpleTableFieldStart(); puts("Length (bp) including gaps  "); cgiTableFieldEnd(); cgiTableRowEnd(); if (sameString(database,"hg38")) chromInfoRowsChromExt("withAltRandom"); else if ((startsWith("chr", defaultChrom) || startsWith("Group", defaultChrom)) && hChromCount(database) < 100) chromInfoRowsChrom(); else chromInfoRowsNonChrom(1000); chromSizesDownloadRow(); hTableEnd(); cgiDown(0.9); hgPositionsHelpHtml(organism, database); puts(""); dyStringFree(&title); webEndSectionTables(); } void resetVars() /* Reset vars except for position and database. */ { static char *except[] = {"db", "position", NULL}; char *cookieName = hUserCookie(); char *sessionId = cgiOptionalString(cartSessionVarName()); char *userId = findCookieData(cookieName); struct cart *oldCart = cartNew(userId, sessionId, NULL, NULL); cartRemoveExcept(oldCart, except); cartCheckout(&oldCart); cgiVarExcludeExcept(except); } void setupHotkeys(boolean gotExtTools) /* setup keyboard shortcuts and a help dialog for it */ { // wire the keyboard hotkeys hPrintf("\n"); // help dialog hPrintf("

      \n"); hPrintf("
      \n"); hButtonWithOnClick("hgt.collapseGroups", "collapse all", "collapse all track groups", "return vis.expandAllGroups(false)"); hPrintf("" "Use drop-down controls below and press refresh to alter tracks " "displayed.
      " "Tracks with lots of items will automatically be displayed in " "more compact modes.
      "); hButtonWithOnClick("hgt.expandGroups", "expand all", "expand all track groups", "return vis.expandAllGroups(true)"); hPrintf("
      ",MAX_CONTROL_COLUMNS); else hPrintf("", MAX_CONTROL_COLUMNS-1); hPrintf("
      "); hPrintf("\n",group->name); hPrintf("\"%s\"  ", group->name, group->name, indicatorImg, indicator,isOpen?"Collapse":"Expand"); hPrintf("\n%s", group->label); hPrintf("\n"); if (isHubTrack(group->name)) hPrintf("\n", &group->name[sizeof hubTrackPrefix - 1]); hPrintf("\n"); hPrintf("
      \n"); hPrintf("\n"); // percent sign hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); // percent sign hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); hPrintf(""); if (gotExtTools) hPrintf(""); hPrintf(" \n"); hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); -hPrintf("\n"); -hPrintf("\n"); -hPrintf("\n"); -hPrintf("\n"); -hPrintf("\n"); +hPrintf("\n"); +hPrintf("\n"); +hPrintf("\n"); +hPrintf("\n"); +hPrintf("\n"); hPrintf("
      left 10%ctrl+j track searcht then s
      left 1/2 screenj default tracksd then t
      left one screenJ default orderd then o
      right 10%ctrl+l hide allh then a
      right 1/2 screenl custom tracksc then t
      right one screenL track hubst then h
      zoom in 1.5xctrl+i configurec then f
      zoom in 3xi reverser then v
      zoom in 10xI resizer then s
      zoom in base levelb refreshr then f
      zoom out 1.5xctrl+k jump to position box/
      zoom out 3xksend to external tools then t
      zoom out 10xK exon viewe then v
      zoom out 100x0 default viewd then v
      zoom to ... view DNAv then d
       10bp (1 zero)1
       100bp (2 zeroes)2
       1000bp (3 zeroes)3
       50kbp (4 zeroes)4
       100kbp (5 zeroes)5
       1Mbp (6 zeroes)6
       100bp (2 zeros)2
       1000bp (3 zeros)3
       50kbp (4 zeros)4
       100kbp (5 zeros)5
       1Mbp (6 zeros)6
      \n"); hPrintf(""); hPrintf("\n"); } void doMiddle(struct cart *theCart) /* Print the body of an html file. */ { cart = theCart; measureTiming = hPrintStatus() && isNotEmpty(cartOptionalString(cart, "measureTiming")); if (measureTiming) measureTime("Startup"); hgBotDelay(); if (measureTiming) measureTime("Bottleneck delay"); char *debugTmp = NULL; /* Uncomment this to see parameters for debugging. */ /* struct dyString *state = NULL; */ /* Initialize layout and database. */ if (measureTiming) measureTime("Get cart of %d for user:%s session:%s", theCart->hash->elCount, theCart->userId, theCart->sessionId); /* #if 1 this to see parameters for debugging. */ /* Be careful though, it breaks if custom track * is more than 4k */ #if 0 state = cgiUrlString(); printf("State: %s\n", state->string); #endif getDbAndGenome(cart, &database, &organism, oldVars); initGenbankTableNames(database); protDbName = hPdbFromGdb(database); debugTmp = cartUsualString(cart, "hgDebug", "off"); if(sameString(debugTmp, "on")) hgDebug = TRUE; else hgDebug = FALSE; int timeout = cartUsualInt(cart, "udcTimeout", 300); if (udcCacheTimeout() < timeout) udcSetCacheTimeout(timeout); // tell UDC where to put its statistics file char *udcLogFile; if ((udcLogFile = cfgOption("udcLog")) != NULL) { FILE *fp = mustOpen(udcLogFile, "a"); udcSetLog(fp); } initTl(); char *configPageCall = cartCgiUsualString(cart, "hgTracksConfigPage", "notSet"); char *configMultiRegionPageCall = cartCgiUsualString(cart, "hgTracksConfigMultiRegionPage", "notSet"); /* Do main display. */ if (cartUsualBoolean(cart, "hgt.trackImgOnly", FALSE)) { trackImgOnly = TRUE; ideogramToo = cartUsualBoolean(cart, "hgt.ideogramToo", FALSE); hideControls = TRUE; withNextItemArrows = FALSE; withNextExonArrows = FALSE; hgFindMatches = NULL; // XXXX necessary ??? } jsonForClient = newJsonObject(newHash(8)); jsonObjectAdd(jsonForClient, "cgiVersion", newJsonString(CGI_VERSION)); boolean searching = differentString(cartUsualString(cart, TRACK_SEARCH,"0"), "0"); if(!trackImgOnly) { // Write out includes for css and js files hWrites(commonCssStyles()); jsIncludeFile("mousetrap.min.js", NULL); jsIncludeFile("jquery.js", NULL); jsIncludeFile("jquery-ui.js", NULL); jsIncludeFile("utils.js", NULL); jsIncludeFile("ajax.js", NULL); jsIncludeFile("jquery.watermarkinput.js", NULL); if(!searching) { jsIncludeFile("jquery.history.js", NULL); jsIncludeFile("jquery.imgareaselect.js", NULL); } jsIncludeFile("autocomplete.js", NULL); jsIncludeFile("hgTracks.js", NULL); #ifdef LOWELAB jsIncludeFile("lowetooltip.js", NULL); #endif///def LOWELAB webIncludeResourceFile("jquery-ui.css"); if (!searching) // NOT doing search { webIncludeResourceFile("jquery.contextmenu.css"); jsIncludeFile("jquery.contextmenu.js", NULL); webIncludeResourceFile("ui.dropdownchecklist.css"); jsIncludeFile("ui.dropdownchecklist.js", NULL); jsIncludeFile("ddcl.js", NULL); } hPrintf("\n"); hPrintf("\n"); cartFlushHubWarnings(); } if (cartVarExists(cart, "chromInfoPage")) { cartRemove(cart, "chromInfoPage"); chromInfoPage(); } else if (differentString(cartUsualString(cart, TRACK_SEARCH,"0"),"0")) { doSearchTracks(groupList); } else if (sameWord(configPageCall, "configure") || sameWord(configPageCall, "configure tracks and display")) { cartRemove(cart, "hgTracksConfigPage"); configPage(); } else if (sameWord(configMultiRegionPageCall, "multi-region")) { cartRemove(cart, "hgTracksConfigMultiRegionPage"); configMultiRegionPage(); } else if (cartVarExists(cart, configHideAll)) { cartRemove(cart, configHideAll); configPageSetTrackVis(tvHide); } else if (cartVarExists(cart, configShowAll)) { cartRemove(cart, configShowAll); configPageSetTrackVis(tvDense); } else if (cartVarExists(cart, configDefaultAll)) { cartRemove(cart, configDefaultAll); configPageSetTrackVis(-1); } else if (cartVarExists(cart, configHideAllGroups)) { cartRemove(cart, configHideAllGroups); struct grp *grp = NULL, *grps = hLoadGrps(database); for (grp = grps; grp != NULL; grp = grp->next) collapseGroup(grp->name, TRUE); configPageSetTrackVis(-2); } else if (cartVarExists(cart, configShowAllGroups)) { cartRemove(cart, configShowAllGroups); struct grp *grp = NULL, *grps = hLoadGrps(database); for (grp = grps; grp != NULL; grp = grp->next) collapseGroup(grp->name, FALSE); configPageSetTrackVis(-2); } else if (cartVarExists(cart, configHideEncodeGroups)) { /* currently not used */ cartRemove(cart, configHideEncodeGroups); struct grp *grp = NULL, *grps = hLoadGrps(database); for (grp = grps; grp != NULL; grp = grp->next) if (startsWith("encode", grp->name)) collapseGroup(grp->name, TRUE); configPageSetTrackVis(-2); } else if (cartVarExists(cart, configShowEncodeGroups)) { /* currently not used */ cartRemove(cart, configShowEncodeGroups); struct grp *grp = NULL, *grps = hLoadGrps(database); for (grp = grps; grp != NULL; grp = grp->next) if (startsWith("encode", grp->name)) collapseGroup(grp->name, FALSE); configPageSetTrackVis(-2); } else { tracksDisplay(); } if (cartVarExists(cart, "hgt.convertChromToVirtChrom")) { cartRemove(cart, "hgt.convertChromToVirtChrom"); return; } jsonObjectAdd(jsonForClient, "measureTiming", newJsonBoolean(measureTiming)); // js code needs to know if a highlightRegion is defined for this db char *highlightDef = cartOptionalString(cart, "highlight"); if (highlightDef && startsWith(database,highlightDef) && highlightDef[strlen(database)] == '.') jsonObjectAdd(jsonForClient, "highlight", newJsonString(highlightDef)); jsonObjectAdd(jsonForClient, "enableHighlightingDialog", newJsonBoolean(cartUsualBoolean(cart, "enableHighlightingDialog", TRUE))); hPrintf("\n"); boolean gotExtTools = extToolsEnabled(); setupHotkeys(gotExtTools); if (gotExtTools) printExtMenuData(); if (measureTiming) measureTime("Time at end of doMiddle, next up cart write"); if (cartOptionalString(cart, "udcTimeout")) { warn("The Genome Browser cart currently includes the \"udcTimeout\" string. " "While this is useful for debugging hubs, it may negatively impact " "performance. To clear this variable, click " "here.",cartSessionId(cart)); } }