8c908f948b09826c6cb4452ee5b282aca41be85e galt Tue Dec 8 21:52:59 2015 -0800 Multi-region (exonMostly). This work allows people to look at virtual chromosomes from a list of regions and then navigate and perform all of the usual functions on it. diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index 35272f5..370e92c 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -1,6094 +1,9766 @@ -/* hgTracks - the original, and still the largest module for the UCSC Human Genome +/* 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 "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" /* 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]; if(chromName == NULL) safef(buf, sizeof(buf), "%s?%s=%s&g=%s", hgTrackUiName(), cartSessionVarName(), cartSessionId(cart), encodedMapName); else safef(buf, sizeof(buf), "%s?%s=%s&c=%s&g=%s", hgTrackUiName(), 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); } } } -void makeChromIdeoImage(struct track **pTrackList, char *psOutput, +boolean makeChromIdeoImage(struct track **pTrackList, char *psOutput, struct tempName *ideoTn) /* Make an ideogram image of the chromsome and our position in it. If the * ideoTn parameter is not NULL, it is filled in if the ideogram is created. */ { struct track *ideoTrack = NULL; MgFont *font = tl.font; char *mapName = "ideoMap"; struct hvGfx *hvg; boolean doIdeo = TRUE; 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); +//warn("makeChromIdeoImage ideoTrack=%lu ideoTrack->track=%s", (unsigned long) ideoTrack, ideoTrack ? ideoTrack->track : ""); // DEBUG REMOVE + /* If no ideogram don't draw. */ if(ideoTrack == NULL) doIdeo = FALSE; else if(trackImgOnly && !ideogramToo) { doIdeo = FALSE; } else { + //warn("makeChromIdeoImage about to remove track from group and tracklist"); // DEBUG REMOVE /* 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; } if(doIdeo) { + //warn("makeChromIdeoImage doIdeo = TRUE"); // DEBUG REMOVE char startBand[16]; char endBand[16]; - char title[32]; + 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 (isEmpty(startBand)) + 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); - ideoTrack->drawItems(ideoTrack, winStart, winEnd, hvg, textWidth+4, gfxBorder, - ideoWidth-textWidth-4, font, ideoTrack->ixColor, ideoTrack->limitedVis); + // 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"); } hPrintf(""); if (doIdeo && !psOutput) { hPrintf(""); hPrintf(""); hPrintf("
", ideoTn->forHtml, ideoWidth, ideoHeight, mapName); hPrintf("
\n"); } else hPrintf("\n"); 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; } 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; -enum trackVisibility savedVis = vis; Color labelColor = (track->labelColor ? track->labelColor : track->ixColor); int fontHeight = mgFontLineHeight(font); int tHeight = trackPlusLabelHeight(track, fontHeight); if (vis == tvHide) return y; /* if a track can do its own left labels, do them after drawItems */ if (track->drawLeftLabels != NULL) return y + tHeight; /* Wiggle tracks depend upon clipping. They are reporting * totalHeight artifically high by 1 so this will leave a * blank area one pixel high below the track. */ if (sameString("wig",track->tdb->type) || sameString("bedGraph",track->tdb->type)) hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight-1); else hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight); minRange = 0.0; safef( o4, sizeof(o4),"%s.min.cutoff", track->track); safef( o5, sizeof(o5),"%s.max.cutoff", track->track); minRangeCutoff = max( atof(cartUsualString(cart,o4,"0.0"))-0.1, track->minRange ); maxRangeCutoff = min( atof(cartUsualString(cart,o5,"1000.0"))+0.1, track->maxRange); if( sameString( track->table, "humMusL" ) || sameString( track->table, "musHumL" ) || sameString( track->table, "mm3Rn2L" ) || sameString( track->table, "hg15Mm3L" ) || sameString( track->table, "mm3Hg15L" ) || sameString( track->table, "regpotent" ) || sameString( track->table, "HMRConservation" ) ) { int binCount = round(1.0/track->scaleRange); minRange = whichSampleBin( minRangeCutoff, track->minRange, track->maxRange, binCount ); maxRange = whichSampleBin( maxRangeCutoff, track->minRange, track->maxRange, binCount ); min0 = whichSampleNum( minRange, track->minRange,track->maxRange, binCount ); max0 = whichSampleNum( maxRange, track->minRange, track->maxRange, binCount ); sprintf( minRangeStr, " " ); sprintf( maxRangeStr, " " ); if( vis == tvFull && track->heightPer >= 74 ) { samplePrintYAxisLabel( hvg, y+5, track, "1.0", min0, max0 ); samplePrintYAxisLabel( hvg, y+5, track, "2.0", min0, max0 ); samplePrintYAxisLabel( hvg, y+5, track, "3.0", min0, max0 ); samplePrintYAxisLabel( hvg, y+5, track, "4.0", min0, max0 ); samplePrintYAxisLabel( hvg, y+5, track, "5.0", min0, max0 ); samplePrintYAxisLabel( hvg, y+5, track, "6.0", min0, max0 ); } } else { sprintf( minRangeStr, "%d", (int)round(minRangeCutoff)); sprintf( maxRangeStr, "%d", (int)round(maxRangeCutoff)); } /* special label handling for wigMaf type tracks -- they display a left label in pack mode. To use the full mode labeling, temporarily set visibility to full. Restore savedVis later */ if (startsWith("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); /* 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; } -/* NOTE: might want to just restore savedVis here for all track types, - but I'm being cautious... */ -if (sameString(track->tdb->type, "wigMaf")) - vis = savedVis; hvGfxUnclip(hvg); return y; } +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 = insideWidth; -int portX = insideX; +int portWidth = fullInsideWidth; +int portX = fullInsideX; // If a portal was established, then set the portal dimensions -int portalStart,chromStart; +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); + +// warn("portX=%d y+1=%d portWidth=%d arrowButtonWidth=%d track=%s", portX, y+1, portWidth, arrowButtonWidth, track->track); // DEBUG REMOVE + 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 +//warn("portX+arrowButtonWidth=%d y+1=%d portWidth-(2*arrowButtonWidth)=%d arrowButtonWidth=%d track=%s", + //portX + arrowButtonWidth, y+1, portWidth - (2 * arrowButtonWidth), arrowButtonWidth, track->track); // DEBUG REMOVE 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) + 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); } } - hvGfxTextCentered(hvg, insideX, y+1, insideWidth, insideHeight, + 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 int doDrawItems(struct track *track, struct hvGfx *hvg, MgFont *font, int y, long *lastTime) /* Draw track items. Return y coord */ { int fontHeight = mgFontLineHeight(font); int pixWidth = tl.picWidth; if (isCenterLabelIncluded(track)) y += fontHeight; if (track->limitedVis == tvPack) { hvGfxSetClip(hvg, gfxBorder+trackTabWidth+1, y, pixWidth-2*gfxBorder-trackTabWidth-1, track->height); } else hvGfxSetClip(hvg, insideX, y, insideWidth, track->height); track->drawItems(track, winStart, winEnd, hvg, insideX, y, insideWidth, font, track->ixColor, track->limitedVis); if (measureTiming && lastTime) { long thisTime = clock1000(); track->drawTime = thisTime - *lastTime; *lastTime = thisTime; } hvGfxUnclip(hvg); y += track->totalHeight(track, track->limitedVis); return y; } static int doMapItems(struct track *track, struct hvGfx *hvg, int fontHeight, int y) /* Draw map boxes around track items */ { char *type = track->tdb->type; int newy; int trackPastTabX = (withLeftLabels ? trackTabWidth : 0); int trackPastTabWidth = tl.picWidth - trackPastTabX; int start = 1; struct slList *item; boolean isWig = (sameString("wig", type) || startsWith("wig ", type) || startsWith("bedGraph", type)); if (isCenterLabelIncluded(track)) y += fontHeight; if (track->mapsSelf) { return y+track->height; } if (track->subType == lfSubSample && track->items == NULL) y += track->lineHeight; /* override doMapItems for hapmapLd track */ /* does not scale with subtracks right now, so this is commented out until it can be fixed if (startsWith("hapmapLd",track->table)) { y += round((double)(scaleForPixels(insideWidth)*insideWidth/2)); return y; } */ for (item = track->items; item != NULL; item = item->next) { int height = track->itemHeight(track, item); /*wiggle tracks don't always increment height (y-value) here*/ if( track->subType == lfSubSample ) { newy = y; if( !start && item->next != NULL ) { newy += sampleUpdateY( track->itemName(track, item), track->itemName(track, item->next), height ); } else if( item->next != NULL || start ) newy += height; start = 0; y = newy; } else { if (track->mapItem == NULL) track->mapItem = genericMapItem; if (!track->mapsSelf) { track->mapItem(track, hvg, item, track->itemName(track, item), track->mapItemName(track, item), track->itemStart(track, item), track->itemEnd(track, item), trackPastTabX, y, trackPastTabWidth,height); } y += height; } } /* Wiggle track's ->height is actually one less than what it returns from * totalHeight()... I think the least disruptive way to account for this * (and not touch Ryan Weber's Sample stuff) is to just correct here if * we see wiggle or bedGraph: */ if (isWig) y++; return y; } static int doOwnLeftLabels(struct track *track, struct hvGfx *hvg, MgFont *font, int y) /* Track draws it own, custom left labels */ { int fontHeight = mgFontLineHeight(font); int tHeight = trackPlusLabelHeight(track, fontHeight); Color labelColor = (track->labelColor ? track->labelColor : track->ixColor); hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight); track->drawLeftLabels(track, winStart, winEnd, hvg, leftLabelX, y, leftLabelWidth, tHeight, isCenterLabelIncluded(track), font, labelColor, track->limitedVis); hvGfxUnclip(hvg); y += tHeight; return y; } // defined below: static int getMaxWindowToDraw(struct trackDb *tdb); int doTrackMap(struct track *track, struct hvGfx *hvg, int y, int fontHeight, int trackPastTabX, int trackPastTabWidth) /* Write out the map for this track. Return the new offset. */ { int mapHeight = 0; switch (track->limitedVis) { case tvPack: case tvSquish: y += trackPlusLabelHeight(track, fontHeight); break; case tvFull: if (!nextItemCompatible(track)) { if (trackIsCompositeWithSubtracks(track)) // TODO: Change when tracks->subtracks { // are always set for composite if (isCenterLabelIncluded(track)) y += fontHeight; struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL;subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { if (subtrack->limitedVis == tvFull) y = doMapItems(subtrack, hvg, fontHeight, y); else { if (isCenterLabelIncluded(subtrack)) y += fontHeight; if (theImgBox && subtrack->limitedVis == tvDense) mapBoxToggleVis(hvg, trackPastTabX, y, trackPastTabWidth, track->lineHeight, subtrack); y += subtrack->totalHeight(subtrack, subtrack->limitedVis); } } } } else y = doMapItems(track, hvg, fontHeight, y); } else y += trackPlusLabelHeight(track, fontHeight); break; case tvDense: if (isCenterLabelIncluded(track)) y += fontHeight; if (tdbIsComposite(track->tdb)) mapHeight = track->height; else mapHeight = track->lineHeight; int maxWinToDraw = getMaxWindowToDraw(track->tdb); if (maxWinToDraw <= 1 || (winEnd - winStart) <= maxWinToDraw) mapBoxToggleVis(hvg, trackPastTabX, y, trackPastTabWidth, mapHeight, track); y += mapHeight; break; case tvHide: default: break; /* Do nothing; */ } return y; } -int computeScaleBar(int numBases, char scaleText[], int scaleTextSize) +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"; -int scaleBases = 0; +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 makeRulerZoomBoxes(struct hvGfx *hvg, struct cart *cart, int winStart,int winEnd, - int insideWidth,int seqBaseCount,int rulerClickY, - int rulerClickHeight) -/* Make hit boxes that will zoom program around ruler. */ +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 boxes = 30; int winWidth = winEnd - winStart; int newWinWidth = winWidth; -int i, ws, we = 0; -int mid, ns, ne; -double wScale = (double)winWidth/boxes; char message[32]; char *zoomType = cartCgiUsualString(cart, RULER_BASE_ZOOM_VAR, ZOOM_3X); safef(message, sizeof(message), "%s zoom", zoomType); if (sameString(zoomType, ZOOM_1PT5X)) newWinWidth = winWidth/1.5; else if (sameString(zoomType, ZOOM_3X)) newWinWidth = winWidth/3; else if (sameString(zoomType, ZOOM_10X)) newWinWidth = winWidth/10; else if (sameString(zoomType, ZOOM_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; -for (i=1; i<=boxes; ++i) - { - ws = we; - we = round(wScale*i); - mid = (ws + we)/2 + winStart; - ns = mid-newWinWidth/2; - ne = ns + newWinWidth; - if (ns < 0) - { - ns = 0; - ne -= ns; +return newWinWidth; } - if (ne > seqBaseCount) + +static void drawScaleBar( + struct hvGfx *hvg, + MgFont *font, + int fontHeight, + int yAfterRuler, + int y, + int scaleBarTotalHeight + ) +/* draws the scale bar */ { - ns -= (ne - seqBaseCount); - ne = seqBaseCount; - } +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)); } -return newWinWidth; } -static int doDrawRuler(struct hvGfx *hvg,int *newWinWidth,int *rulerClickHeight, +static int doDrawRuler(struct hvGfx *hvg, int *rulerClickHeight, int rulerHeight, int yAfterRuler, int yAfterBases, MgFont *font, - int fontHeight,boolean rulerCds) + int fontHeight, boolean rulerCds, int scaleBarTotalHeight, struct window *window) /* draws the ruler. */ { -int scaleBarPad = 2; -int scaleBarHeight = fontHeight; -int scaleBarTotalHeight = fontHeight + 2 * scaleBarPad; int titleHeight = fontHeight; int baseHeight = fontHeight; //int yAfterBases = yAfterRuler; int showPosHeight = fontHeight; int codonHeight = fontHeight; struct dnaSeq *seq = NULL; int rulerClickY = 0; *rulerClickHeight = rulerHeight; int y = rulerClickY; hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1); int relNumOff = winStart; if (baseTitle) { - hvGfxTextCentered(hvg, insideX, y, insideWidth, titleHeight,MG_BLACK, font, baseTitle); + 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, winEnd-winStart); + 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); - hvGfxTextCentered(hvg, insideX, y, insideWidth, showPosHeight,MG_BLACK, font, txt); - *rulerClickHeight += showPosHeight; 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) { - char scaleText[32]; - int numBases = winEnd-winStart; - int scaleBases = computeScaleBar(numBases, scaleText, sizeof(scaleText)); - int scalePixels = (int)((double)insideWidth*scaleBases/numBases); - int scaleBarX = insideX + (int)(((double)insideWidth-scalePixels)/2); - int scaleBarEndX = scaleBarX + scalePixels; - int scaleBarY = y + 0.5 * scaleBarTotalHeight; - *rulerClickHeight += scaleBarTotalHeight; - hvGfxTextRight(hvg, insideX, y + scaleBarPad, - (scaleBarX-2)-insideX, scaleBarHeight, MG_BLACK, font, scaleText); - hvGfxLine(hvg, scaleBarX, scaleBarY, scaleBarEndX, scaleBarY, MG_BLACK); - hvGfxLine(hvg, scaleBarX, y+scaleBarPad, scaleBarX, - y+scaleBarTotalHeight-scaleBarPad, MG_BLACK); - hvGfxLine(hvg, scaleBarEndX, y+scaleBarPad, scaleBarEndX, - y+scaleBarTotalHeight-scaleBarPad, MG_BLACK); - if(cartUsualBoolean(cart, BASE_SHOWASM_SCALEBAR, TRUE)) + if (window == windows) // first window { - int fHeight = vgGetFontPixelHeight(hvg->vg, font); - hvGfxText(hvg, scaleBarEndX + 10, - y + (scaleBarTotalHeight - fHeight)/2 + ((font == mgSmallFont()) ? 1 : 0), - MG_BLACK, font, trackHubSkipHubName(database)); + 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) +if (baseShowRuler && (insideWidth >=36)) { hvGfxDrawRulerBumpText(hvg, insideX, y, rulerHeight, insideWidth, MG_BLACK, font, relNumOff, winBaseCount, 0, 1); } -*newWinWidth = makeRulerZoomBoxes(hvg, cart,winStart,winEnd,insideWidth,seqBaseCount, - rulerClickY,*rulerClickHeight); if (zoomedToBaseLevel || rulerCds) { Color baseColor = MG_BLACK; int start, end, chromSize; struct dnaSeq *extraSeq; /* extraSeq has extra leading & trailing bases * for translation in to amino acids */ boolean complementRulerBases = cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE); // gray bases if not matching the direction of display if (complementRulerBases != revCmplDisp) baseColor = MG_GRAY; /* get sequence, with leading & trailing 3 bases * used for amino acid translation */ start = max(winStart - 3, 0); chromSize = hChromSize(database, chromName); end = min(winEnd + 3, chromSize); extraSeq = hDnaFromSeq(database, chromName, start, end, dnaUpper); if (start != winStart - 3 || end != winEnd + 3) { /* at chromosome boundaries, pad with N's to assure * leading & trailing 3 bases */ char header[4] = "NNN", trailer[4] = "NNN"; int size = winEnd - winStart + 6; char *padded = (char *)needMem(size+1); header[max(3 - winStart, 0)] = 0; trailer[max(winEnd - chromSize + 3, 0)] = 0; safef(padded, size+1, "%s%s%s", header, extraSeq->dna, trailer); extraSeq = newDnaSeq(padded, strlen(padded), extraSeq->name); } /* for drawing bases, must clip off leading and trailing 3 bases */ seq = cloneDnaSeq(extraSeq); seq = newDnaSeq(seq->dna+3, seq->size-6, seq->name); if (zoomedToBaseLevel) drawBases(hvg, insideX, y+rulerHeight, insideWidth, baseHeight, baseColor, font, complementRulerBases, seq); /* set up clickable area to toggle ruler visibility */ { char newRulerVis[100]; safef(newRulerVis, 100, "%s=%s", RULER_TRACK_NAME, rulerMode == tvFull ? rulerMenu[tvDense] : rulerMenu[tvFull]); + // 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); } } -void highlightRegion(struct cart *cart, struct hvGfx *hvg, int insideX, int imagePixelHeight, - int winStart, int winEnd) +struct highlightVar +// store highlight information +{ +char *db; +char *chrom; +long chromStart; +long chromEnd; +char *hexColor; +}; + +struct highlightVar *parseHighlightInfo() +// Parse highlight info from cart var +// db.chrom:start-end#hexColor +{ +struct highlightVar *h = NULL; +char *highlightDef = cartOptionalString(cart, "highlight"); +//warn("parseHighlightInfo highlight=%s", highlightDef); // DEBUG REMOVE +if(highlightDef) + { + AllocVar(h); + h->db = cloneNextWordByDelimiter(&highlightDef,'.'); + h->chrom = cloneNextWordByDelimiter(&highlightDef,':'); + // long to handle virt chrom coordinates + h->chromStart = atol(cloneNextWordByDelimiter(&highlightDef,'-')); + h->chromEnd = atol(cloneNextWordByDelimiter(&highlightDef,'#')); + h->chromStart--; // Not zero based + if (highlightDef && *highlightDef != '\0') + h->hexColor = cloneString(highlightDef); + //verbose(1, "DEBUG GALT highlightRegion() db=%s chrom=%s chromStart=%ld chromEnd=%ld virtChromName=%s winStart=%ld winEnd=%ld\n", + //h->db, h->chrom, h->chromStart, h->chromEnd, virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE + } +return h; +} + +void highlightRegion(struct cart *cart, struct hvGfx *hvg, int imagePixelHeight) // Highlights a region 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 { -char *highlightDef = cartOptionalString(cart, "highlight"); -if(highlightDef && theImgBox == NULL) // Only highlight region when imgBox is not used. - { - // expect db.chrom:start-end#hexColor - char *db = cloneNextWordByDelimiter(&highlightDef,'.'); - char *chrom = cloneNextWordByDelimiter(&highlightDef,':'); - int chromStart = atoi(cloneNextWordByDelimiter(&highlightDef,'-')); - int chromEnd = atoi(cloneNextWordByDelimiter(&highlightDef,'#')); - if ((db != NULL && sameString(db, database )) - && (chrom != NULL && sameString(chrom,chromName)) - && (chromEnd != 0) - && (chromStart <= winEnd && chromEnd >= winStart)) - { - chromStart--; // Not zero based - chromStart = max(chromStart, winStart); - chromEnd = min(chromEnd, winEnd); - double pixelsPerBase = scaleForPixels(insideWidth + 1); - int startPixels = pixelsPerBase * (chromStart - winStart); // floor +struct highlightVar *h = parseHighlightInfo(); + +if(h && theImgBox == NULL) // Only highlight region when imgBox is not used. (pdf and show-image) + { + 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)(chromEnd - chromStart) + 0.5; // round up + 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 (highlightDef != NULL && *highlightDef != '\0') + if (h->hexColor) { - long rgb = strtol(highlightDef,NULL,16); // Big and little Endians + long rgb = strtol(h->hexColor,NULL,16); // Big and little Endians hexColor = MAKECOLOR_32( ((rgb>>16)&0xff), ((rgb>>8)&0xff), (rgb&0xff) ); } - hvGfxBox(hvg, insideX + startPixels, 0, width, imagePixelHeight,hexColor); + 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"); //} -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; - -static struct image *theOneImg = NULL; // No need to be global, only the map needs to be global -static struct image *theSideImg = NULL; // Because dragScroll drags off end of image, - // the side label gets seen. Therefore we need 2 images!! -static struct imgSlice *curSlice = NULL; // No need to be global, only the map needs to be global +//--- GALT -// Set up imgBox dimensions -int sliceWidth[stMaxSliceTypes]; // Just being explicit -int sliceOffsetX[stMaxSliceTypes]; -int sliceHeight = 0; -int sliceOffsetY = 0; -char *rulerTtl = NULL; -if (theImgBox) -// theImgBox is a global for now to avoid huge rewrite of hgTracks. It is started -// prior to this in doTrackForm() - { - 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 image size - if (imgBoxPortalDimensions(theImgBox,&winStart,&winEnd,&(tl.picWidth),NULL,NULL,NULL,NULL,NULL)) +char *rgbColorToString(struct rgbColor color) +/* make rgbColor into printable string */ { - pixWidth = tl.picWidth; - winBaseCount = winEnd - winStart; - insideWidth = tl.picWidth-gfxBorder-insideX; +char buf[256]; +safef(buf, sizeof buf, "rgbColor r:%d g:%d b:%d", color.r, color.g, color.b); +return cloneString(buf); } - memset((char *)sliceWidth, 0,sizeof(sliceWidth)); - memset((char *)sliceOffsetX,0,sizeof(sliceOffsetX)); - if (withLeftLabels) + +char *trackDumpString(struct track *track) +/* Write out info on track to string */ { - 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 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); } -struct flatTracks *flatTracks = NULL; -struct flatTracks *flatTrack = NULL; -if (rulerMode != tvFull) +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. */ { - rulerCds = FALSE; +if (sameString(virtRegion1->chrom, virtRegion2->chrom) && virtRegion1->end <= virtRegion2->start) + return TRUE; +return FALSE; } -/* Figure out height of each visible track. */ -pixHeight = gfxBorder; -if (rulerMode != tvHide) +/* --- 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. */ +{ +//warn("disguisePosition position="+position); // DEBUG REMOVE +/* 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 +//warn("start="+start+" end="+end); // DEBUG REMOVE +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) + { + //warn("w->chromName=%s w->winStart=%d w->winEnd=%d",w->chromName, w->winStart, w->winEnd); // DEBUG REMOVE + 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. */ +{ +//warn("undisguisePosition position="+position); // DEBUG REMOVE +/* 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; +//warn("start="+start+" end="+end); // DEBUG REMOVE +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; + //warn("cs="+cs+" ce="+ce); // DEBUG REMOVE + 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); +//warn("undisguisePosition newPos="+newPos); // DEBUG REMOVE +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.*/ { - if (!baseShowRuler && !baseTitle && !baseShowPos && !baseShowAsm && !baseShowScaleBar && !zoomedToBaseLevel && !rulerCds) +char buf[256]; +char *chromName = windows->chromName; +int start = windows->winStart; +struct window *w = windows, *last = NULL; +while(w->next) // find last window { - warn("Can't turn everything off in base position track. Turning ruler back on"); - baseShowRuler = TRUE; - cartSetBoolean(cart, BASE_SHOWRULER, TRUE); + 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); } - if (baseTitle) - basePositionHeight += titleHeight; +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. - if (baseShowPos||baseShowAsm) - basePositionHeight += showPosHeight; + * NOTE this assumes that the regions are in order, but tolerates hiccups in order. - if (baseShowScaleBar) - basePositionHeight += scaleBarTotalHeight; + * DONE make it handle multiple chromsomes + * + * TODO what about just modifying the original list directly? - if (!baseShowRuler) + * 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. + * */ { - basePositionHeight -= rulerHeight; - rulerHeight = 0; +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); } - - if (zoomedToBaseLevel) - basePositionHeight += baseHeight; - - yAfterRuler += basePositionHeight; - yAfterBases = yAfterRuler; - pixHeight += basePositionHeight; - if (rulerCds) + v->chrom = virtRegion->chrom; // TODO is cloning the string needed? + leftWindowPadding = windowPadding; + rightWindowPadding = windowPadding; + if (lastVirtRegion && regionsAreInOrder(lastVirtRegion, virtRegion)) { - yAfterRuler += rulerTranslationHeight; - pixHeight += rulerTranslationHeight; + int distToPrevRegion = virtRegion->start - lastVirtRegion->end; + if (distToPrevRegion < (2*windowPadding)) + { + leftWindowPadding = distToPrevRegion/2; } } - -boolean safeHeight = TRUE; -/* firefox on Linux worked almost up to 34,000 at the default 620 width */ -#define maxSafeHeight 32000 -/* Hash tracks/subtracks, limit visibility and calculate total image height: */ -for (track = trackList; track != NULL; track = track->next) + if (virtRegion->next && regionsAreInOrder(virtRegion, virtRegion->next)) { - if (tdbIsCompositeChild(track->tdb)) // When single track is requested via AJAX, - limitedVisFromComposite(track); // it could be a subtrack - else - limitVisibility(track); + 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; + } +warn("After padding, %d regions", regionCount); +slReverse(&newList); +virtRegionList = newList; // update new list -- TODO should the old one be freed? if so, the chrom name should use cloneString +} + - if (!safeHeight) +void makeVirtChrom() +/* build virtual chrom array from virt region list */ { - track->limitedVis = tvHide; - track->limitedVisSet = TRUE; - continue; +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); + //warn("virtChrom[%d] .virtPos=%ld", i, virtChrom[i].virtPos); // DEBUG REMOVE + } +virtSeqBaseCount = totalBases; } - if (tdbIsComposite(track->tdb)) +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*/ { - struct track *subtrack; - for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) +//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) { - if (!isSubtrackVisible(subtrack)) - continue; + while(1) + { + long c = (a + b) / 2; - // subtrack vis can be explicit or inherited from composite/view. - // Then it could be limited because of pixel height - limitedVisFromComposite(subtrack); + //warn("target=%ld a=%ld b=%ld c=%ld virtRegionCount=%d", target, a, b, c, virtRegionCount); // DEBUG REMOVE - if (subtrack->limitedVis != tvHide) + if (a > b) { - subtrack->hasUi = track->hasUi; - flatTracksAdd(&flatTracks,subtrack,cart); + 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 (track->limitedVis != tvHide) - flatTracksAdd(&flatTracks,track,cart); + else if (target < virtChrom[c].virtPos) + { + b = c - 1; } -flatTracksSort(&flatTracks); // Now we should have a perfectly good flat track list! -struct track *prevTrack = NULL; -for (flatTrack = flatTracks,prevTrack=NULL; flatTrack != NULL; flatTrack = flatTrack->next) + else { - track = flatTrack->track; - assert(track->limitedVis != tvHide); - int totalHeight = pixHeight+trackPlusLabelHeight(track,fontHeight); - if (maxSafeHeight < totalHeight) + // 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 */ { - char numBuf[SMALLBUF]; - sprintLongWithCommas(numBuf, maxSafeHeight); +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); + +//warn("after binary searches, virtIndexStart=%ld virtOffsetStart=%d , virtIndexEnd=%ld virtOffsetEnd=%d", virtIndexStart, virtOffsetStart, virtIndexEnd, virtOffsetEnd); // DEBUG REMOVE + +// 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) +// warn("remapHighlightPos h-> chrom=%s chromStart=%ld chromEnd=%ld", h->chrom, h->chromStart, h->chromEnd); // DEBUG REMOVE + +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) + { + //warn("w->chromName=%s w->winStart=%d w->winEnd=%d",w->chromName, w->winStart, w->winEnd); // DEBUG REMOVE + 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; + + //warn("window x = %d width = %d x + width=%d basesInWindow=%d", + //window->insideX, window->insideWidth, window->insideX + window->insideWidth, basesInWindow); // DEBUG REMOVE + + if (pixelsInWindow < 1) // remove windows less than one pixel from the list + { + //if (windowsTooSmall < 5) + // warn("window x = %d width = %d x + width=%d", window->insideX, window->insideWidth, window->insideX + window->insideWidth); + *pWindows = window->next; + --winCount; + ++windowsTooSmall; + } + else + { + pWindows = &window->next; + } + } +//if (windowsTooSmall > 0) +// warn("Summary: %d windowsTooSmall: (pixelsInWindow < 1)", windowsTooSmall); + +//warn("winCount=%d slCount(windows)=%d #LessThan1Pixel=%d fullInsideWidth=%d virtWinBaseCount=%ld basesUsed=%ld", + //winCount, slCount(windows), windowsTooSmall, fullInsideWidth, virtWinBaseCount, basesUsed); + +// DEBUG REMOVE: +// nextExonArrows are working now +//if (winCount >= 2) +// withNextExonArrows = FALSE; /* Display next exon navigation buttons near center labels? */ +} + +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; + //warn("findNearest: nearestRegion=%d nearestDistance=%d p->virtStart=%ld p->virtEnd=%ld ", + //nearestRegion, nearestDistance, p->virtStart, p->virtEnd); // DEBUG REMOVE + 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; +} + + +// ---------------------------------- + + +static void checkMaxWindowToDraw(struct track *tg); // forward declaration // DEBUG REMOVE? + + +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 +//warn("database=%s, haplotypeId=%s, haploSize=%d", database, haplotypeId, haploSize); // DEBUG REMOVE + +// 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)); + +// DEBUG REMOVE testing +//warn("initSingleAltHaplotype: offset=%ld", offset); +//long testOffset = 0; +//for (v=virtRegionList; v; v=v->next) +// { +// testOffset += (v->end - v->start); +// warn("%s:%d-%d cumuulative offset %ld", v->chrom, v->start, v->end, testOffset); +// } + +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) +/* pad all of the exons */ +{ +int i; +for(i=0; i < gene->exonCount; ++i) + { + // padding + gene->exonStarts[i] -= emPadding; + if (gene->exonStarts[i] < 0) + gene->exonStarts[i] = 0; + gene->exonEnds[i] += emPadding; + 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]; +// 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); + //warn("query = [%s]", query); // DEBUG REMOVE + 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 +if (sameString(emGeneTable,"refGene")) + sqlSafefAppend(query, sizeof(query), " order by chrom, txStart"); +//warn("query = [%s]", query); // DEBUG REMOVE +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); + //warn("GALT loaded gene %s\n", gene->name); // DEBUG REMOVE + if (geneMostly) + convertGenePredGeneToExon(gene); + if (!sameString(lastChromSizeChrom, gene->chrom)) + { + chromSize = hChromSize(database, gene->chrom); + safecpy(lastChromSizeChrom, sizeof lastChromSizeChrom, gene->chrom); + } + if (emPadding > 0) + padExons(gene, chromSize); // handle padding + } + else + { + isEOF = TRUE; + } + } + if (gene && !showNoncoding && (gene->cdsStart == gene->cdsEnd)) + { + //warn("GALT skip non-coding gene %s cdsStart==cdsEnd", gene->name); // DEBUG REMOVE + genePredFree(&gene); + } + if (gene && knownCanonical && !hashLookup(kcHash, gene->name)) + { + //warn("GALT skip not in knownCanonical hash gene %s", gene->name); // DEBUG REMOVE + genePredFree(&gene); + } + if (gene && knownToTag && !hashLookup(ktHash, gene->name)) + { + //warn("GALT skip not in knownToTag Basic hash gene %s", gene->name); // DEBUG REMOVE + 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); + //warn("GALT check chrom gene->chrom=%s, lastChrom=%s, chrom=%s", gene->chrom, lastChrom, chrom); // DEBUG REMOVE + if (sameString(gene->chrom, chrom)) + { + //warn("GALT best=%d gene %s exonStart[0]=%d", best, gene->name, gene->exonStarts[0]); // DEBUG REMOVE + if (gene->exonStarts[0] < best) + transferIt = TRUE; + } + } + if (transferIt) + { + //warn("GALT transferred gene %s to kcelist", gene->name); // DEBUG REMOVE + // 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 + } + //warn("GALT readIt=%d transferIt=%d", readIt, transferIt); // DEBUG REMOVE + 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]; + + //warn("GALT GOT DATA chrom=%s start=%d end=%d lastChrom=%s lastStart=%d lastEnd=%d", + //chrom, start, end, lastChrom, lastStart, lastEnd); // DEBUG REMOVE + + if (sameString(chrom, lastChrom)) + { + if (start <= lastEnd) + { + //warn("GALT overlap extend start=%d <= lastEnd=%d, end=%d", start, lastEnd, end); // DEBUG REMOVE + // overlap detected in knownCanonical + // extend current region + if (end > lastEnd) + { + lastEnd = end; + } + } + else + { + printIt = TRUE; + } + } + else + { + printIt = TRUE; + } + } + else + { + printIt = TRUE; + isEOF = TRUE; + } + + //warn("GALT printIt=%d", printIt); // DEBUG REMOVE + + if (printIt) + { + if (firstTime) + { + firstTime = FALSE; + } + else + { + //warn("GALT adding region %s:%d-%d", lastChrom, lastStart, lastEnd); // DEBUG REMOVE + 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; + } + + //warn("used up %s exon# %d", bestKce->gene->name, bestKce->exonNumber); // DEBUG REMOVE + // TODO update or remove current thing from kceList + ++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; +warn("testRegionList() started DEBUG."); // DEBUG REMOVE +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; + } +warn("testRegionList() completed OK."); +} + + +// 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 avgWidth = fullInsideWidth/winCount ; // can shrink down to 1 ok! // DEBUG REMOVE +//int x = fullInsideX; +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]); + //warn("chrom=%s size=%d", row[0], chromSize); + AllocVar(v); + v->chrom = cloneString(row[0]); + v->start = 1 - 1; + v->end = chromSize; + slAddHead(&virtRegionList, v); + ++winCount; + //if (winCount >= 2) // DEBUG HACK REMOVE + //break; + } +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 +//warn("initVirtRegionsFromBedUrl got here multiRegionsBedUrl=%s", multiRegionsBedUrl); // DEBUG REMOVE +// 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); +//warn("unix time = %ld on BED file %s", (long)*bedDateTime, multiRegionsBedUrl); // DEBUG REMOVE +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 "); + //warn("got #database setting: %s, current database %s", dbFromBed, database); // DEBUG REMOVE + 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 ")); + //warn("got #shortDesc setting: %s", virtModeShortDescr); // DEBUG REMOVE + } + if (startsWith("#padding ",line)) + { + bedPadding = sqlSigned(line+strlen("#padding ")); + warn("got #padding setting: %d", bedPadding); // DEBUG REMOVE + } + 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; + //warn("got expectedFieldCount=%d", expectedFieldCount); // DEBUG REMOVE + } + else + { + //warn("got numFields=%d, expectedFieldCount=%d", numFields, expectedFieldCount); // DEBUG REMOVE + 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); + } + + //warn("got about to call load and validate bed"); // DEBUG REMOVE + + 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. + slAddHead(&bedList, bed); + + //warn("got after load and validate bed"); // DEBUG REMOVE + + struct virtRegion *v; + if (numFields < 12) + { + AllocVar(v); + v->chrom = cloneString(bed->chrom); + v->start = bed->chromStart; + v->end = bed->chromEnd; + slAddHead(&virtRegionList, v); + //warn("got BED region %s %d %d", v->chrom, v->start, v->end); // DEBUG REMOVE + } + else + { + int e; + //warn("got BED12 blockCount=%d bed->chrom=%s", bed->blockCount, bed->chrom); // DEBUG REMOVE + for (e = 0; e < bed->blockCount; ++e) + { + //warn("got BED12 exon blockSizes[e]=%d chromStarts[e]=%d", bed->blockSizes[e], bed->chromStarts[e]); // DEBUG REMOVE + AllocVar(v); + v->chrom = cloneString(bed->chrom); + v->start = bed->chromStart + bed->chromStarts[e]; + v->end = v->start + bed->blockSizes[e]; + //warn("got BED12 exon region %s %d %d", v->chrom, v->start, v->end); // DEBUG REMOVE + slAddHead(&virtRegionList, v); + } + } + + } +lineFileClose(&lf); +bedFreeList(&bedList); +slReverse(&virtRegionList); +if (bedPadding > 0) + padVirtRegions(bedPadding); +return TRUE; +} + +// TODO OBSOLETED by lastDbPosCart +boolean restoreCartSetting(char *cartSetting) +/* Restore cart setting from var=val setting. */ +{ +if (!cartSetting) + return FALSE; +char *eq = strchr(cartSetting,'='); +if (!eq) // nothing to do + return FALSE; +*eq = 0; +char *cartVar = cartSetting; +char *cartVal = eq+1; +if (sameString(cartVal, "(null)")) + cartRemove(cart, cartVar); +else + cartSetString(cart, cartVar, cartVal); +*eq = '='; +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 */ +{ +//warn("initRegionList got here 0"); + +// TODO GALT +// if we are going to support windows from any org and db, +// then we are going to have to loosen up assumptions +// about trackList consistencey across windows. +// But it seems like much of the rest of the code would work. +// Not sure about speed either -- but might be ok. +// +// update, well by 2015-04-28 it seems like we are not going to support windows from other assemblies +// due to difficulties with tracklist. + + +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")) + { + //warn("demo type single original window"); // DEBUG REMOVE + // 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")) + { + + //warn("emGeneTable %s", emGeneTable); // DEBUG REMOVE + // 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); + //warn("slCount(virtRegionList)=%d", slCount(virtRegionList)); // DEBUG REMOVE + 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 + { + //warn("demo KnownCanonical gene regions genome-wide."); // DEBUG REMOVE + initVirtRegionsFromKnownCanonicalGenes("knownCanonical"); + virtModeShortDescr = "genes"; + } +else if (sameString(virtModeType, "customUrl")) + { + //warn("custom regions from BED URL."); // DEBUG REMOVE + 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")) + { + //warn("Single Transcript Id"); // DEBUG REMOVE + 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")) + { + //warn("Single Haplotype Id"); // DEBUG REMOVE + singleAltHaploId = cartUsualString(cart, "singleAltHaploId", singleAltHaploId); // currently 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")) + { + //warn("show all regular chromosomes (not alts)\n" + //"Warning must turn off all tracks except big*"); // DEBUG REMOVE + initAllChroms(); + } +else if (sameString(virtModeType, "demo1")) + { + //warn("demo two windows on two chroms (default posn chr21, and same loc on chr22"); // DEBUG REMOVE + + // NOTE we are losing the ability to specify org and db + + //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 + //window2->organism = "Mouse"; + //window2->database = "mm10"; + //window2->database = "hg38"; + v2->chrom = "chr22"; + v2->start = 33031597 - 1; + v2->end = 33041570; + slAddHead(&virtRegionList, v2); + + slReverse(&virtRegionList); + } +else if (sameString(virtModeType, "demo2")) + { + //warn("demo multiple (70) windows on one chrom chr21 def posn, window size and step exon-like"); // DEBUG REMOVE + initExonStep(); + } +else if (sameString(virtModeType, "demo4")) + { + //warn("demo multiple (20) windows showing exons from TITIN gene uc031rqd.1."); // DEBUG REMOVE + initVirtRegionsFromEmGeneTable("uc031rqd.1"); // TITIN // "uc002ypa.3"); // SOD1 + } +else if (sameString(virtModeType, "demo5")) + { + //warn("demo alt locus on hg38\n" + //"Shows alt chrom surrounded by preceding and following regions of same size from reference genome "); // DEBUG REMOVE + initWindowsAltLoci(); + } +else if (sameString(virtModeType, "demo6")) + { + //warn("demo SOD1\n" + //"Shows zoomed in exon-exon junction from SOD1 gene, between exon1 and exon2."); // DEBUG REMOVE + + initVirtRegionsFromSOD1Hardwired(); + + } +else + { + //warn("unrecognized virtModeType = %s", virtModeType); + return FALSE; // return to default mode + } + +virtModeExtraState = dyStringCannibalize(&dy); + +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; +//warn("track %s has type %s", track->track, typeLine); +if (typeLine == NULL) + return FALSE; +wordCount = chopLine(cloneString(typeLine), words); +if (wordCount <= 0) + return FALSE; +type = words[0]; +//warn("track %s has type word[0] = %s, canPack=%d", track->track, type, track->canPack); +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 + ) + { + //warn("track %s is BEDLIKE", track->track); + return TRUE; + } + +//warn("track %s is NOT BEDLIKE", track->track); +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; +//warn("track %s has type %s", track->track, typeLine); +if (typeLine == NULL) + return FALSE; +wordCount = chopLine(cloneString(typeLine), words); +if (wordCount <= 0) + return FALSE; +type = words[0]; +//warn("track %s has type word[0] = %s", track->track, type); +if (sameWord(type, "expRatio")) + { + //warn("track %s is like expRatio, needs one row per item", track->track); + return TRUE; + } + +//warn("track %s is NOT like expRatio, which needs only one row per item", track->track); +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); + + //warn("track %s height=%d isCenterLabelIncluded(winTrack)=%d\n", winTrack->track, trackHeight, isCenterLabelIncluded(winTrack)); // DEBUG REMOVE + //fflush(stdout); // DEBUG REMOVE + + if (trackHeight > maxHeight) + maxHeight = trackHeight; + } +setGlobalsFromWindow(windows); // first window + +flatTrack->maxHeight = maxHeight; + +//warn("track %s maxHeight=%d\n", track->track, maxHeight); // DEBUG REMOVE +//fflush(stdout); // DEBUG REMOVE +} + +void makeActiveImage(struct track *trackList, char *psOutput) +/* Make image and image map. */ +{ + +boolean galtDebug = FALSE; + +if (galtDebug) +warn("makeActiveImage begins"); + +struct window *window = NULL; + +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; + +//warn("theImgBox=%lu", (unsigned long)theImgBox); // DEBUG REMOVE + +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. + +if (galtDebug) +warn("organism=%s database=%s", organism, database); // DEBUG REMOVE + +// 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. +if (galtDebug) +warn("set heights and visibilities"); // DEBUG REMOVE +for(window=windows;window;window=window->next) + { + //warn("**** window=%lu insideX=%d insideWidth=%d", (unsigned long) window, window->insideX, window->insideWidth); // DEBUG REMOVE + 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 + { + //warn("DEBUG REMOVE regular track %s", track->track); // DEBUG REMOVE + 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; + } + } + } + + //warn("heights and vis: track %s vis=%d limitedVis==%d limitedVisSet=%d", track->track, track->visibility, track->limitedVis, track->limitedVisSet); // DEBUG REMOVE + } + } +trackList = windows->trackList; +setGlobalsFromWindow(windows); // first window + +// Construct flatTracks +if (galtDebug) +warn("Construct flatTracks"); +for (track = trackList; track != NULL; track = track->next) + { + //warn("construct flat track %s vis=%d limitedVis==%d", track->track, track->visibility, track->limitedVis); // DEBUG REMOVE + if (tdbIsComposite(track->tdb)) + { + struct track *subtrack; + for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) + { + if (!isSubtrackVisible(subtrack)) + continue; + + //warn("about to call isLimitedVisHiddenForAllWindows(%s) on subtrack", subtrack->track); + if (!isLimitedVisHiddenForAllWindows(subtrack)) + { + //warn("adding flatTrack subtrack (%s)", subtrack->track); // DEBUG REMOVE + flatTracksAdd(&flatTracks,subtrack,cart); + } + } + } + else + { + //warn("about to call isLimitedVisHiddenForAllWindows(%s) on track", track->track);// DEBUG REMOVE + if (!isLimitedVisHiddenForAllWindows(track)) + { + //warn("adding flatTrack track (%s)", track->track); // DEBUG REMOVE + flatTracksAdd(&flatTracks,track,cart); + } + } + } +//warn("slCount(flatTracks)=%d", slCount(flatTracks)); // DEBUG REMOVE +flatTracksSort(&flatTracks); // Now we should have a perfectly good flat track list! + + +// for each track, figure out maximum height needed from all windows +if (galtDebug) +warn("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) + { + //warn("flat track %s limitedVis==tvHide. why?", track->track); // DEBUG REMOVE + continue; + } + + setFlatTrackMaxHeight(flatTrack, fontHeight); + + } + +//warn("got done with flatTrack->maxHeight"); +//fflush(stdout); // DEBUG REMOVE + + +// fill out track->prevTrack, and check for maxSafeHeight +if (galtDebug) +warn("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); + //warn("track %s flat maxHeight=%d", track->track, flatTrack->maxHeight); // DEBUG REMOVE + 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 (track->limitedVis != tvHide) + } + if (!isLimitedVisHiddenForAllWindows(track)) { - track->prevTrack = prevTrack; // Important for keeping track of conditional center labels! - pixHeight += trackPlusLabelHeight(track, fontHeight); + //warn("setting winTracks ->prevTrack to %lu for all windows", (unsigned long) prevTrack); // DEBUG REMOVE + 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; } } +if (galtDebug) +warn("About to allocate hvg png pixWidth=%d, pixHeight=%d", pixWidth, pixHeight); +fflush(stdout); // DEBUG REMOVE + 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; // 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. - highlightRegion(cart, hvg, insideX, imagePixelHeight, winStart, winEnd); +if (theImgBox == NULL) // imageV2 highlighting is done by javascript. This does pdf and view-image highlight + highlightRegion(cart, hvg, imagePixelHeight); +for (window=windows; window; window=window->next) + { // TODO GALT do I need to set globals here? + //setGlobalsFromWindow(window); // TEMP HACK DOES THIS HELP? /* Find colors to draw in. */ -findTrackColors(hvg, trackList); + findTrackColors(hvg, window->trackList); + } +//setGlobalsFromWindow(windows); // first window // TEMP HACK DOES THIS HELP? // REMOVE? + // 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 (track->limitedVis != tvHide) + if (!isLimitedVisHiddenForAllWindows(track)) + { + //warn("hvGfxFindRgb !isLimitedVisHiddenForAllWindows(%s)", track->track); // DEBUG REMOVE + struct track *winTrack; + for (winTrack=track; winTrack; winTrack=winTrack->nextWindow) + //for (window=windows, winTrack=track; window; window=window->next, winTrack=winTrack->nextWindow) // TEMP HACK DOES THIS HELP? + { // TODO GALT do I need to set globals here? + //warn("hvGfxFindRgb (%s) winTrack labelColor=%d ixColor=%d color=%s", track->track, winTrack->labelColor, winTrack->ixColor, rgbColorToString(winTrack->color)); // DEBUG REMOVE + if (winTrack->labelColor == winTrack->ixColor && winTrack->ixColor == 0) { - if (track->labelColor == track->ixColor && track->ixColor == 0) - track->ixColor = hvGfxFindRgb(hvg, &track->color); + //warn("hvGfxFindRgb got here window : %s %s %s:%d-%d offset %d width %d", // DEBUG REMOVE + //window->organism, window->database, window->chromName, window->winStart+1, window->winEnd, window->insideX, window->insideWidth); + //setGlobalsFromWindow(window); // TEMP HACK DOES THIS HELP? + + winTrack->ixColor = hvGfxFindRgb(hvg, &winTrack->color); + + //setGlobalsFromWindow(windows); // first window // TEMP HACK DOES THIS HELP? // REMOVE? + } + } 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 (galtDebug) +warn("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) { - y += trackPlusLabelHeight(track, fontHeight); + // ORIG y += trackPlusLabelHeight(track, fontHeight); + y += flatTrack->maxHeight; // DEBUG REMOVE 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); //warn("GTEX 2: track %s, sliceHeight=%d\n", track->shortLabel, sliceHeight); 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 (galtDebug) +warn("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); //warn("GTEX 3: track %s, sliceHeight=%d\n", track->shortLabel, sliceHeight); 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 - sliceHeight = trackPlusLabelHeight(track, fontHeight); + //ORIG sliceHeight = trackPlusLabelHeight(track, fontHeight); + sliceHeight = flatTrack->maxHeight; // DEBUG REMOVE sliceOffsetY = y; curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL); //warn("GTEX 4: track %s, sliceHeight=%d\n", track->shortLabel, sliceHeight); 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 { #ifdef IMAGEv2_NO_LEFTLABEL_ON_FULL if (theImgBox && track->limitedVis != tvDense) y += sliceHeight; else #endif ///def IMAGEv2_NO_LEFTLABEL_ON_FULL - y = doLeftLabels(track, hvgSide, font, y); + { + setGlobalsFromWindow(windows); // use GLOBALS from first window + int ynew = doLeftLabels(track, hvgSide, font, y); + //setGlobalsFromWindow(windows); // first window // REMOVE? this is the same as above + + y += flatTrack->maxHeight; // DEBUG REMOVE + 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; } + +//goto drawNow; + + +//setGlobalsFromWindow(windows); // first window // TEMP HACK DEBUG REMOVE THIS should not be needed + + +// DEBUG +/* Draw Windows Dividers/Diffrentiators +if (TRUE) // TODO make conditional + { + + hvGfxSetClip(hvg, fullInsideX, 0, fullInsideWidth, pixHeight); + + Color lightRed = hvGfxFindRgb(hvg, &vertWindowSeparatorColor); + for (window=windows->next; window; window=window->next) // skips first window because we already have a big vertical red where graphic begins. + hvGfxBox(hvg, window->insideX, 0, 1, pixHeight, lightRed); + + hvGfxUnclip(hvg); + } +*/ + /* Draw guidelines. */ +if (galtDebug) +warn("Draw guidelines"); // OR window separators in virtMode multi-window mode if (withGuidelines) { struct hvGfx *bgImg = hvg; // Default to the one image boolean exists = FALSE; if (theImgBox) { struct tempName gifBg; char base[64]; - safef(base,sizeof(base),"blueLines%d-%s%d-%d",pixWidth,(revCmplDisp?"r":""),insideX, + 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) { - int x; - Color lightBlue = hvGfxFindRgb(bgImg, &guidelineColor); - hvGfxSetClip(bgImg, insideX, 0, insideWidth, pixHeight); + hvGfxSetClip(bgImg, fullInsideX, 0, fullInsideWidth, pixHeight); y = gfxBorder; - for (x = insideX+guidelineSpacing-1; xnext) // background under every other window + { + //if (blueBack) + if (window->regionOdd) + hvGfxBox(bgImg, window->insideX, 0, window->insideWidth, pixHeight, lightBlue); + //blueBack = !blueBack; + } + } + 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; xshortLabel, sliceHeight); curSlice = imgTrackSliceUpdateOrAdd(curImgTrack,stData,theOneImg,rulerTtl, sliceWidth[stData],sliceHeight, sliceOffsetX[stData],sliceOffsetY); (void) sliceMapFindOrStart(curSlice,RULER_TRACK_NAME,NULL); // No common linkRoot } - y = doDrawRuler(hvg,&newWinWidth,&rulerClickHeight,rulerHeight,yAfterRuler,yAfterBases,font, - fontHeight,rulerCds); + + // need to have real winBaseCount to draw ruler scale + + for (window=windows; window; window=window->next) + { + + // DETAILS + // warn("window : %s %s %s:%d-%d offset %d width %d window=%lu", // DEBUG REMOVE + // window->organism, window->database, window->chromName, window->winStart+1, window->winEnd, + // window->insideX, window->insideWidth, (unsigned long) window); + + 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 + //TODO REMOVE winBaseCount = saveWinBaseCount; // TODO REMOVE later + } + + /* Draw center labels. */ +if (galtDebug) +warn("Draw center labels."); if (withCenterLabels) { - hvGfxSetClip(hvg, insideX, gfxBorder, insideWidth, pixHeight - 2*gfxBorder); + setGlobalsFromWindow(windows); // use GLOBALS from 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); //warn("GTEX 6: track %s, sliceHeight=%d\n", track->shortLabel, sliceHeight); 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)) + if (isCenterLabelConditional(track)) // sometimes calls track height, especially when no data there + { + //warn("isCenterLabelConditional(track)=TRUE %s", track->track); imgTrackUpdateCenterLabelSeen(curImgTrack,isCenterLabelConditionallySeen(track) ? clNowSeen : clNotSeen); } + } if (trackShouldUseAjaxRetrieval(track)) + { + //warn("trackShouldUseAjaxRetrieval(%s)", track->track); // DEBUG REMOVE y += REMOTE_TRACK_HEIGHT; + } else - y = doCenterLabels(track, track, hvg, font, y); + { + int savey = y; // GALT + //warn("calling doCenterLabels fullInsideWidth=%d", fullInsideWidth); // DEBUG REMOVE + 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; // GALT + } } hvGfxUnclip(hvg); + + setGlobalsFromWindow(windows); // first window } + +//warn("Start window draw: %s:%d-%d offset %d width %d", + // chromName, winStart+1, winEnd, insideX, insideWidth); + +//drawNow: // DEBUG REMOVE + /* Draw tracks. */ + +if (galtDebug) +warn("Draw tracks"); + { + long lastTime = 0; y = yAfterRuler; if (measureTiming) lastTime = clock1000(); for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next) { track = flatTrack->track; - if (track->limitedVis == tvHide) + + // parallelize more this?: + + //ORIG if (track->limitedVis == tvHide) + if (isLimitedVisHiddenForAllWindows(track)) continue; + //warn("flatTrack->track->track: %s", flatTrack->track->track); // DEBUG REMOVE + //fflush(stdout); // DEBUG REMOVE + int centerLabelHeight = (isCenterLabelIncluded(track) ? fontHeight : 0); + //warn("flatTrack->track->track: %s isCenterLabelIncluded(track): %d " + // "trackPlusLabelHeight(track, fontHeight): %d", + //flatTrack->track->track, isCenterLabelIncluded(track), trackPlusLabelHeight(track, fontHeight)); // DEBUG REMOVE int yStart = y + centerLabelHeight; - int yEnd = y + trackPlusLabelHeight(track, fontHeight); + // ORIG int yEnd = y + trackPlusLabelHeight(track, fontHeight); + int yEnd = y + flatTrack->maxHeight; // DEBUG REMOVE if (theImgBox) { // data slice of tracks sliceOffsetY = yStart; sliceHeight = yEnd - yStart - 1; curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL); if (sliceHeight > 0) { //warn("GTEX 7: track %s, sliceHeight=%d\n", track->shortLabel, sliceHeight); 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 - y = doDrawItems(track, hvg, font, y, &lastTime); + { + int savey = y; // DEBUG REMOVE + struct track *winTrack; + + for (window=windows, winTrack=track; window; window=window->next, winTrack=winTrack->nextWindow) + { + setGlobalsFromWindow(window); + //warn("Draw tracks track dump %s\n", makeTrackDumpLink(track)); // DEBUG REMOVE + 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); + //warn("y=%d ynew=%d (ynew-y)=%d flatTrack->maxHeight=%d", y, ynew, ynew - y, flatTrack->maxHeight); + 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; // DEBUG REMOVE + } 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++; } + +if (galtDebug) +warn("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 - sliceHeight = trackPlusLabelHeight(track, fontHeight); + // ORIG sliceHeight = trackPlusLabelHeight(track, fontHeight); + sliceHeight = flatTrack->maxHeight; // DEBUG REMOVE sliceOffsetY = y; curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL); //warn("WARN 8: track %s, sliceHeight=%d\n", track->shortLabel, sliceHeight); 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 - y += trackPlusLabelHeight(track, fontHeight); + // ORIG y += trackPlusLabelHeight(track, fontHeight); + y += flatTrack->maxHeight; // DEBUG REMOVE } } + /* Make map background. */ +if (galtDebug) +warn("Make map background"); y = yAfterRuler; for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next) { track = flatTrack->track; if (track->limitedVis != tvHide) { if (theImgBox) { // Set imgTrack in case any map items will be set - sliceHeight = trackPlusLabelHeight(track, fontHeight); + // ORIG sliceHeight = trackPlusLabelHeight(track, fontHeight); + sliceHeight = flatTrack->maxHeight; // DEBUG REMOVE sliceOffsetY = y; curImgTrack = imgBoxTrackFind(theImgBox,track->tdb,NULL); } + + // TODO Parallelize? + setGlobalsFromWindow(windows); // first window doTrackMap(track, hvg, y, fontHeight, trackPastTabX, trackPastTabWidth); - y += trackPlusLabelHeight(track, fontHeight); + + // ORIG y += trackPlusLabelHeight(track, fontHeight); + y += flatTrack->maxHeight; // DEBUG REMOVE } } /* 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,&winStart,&winEnd,&(tl.picWidth),NULL)) + if (imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL)) { pixWidth = tl.picWidth; - winBaseCount = winEnd - winStart; - insideWidth = tl.picWidth-gfxBorder-insideX; + 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"); +//warn("DEBUG loadFromTrackDb:: hgt.trackNameFilter=%s\n", trackNameFilter); // DEBUG REMOVE GALT 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, "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 /* build group objects from database. */ for (grp = grps; grp != NULL; grp = grp->next) { /* 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; 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); } /* 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 (cgiOptionalString("hideTracks")) { 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; } -void doNextPrevItem(boolean goNext, char *trackName) -/* In case a next item arrow was clicked on a track, change */ -/* position (i.e. winStart, winEnd, etc.) based on what track it was */ -{ -struct track *track = trackFindByName(trackList, trackName); -if ((track != NULL) && (track->nextPrevItem != NULL)) - track->nextPrevItem(track, goNext); -} - char *collapseGroupVar(char *name) /* Construct cart variable name for collapsing group */ { static char varName[256]; safef(varName, sizeof(varName), "%s%s_%s_%s", "hgt", "group", name, "close"); return (cloneString(varName)); } boolean isCollapsedGroup(struct group *grp) /* Determine if group is collapsed */ { return cartUsualInt(cart, collapseGroupVar(grp->name), grp->defaultIsClosed); } void collapseGroupGoodies(boolean isOpen, boolean wantSmallImage, char **img, char **indicator, char **otherState) /* Get image, char representation of image, and 'otherState' (1 or 0) * for a group, based on whether it is collapsed, and whether we want * larger or smaller image for collapse box */ { if (otherState) *otherState = (isOpen ? "1" : "0"); if (indicator) *indicator = (isOpen ? "-" : "+"); if (img) { if (wantSmallImage) *img = (isOpen ? "../images/remove_sm.gif" : "../images/add_sm.gif"); else *img = (isOpen ? "../images/remove.gif" : "../images/add.gif"); } } void collapseGroup(char *name, boolean doCollapse) /* Set cart variable to cause group to collapse */ { cartSetBoolean(cart, collapseGroupVar(name), doCollapse); } void myControlGridStartCell(struct controlGrid *cg, boolean isOpen, char *id) /* Start a new cell in control grid; support Javascript open/collapsing by including id's in tr's. id is used as id prefix (a counter is added to make id's unique). */ { static int counter = 1; if (cg->columnIx == cg->columns) controlGridEndRow(cg); if (!cg->rowOpen) { #if 0 /* This is unnecessary, b/c we can just use a blank display attribute to show the element rather than figuring out what the browser specific string is to turn on display of the tr; however, we may want to put in browser specific strings in the future, so I'm leaving this code in as a reference. */ char *ua = getenv("HTTP_USER_AGENT"); char *display = ua && stringIn("MSIE", ua) ? "block" : "table-row"; #endif // use counter to ensure unique tr id's (prefix is used to find tr's in javascript). printf("", isOpen ? "" : "style='display: none' ", id, counter++); cg->rowOpen = TRUE; } if (cg->align) printf("", cg->align); else printf(""); } static void pruneRedundantCartVis(struct track *trackList) /* When the config page or track form has been submitted, there usually * are many track visibility cart variables that have not been changed * from the default. To keep down cart bloat, prune those out before we * save the cart. changeTrackVis does this too, but this is for the * more common case where track visibilities are tweaked. */ { struct track *track; 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("bigMaf" , 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 void printTrackTiming() { -hPrintf("track, load time, draw time, total
    \n"); +hPrintf("track, load time, draw time, total (first window)
    \n"); +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); + 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); + int avgLoad = avgLoadTime(track); + int avgDraw = avgDrawTime(track); + hPrintf("%s, %d, %d, %d
    \n", track->shortLabel, + avgLoad, avgDraw, + avgLoad + avgDraw); if (startsWith("wigMaf", track->tdb->type)) if (track->subtracks) if (track->subtracks->loadTime) hPrintf("    %s wiggle, load %d
    \n", track->shortLabel, track->subtracks->loadTime); } } 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 custom trackList with just trackName +struct track *myTrackList = getTrackListForOneTrack(trackName); +struct track *track = trackFindByName(myTrackList, trackName); +if ((track != NULL) && (track->nextPrevItem != 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 \n\n", hgTracksName()); -jsonObjectAdd(jsonForClient, "insideX", newJsonNumber(insideX)); +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); -if (measureTiming) - measureTime("Time before getTrackList"); -trackList = getTrackList(&groupList, defaultTracks ? -1 : -2); -if (measureTiming) - measureTime("getTrackList"); -makeGlobalTrackHash(trackList); + +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); } -/* 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)); - if(!psOutput && !cartUsualBoolean(cart, "hgt.imageV1", FALSE)) { - // 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 = (insideX - gfxBorder*3) + 2; - theImgBox = imgBoxStart(database,chromName,winStart,winEnd, - (!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,&winStart,&winEnd,&(tl.picWidth),0)) + + // re-establish the enlarged portal + if (imgBoxPortalDimensions(theImgBox,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL,NULL,NULL,NULL,NULL)) { - winBaseCount = winEnd - winStart; - insideWidth = tl.picWidth-gfxBorder-insideX; + 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); /* remove cart priority variables if they are set to the default values in the trackDb */ if (!hTrackOnChrom(track->tdb, chromName)) { 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. + + +//warn("copy track structures for multiple windows"); // DEBUG REMOVE +// COPY TRACK STRUCTURES for other windows. +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) + { + struct track *copy; + AllocVar(copy); + memmove(copy,track,sizeof(struct track)); + copy->next = NULL; + 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) + { + struct track *subcopy; + AllocVar(subcopy); + memmove(subcopy,subtrack,sizeof(struct track)); + subcopy->next = NULL; + subcopy->nextWindow = NULL; + subcopy->prevWindow = subtrack; + slAddHead(©->subtracks, subcopy); + subtrack->nextWindow = subcopy; + } + } + slReverse(©->subtracks); + //warn("%s track subs: %d copy subs: %d", track->track, slCount(track->subtracks), slCount(copy->subtracks)); + } + } + slReverse(&newTrackList); + trackList = newTrackList; + window->next->trackList = trackList; // save new track list in window + } +trackList = windows->trackList; // restore original track list + +// DEBUG REMOVE verify results. +//for (window=windows; window; window=window->next) +// { +// warn("%s %d", window->chromName, slCount(window->trackList)); +// } + +// Loop over each window loading all tracks +//warn("Loop over each window loading all tracks"); // DEBUG REMOVE +trackLoadingInProgress = TRUE; + +// TEMP HACK GALT REMOVE +bool loadHack = FALSE; //TRUE; // probably should only be tried on non-wiggle tracks +//warn ("loadHack = %d", loadHack); // DEBUG REMOVE +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 + //warn("slCount(trackList) just before load regular tracks: %d", slCount(trackList)); // DEBUG REMOVE + int visCount = 0; // DEBUG REMOVE /* load regular tracks */ for (track = trackList; track != NULL; track = track->next) { if (track->visibility != tvHide) { + ++visCount; // DEBUG REMOVE if (!track->parallelLoading) { if (measureTiming) lastTime = clock1000(); + //warn("load regular tracks BEFORE load and check max window to draw %s\n", makeTrackDumpLink(track)); // DEBUG REMOVE checkMaxWindowToDraw(track); + + //warn("load regular tracks BEFORE check if wiggling %s\n", makeTrackDumpLink(track)); // DEBUG REMOVE checkIfWiggling(cart, track); + + //warn("load regular tracks ABOUT to call loadItems() %s\n", makeTrackDumpLink(track)); // DEBUG REMOVE + + if (!loadHack) + { + track->loadItems(track); // DEBUG RESTORE !!! + } + 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? + } + } + + // DEBUG REMOVE how many visible subtracks? + int visSubCount = 0; + struct track *sub; + for (sub=track->subtracks; sub; sub=sub->next) + if (sub->visibility != tvHide) + ++visSubCount; + + //warn("load regular tracks AFTER loadItems #%d %s type %s subtracks %d vis %d %s\n", + //visCount, track->track, trackDbSetting(track->tdb, "type"), + //slCount(track->subtracks), visSubCount, makeTrackDumpLink(track)); // DEBUG REMOVE if (measureTiming) { thisTime = clock1000(); track->loadTime = thisTime - lastTime; } } } } + //warn("after load regular tracks: %d", visCount); // DEBUG REMOVE 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 + +////////////// 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"); + 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,&winStart,&winEnd,&(tl.picWidth),NULL)) + if (imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL)) { - winBaseCount = winEnd - winStart; - insideWidth = tl.picWidth-gfxBorder-insideX; + virtWinBaseCount = virtWinEnd - virtWinStart; + fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX; } } /* Center everything from now on. */ hPrintf("
    \n"); -// info for drag selection javascript -jsonObjectAdd(jsonForClient, "winStart", newJsonNumber(winStart)); -jsonObjectAdd(jsonForClient, "winEnd", newJsonNumber(winEnd)); -jsonObjectAdd(jsonForClient, "chromName", newJsonString(chromName)); +// OLD WAY +//jsonObjectAdd(jsonForClient, "winStart", newJsonNumber(winStart)); +//jsonObjectAdd(jsonForClient, "winEnd", newJsonNumber(winEnd)); +//jsonObjectAdd(jsonForClient, "chromName", newJsonString(chromName)); + +// DONE instead of trying to add new variables here, +// see if we can pass the virt versions in as if they were the original variables. +// That way less of hgTracks.js would need to be changed. +jsonObjectAdd(jsonForClient, "winStart", newJsonNumber(virtWinStart)); +jsonObjectAdd(jsonForClient, "winEnd", newJsonNumber(virtWinEnd)); +jsonObjectAdd(jsonForClient, "chromName", newJsonString(virtChromName)); + +// Tell javascript about multiple windows info +// GALT TODO if end up not using this information, turn this off +// as it sends several K of extra data on a view with many windows. +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))); + + if(trackImgOnly && !ideogramToo) { - struct track *ideoTrack = chromIdeoTrack(trackList); + 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. */ } + } 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]; + // DEBUG RESTORE + 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("", chromName, winStart+1, winEnd); + 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); } - sprintf(buf, "%s:%d-%d", chromName, winStart+1, winEnd); + // DEBUG RESTORE + 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, winEnd - winStart); + 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)); + 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 chromsome 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); } - if (survey && differentWord(survey, "off")) - hPrintf("  " - "%s\n", - survey, surveyLabel ? surveyLabel : "Take survey"); - hPutc('\n'); } } -/* Make chromsome ideogram gif and map. */ -makeChromIdeoImage(&trackList, psOutput, ideoTn); + } +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. "); 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); /* First track group that is not the custom track group (#1) * or a track hub, gets the Base Position track * unless it's collapsed. */ if (!showedRuler && !isHubTrack(group->name) && differentString(group->name, "user") ) { 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"); +// 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(int newSize) +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. */ { -int center = ((long long int)winStart + (long long int)winEnd)/2; +long center = ((long long)virtWinStart + (long long)virtWinEnd)/2; if (center < 0) - errAbort("zoomToSize: error computing center: %d = (%d + %d)/2\n", - center, winStart, winEnd); -if (newSize > seqBaseCount) - newSize = seqBaseCount; -winStart = center - newSize/2; -winEnd = winStart + newSize; -if (winStart <= 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) { - winStart = 0; - winEnd = newSize; + virtWinStart = 0; + virtWinEnd = newSize; } -else if (winEnd > seqBaseCount) +else if (virtWinEnd > virtSeqBaseCount) { - winEnd = seqBaseCount; - winStart = winEnd - newSize; + virtWinEnd = virtSeqBaseCount; + virtWinStart = virtWinEnd - newSize; } -winBaseCount = winEnd - winStart; +virtWinBaseCount = virtWinEnd - virtWinStart; } void zoomAroundCenter(double amount) /* Set ends so as to zoom around center by scaling amount. */ { -double newSizeDbl = (winBaseCount*amount + 0.5); -int newSize; -if (newSizeDbl > seqBaseCount) - newSize = seqBaseCount; +double newSizeDbl = (virtWinBaseCount*amount + 0.5); +long newSize; +if (newSizeDbl > virtSeqBaseCount) + newSize = virtSeqBaseCount; else if (newSizeDbl < 1.0) newSize = 1; else - newSize = (int)newSizeDbl; + newSize = (long)newSizeDbl; zoomToSize(newSize); } void zoomToBaseLevel() /* Set things so that it's zoomed to base level. */ { -zoomToSize(insideWidth/tl.mWidth); +zoomToSize(fullInsideWidth/tl.mWidth); if (rulerMode == tvHide) cartSetString(cart, "ruler", "dense"); } void relativeScroll(double amount) /* Scroll percentage of visible window. */ { -int offset; -int newStart, newEnd; +long offset; +long newStart, newEnd; if (revCmplDisp) amount = -amount; -offset = (int)(amount * winBaseCount); +offset = (long)(amount * virtWinBaseCount); /* Make sure don't scroll of ends. */ -newStart = winStart + offset; -newEnd = winEnd + offset; +newStart = virtWinStart + offset; +newEnd = virtWinEnd + offset; if (newStart < 0) - offset = -winStart; -else if (newEnd > seqBaseCount) - offset = seqBaseCount - winEnd; + offset = -virtWinStart; +else if (newEnd > virtSeqBaseCount) + offset = virtSeqBaseCount - virtWinEnd; + +//warn("\nGALT relativeScroll %f results in offset %ld to apply to virtWinStart,End\n", amount, offset); // DEBUG REMOVE /* Move window. */ -winStart += offset; -winEnd += offset; +virtWinStart += offset; +virtWinEnd += offset; } -void dinkWindow(boolean start, int dinkAmount) +void dinkWindow(boolean start, long dinkAmount) /* Move one end or other of window a little. */ { if (revCmplDisp) { start = !start; dinkAmount = -dinkAmount; } if (start) { - winStart += dinkAmount; - if (winStart < 0) winStart = 0; + virtWinStart += dinkAmount; + if (virtWinStart < 0) + virtWinStart = 0; } else { - winEnd += dinkAmount; - if (winEnd > seqBaseCount) - winEnd = seqBaseCount; + virtWinEnd += dinkAmount; + if (virtWinEnd > virtSeqBaseCount) + virtWinEnd = virtSeqBaseCount; } } -int dinkSize(char *var) +long dinkSize(char *var) /* Return size to dink. */ { char *stringVal = cartOptionalString(cart, var); double x; -int insideX = trackOffsetX(); /* The global versions of these are not yet set */ -int insideWidth = tl.picWidth-gfxBorder-insideX; -double guideBases = (double)guidelineSpacing * (double)(winEnd - winStart) - / ((double)insideWidth); +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); -int ret = round(x*guideBases); +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"); +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); withPriorityOverride = cartUsualBoolean(cart, configPriorityOverride, FALSE); -insideX = trackOffsetX(); -insideWidth = tl.picWidth-gfxBorder-insideX; +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); + +// DEBUG SHOW matching regions found. +//warn("DEBUG mList post-sort-merge search positions: %d", slCount(mList)); +//struct positionMatch *m; +//for (m=mList; m; m=m->next) + //{ + //long span = m->virtEnd - m->virtStart; + //warn("virtStar=%ld virtEnd=%ld span=%ld", m->virtStart, m->virtEnd, span); + //} + +// 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(); +//warn("remapHighlightPos h-> chrom=%s chromStart=%ld chromEnd=%ld", h->chrom, h->chromStart, h->chromEnd); // DEBUG REMOVE +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 newPos[256]; +//warn("top of tracksDisplay()\n"); // DEBUG REMOVE +//warn("cartdump", cartSessionId(cart)); // DEBUG REMOVE char *defaultPosition = hDefaultPos(database); char titleVar[256]; +char *oldPosition = cartUsualString(cart, "oldPosition", ""); +boolean findNearest = cartUsualBoolean(cart, "findNearest", FALSE); +cartRemove(cart, "findNearest"); +//warn("findNearest = %d\n", findNearest); // DEBUG REMOVE + +boolean positionIsVirt = FALSE; position = getPositionFromCustomTracks(); +if (NULL == position) + { + if (sameOk(cgiOptionalString("position"), "lastDbPos")) + { + position = cartGetPosition(cart, database, &lastDbPosCart); + //warn("cartGetPosition: database=%s position=%s", database, position); // DEBUG REMOVE + // DEBUG REMOVE + //struct dyString *encoded = newDyString(4096); + //cartEncodeState(lastDbPosCart, encoded); + //warn("restored lastDbPosCart encoded state = [%s]", encoded->string); // DEBUG REMOVE + restoreSavedVirtPosition(); + } + else + { + position = cloneString(cartUsualString(cart, "position", NULL)); + } + //warn("position = %s\n", position); // DEBUG REMOVE + if (startsWith("virt:", position)) + { + position = stripCommas(position); // sometimes the position string arrives with commas in it. + //warn("positionIsVirt=TRUE position = %s\n", position); // DEBUG REMOVE + positionIsVirt = TRUE; + goto gotVirtPos; + } + } -if (position == NULL) - position = cartGetPosition(cart, database); 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"); } chromName = NULL; winStart = 0; if (isGenome(position) || NULL == (hgp = findGenomePos(database, position, &chromName, &winStart, &winEnd, cart))) { + //warn("doing weird stuff with isGenome() and findGenomePos(), add more debugging"); 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. char *lastPosition = cartUsualString(cart, "lastPosition", hDefaultPos(database)); cartSetString(cart, "position", lastPosition); return; } -// save the current position to the cart var position. -cartSetDbPosition(cart, database, position); +// TODO NOTE lastPosition gets set in cart.c -seqBaseCount = hChromSize(database, chromName); -winBaseCount = winEnd - winStart; + +gotVirtPos: + +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(); +//warn("after setLayoutGlobals() fullInsideX=%d fullInsideWidth=%d tl.picWidth=%d gfxBorder=%d\n", + //fullInsideX, fullInsideWidth, tl.picWidth, gfxBorder); // DEBUG REMOVE + +virtModeType = cartUsualString(cart, "virtModeType", virtModeType); +//warn("virtModeType=%s\n", virtModeType); // DEBUG REMOVE + +if (positionIsVirt && virtualSingleChrom()) + { + // we need chromName to be set before initRegionList() gets called. + position = cartUsualString(cart, "nonVirtPosition", ""); + //warn("positionIsVirt && virtualSingleChrom(), going to nonVirtPosition %s", position); // DEBUG REMOVE + if (!sameString(position,"")) + parseNonVirtPosition(position); + } +//warn("chromName=%s", chromName); // DEBUG REMOVE + +// TODO GALT do we need to add in other types that now depend on emGeneTable too? maybe singleTrans? +if (sameString(virtModeType, "exonMostly") || sameString(virtModeType, "geneMostly")) + { + setEMGeneTrack(); + if (!emGeneTable) // there is no available gene table, undo exonMostly or geneMostly + { + virtModeType = "default"; + cartSetString(cart, "virtModeType", virtModeType); + } + } + +lastVirtModeType = cartUsualString(cart, "lastVirtModeType", lastVirtModeType); + +again: +//warn("virtModeType=%s lastVirtModeType=%s\n", virtModeType, lastVirtModeType); // DEBUG REMOVE +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); + //warn("leaving virtMode, going to position %s", position); // DEBUG REMOVE + if (!sameString(position,"")) + parseNonVirtPosition(position); + } + + +//warn("\nGALT before initRegionList chromName=%s winStart=%d winEnd=%d\n", chromName, winStart,winEnd); // DEBUG REMOVE +if (!initRegionList()) // initialize the region list, sets virtModeExtraState + { // 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; + goto again; + } + +// 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)) + { + //hPrintf("

    %ld


    \n", virtWinStart); + //hPrintf("

    %ld


    \n", 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); +//warn("\nGALT virtModeExtraState=%s lastVirtModeExtraState=%s\n", virtModeExtraState, lastVirtModeExtraState); // DEBUG REMOVE + +// DISGUISED POSITION +if (!startsWith("virt:", position) && (virtualSingleChrom())) + { + //warn("virtualSingleChrom trying to find best vchrom location corresponding to chrom=%s, winStart=%d, winEnd=%d\n", chromName, winStart, winEnd); // DEBUG REMOVE + findNearest = TRUE; + + if (!(chromName && findNearestVirtMatch(chromName, winStart, winEnd, findNearest, &virtWinStart, &virtWinEnd))) // try to find the nearest match + { // 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."); // KEEP? + 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", ""); + //warn("leaving virtMode, going to position %s", position); // DEBUG REMOVE + if (!sameString(position,"")) + parseNonVirtPosition(position); + char *nvh = cartUsualString(cart, "nonVirtHighlight", ""); + //warn("CHANGE FROM ONE NON-DEFAULT virtMode to another nvh=%s",nvh); // DEBUG REMOVE + if (!sameString(nvh, "")) // DEBUG 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); + //warn("\nGALT after parseVirtPosition(%s), virtWinStart=%ld virtWinEnd=%ld\n", position, virtWinStart, virtWinEnd); // DEBUG REMOVE + } + 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)) + { + //warn("\nGALT before virtChromSearchForPosition(), position=%s oldPosition=%s\n", position, oldPosition); // DEBUG REMOVE + + //warn("\nGALT chrom=%s, winStart=%d, winEnd=%d\n", chromName, winStart, winEnd); // DEBUG REMOVE + + 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); // DEBUG REMOVE + // 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, "kcGenes")) // create 1k window near middle of vchrom + { + //warn("trying to find best vchrom location corresponding to chrom=%s, winStart=%d, winEnd=%d\n", chromName, winStart, winEnd); // DEBUG REMOVE + + if (!(chromName && findNearestVirtMatch(chromName, winStart, winEnd, findNearest, &virtWinStart, &virtWinEnd))) // try to find the nearest match + { + warn("Unable to find any region near the position on the chromosome in the multi-regions. Now using middle of view."); // KEEP? + 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")) + { + virtWinStart = 0; + virtWinEnd = virtSeqBaseCount; + virtMode = TRUE; + } + + remapHighlightPos(); + + } + //warn("\nGALT default virtWinStart=%ld virtWinEnd=%ld\n", virtWinStart, virtWinEnd); // DEBUG REMOVE + + } + +if (virtMode) + virtChromName = "virt"; +else + virtChromName = chromName; + +virtWinBaseCount = virtWinEnd - virtWinStart; + + +//warn("\nGALT BEFORE navigation section, virtChromName=%s virtWinStart=%ld virtWinEnd=%ld\n", virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE 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)); + +//warn("\nGALT BEFORE clipping section, virtChromName=%s virtWinStart=%ld virtWinEnd=%ld\n", virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE + + /* Clip chromosomal position to fit. */ -if (winEnd < winStart) +if (virtWinEnd < virtWinStart) { - int temp = winEnd; - winEnd = winStart; - winStart = temp; + // swap start and end + long temp = virtWinEnd; + virtWinEnd = virtWinStart; + virtWinStart = temp; } -else if (winStart == winEnd) +else if (virtWinStart == virtWinEnd) { - winStart -= 1; - winEnd += 1; + //warn("\nGALT weird virtWinStart == virtWinEnd = %ld\n", virtWinStart); // DEBUG REMOVE + // Size 0 window + virtWinStart -= 1; + virtWinEnd += 1; } -if (winStart < 0) +if (virtWinStart < 0) { - winStart = 0; + virtWinStart = 0; } -if (winEnd > seqBaseCount) +if (virtWinEnd > virtSeqBaseCount) { - winEnd = seqBaseCount; + virtWinEnd = virtSeqBaseCount; } -if (winStart > seqBaseCount) +if (virtWinStart > virtSeqBaseCount) { - winStart = seqBaseCount - 1000; + virtWinStart = virtSeqBaseCount - 1000; } -winBaseCount = winEnd - winStart; -if (winBaseCount <= 0) - hUserAbort("Window out of range on %s", chromName); +virtWinBaseCount = virtWinEnd - virtWinStart; +if (virtWinBaseCount <= 0) + hUserAbort("Window out of range on %s", virtChromName); + +//warn("\nGALT BEFORE portal start section, virtChromName=%s virtWinStart=%ld virtWinEnd=%ld\n", virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE + +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; + } + + } + +//warn("AFTER portal start section, virtChromName=%s virtWinStart=%ld virtWinEnd=%ld\n", virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE + + +// 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."); + +//warn("winCount=%d\n", slCount(windows)); + +//warn("AFTER makeWindowListFromVirtChrom, virtChromName=%s virtWinStart=%ld virtWinEnd=%ld\n", virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE + + +allocPixelsToWindows(); // sets windows insideWidth and insideX + +//warn("AFTER allocPixelsToWindows, virtChromName=%s virtWinStart=%ld virtWinEnd=%ld\n", virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE + + + +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; + } + } + +//warn("AFTER portal demensions section, virtChromName=%s virtWinStart=%ld virtWinEnd=%ld\n", virtChromName, virtWinStart, virtWinEnd); // DEBUG REMOVE + + +setGlobalsFromWindow(windows); // first window + +seqBaseCount = hChromSize(database, chromName); + +//warn("virtWinStart=%ld virtWinEnd=%ld\n", virtWinStart, virtWinEnd); // DEBUG REMOVE +//warn("virtWinBaseCount=%ld virtSeqBaseCount=%ld\n", virtWinBaseCount, virtSeqBaseCount); // DEBUG REMOVE + /* Save computed position in cart. */ -sprintf(newPos, "%s:%d-%d", chromName, winStart+1, winEnd); cartSetString(cart, "org", organism); cartSetString(cart, "db", database); -cartSetString(cart, "position", newPos); + +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); // DEBUG REMOVE MAYBE +//cartSetString(cart, "lastPosition", position); // DEBUG REMOVE TESTING. + +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"); + +// DEBUG REMOVE +//struct dyString *encoded = newDyString(4096); +//cartEncodeState(lastDbPosCart, encoded); +//warn("lastDbPosCart encoded state = [%s]", encoded->string); // DEBUG REMOVE + +//warn("database=%s, position=%s, virtModeType=%s, virtModeExtraState=%s", database, position, virtModeType, virtModeExtraState); // DEBUG REMOVE +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"); 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("
    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 view DNAv then d
    zoom out 100x0
    zoom out 10xK exon viewe then v
    zoom out 100x0 default viewd then v
    view DNAv then d
    \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); 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)); } -boolean gotExtTools = extToolsEnabled(); -setupHotkeys(gotExtTools); -if (gotExtTools) - printExtMenuData(); - } +