fc3472e7e9978eb81e3ac14e985bf27e5196ab73 kate Fri Jul 24 10:58:01 2020 -0700 Recommended track sets feature. refs #25601 diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index 80dc532..423316e 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -60,30 +60,33 @@ #include "search.h" #include "errCatch.h" #include "iupac.h" #include "botDelay.h" #include "chromInfo.h" #include "extTools.h" #include "basicBed.h" #include "customFactory.h" #include "genbank.h" #include "bigWarn.h" #include "wigCommon.h" #include "knetUdc.h" #include "hex.h" #include #include "customComposite.h" + +boolean isSessChanged = FALSE; + //#include "bed3Sources.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", @@ -7766,30 +7769,179 @@ { if (jsonList == NULL) jsonList = newJsonList(NULL); struct jsonElement *collection = newJsonObject(newHash(4)); jsonObjectAdd(collection, "track", newJsonString(tdb->track)); jsonObjectAdd(collection, "shortLabel", newJsonString(tdb->shortLabel)); jsonListAdd(jsonList, collection); } } if (jsonList != NULL) jsonObjectAdd(jsonForClient, "collections", jsonList); } } +// TODO: move to a file in lib (cheapcgi?) +char *cgiVarStringSort(char *cgiVarString) +/* Return cgi var string sorted alphabetically by vars */ +{ +struct slName *vars = slNameListFromString(cgiVarString, '&'); +slNameSort(&vars); +char *cgiString = slNameListToString(vars, '&'); +return cgiString; +} + +char *cgiTrackVisString(char *cgiVarString) +/* Filter cgi var string (var=val&) to just track visibilities, but equivalence + * dense/pack/squish/full by replacing as 'on'. + * Return string with track data vars in alphabetic order */ +{ +// TODO: use cheapcgi.cgiParsedVarsNew() to parse and get list ? +#define MAX_CGI_VARS 1000 +// NOTE: Ana featured sessions have: 473, 308, 288 +char *cgiVars[MAX_CGI_VARS]; +int ct = chopByChar(cgiVarString, '&', cgiVars, sizeof cgiVars); + +char *cgiVar = NULL; +char *val = NULL; +int i; +struct dyString *dsCgiTrackVis = dyStringCreate("db=%s", database); +for (i=0; inext) + { + struct trackDb *parent = track->tdb->parent; + if (parent) + { + if (hashLookup(parentHash, parent->track) == NULL) + { + hashStore(parentHash, parent->track); + if (parent->isShow) + outIfNotPresent(cart, dy, parent->track, tvShow); + } + } + if (track->tdb->visibility != tvHide) + outIfNotPresent(cart, dy, track->tdb->track, track->tdb->visibility); + } +// Put a variable in the cart that says we put the default +// visibilities in it. +if (dy) + dyStringPrintf(dy,"&%s=on", CART_HAS_DEFAULT_VISIBILITY); +else + printf("%s on", CART_HAS_DEFAULT_VISIBILITY); +} + +boolean hasSessionChanged() +{ +/* Have any tracks been hidden or added ? */ + +// get featured session from database +char *sessionName = cartString(cart, "hgS_otherUserSessionName"); +if (!sessionName) + return FALSE; +struct sqlConnection *conn = hConnectCentral(); +char query[1000]; +sqlSafef(query, sizeof query, + "SELECT contents FROM namedSessionDb where sessionName='%s'", + replaceChars(sessionName, " ", "%20")); +char *cartString = sqlQuickNonemptyString(conn, query); +hDisconnectCentral(&conn); + +// TODO: use cheapcgi.cgiParsedVarsNew() to parse and get list ? +#define MAX_SESSION_LEN 100000 +char *curSessCart = (char *)needMem(MAX_SESSION_LEN); +cgiDecodeFull(cartString, curSessCart, MAX_SESSION_LEN); +char *curSessVisTracks = cgiTrackVisString(curSessCart); + +// get track-related vars from current cart +struct dyString *dsCgiVars = newDyString(0); +cartEncodeState(cart, dsCgiVars); +outDefaultTracks(cart, dsCgiVars); +char *this = dyStringCannibalize(&dsCgiVars); +// TODO: again, better parsing +char *this2 = replaceChars(this, "%2D", "-"); +char *thisSessVars = replaceChars(this2, "%2B", "+"); +char *thisSessVisTracks = cgiTrackVisString(thisSessVars); + +//freeMem(curSessCart); +boolean isSessChanged = FALSE; +if (differentString(curSessVisTracks, thisSessVisTracks)) + { + isSessChanged = TRUE; + #ifdef DEBUG + uglyf("
curSess vis tracks: %s", curSessVisTracks); + uglyf("
thsSess vis tracks: %s", thisSessVisTracks); + #endif + } +return isSessChanged; +} + void doTrackForm(char *psOutput, struct tempName *ideoTn) /* Make the tracks display form with the zoom/scroll buttons and the active * image. If the ideoTn parameter is not NULL, it is filled in if the * ideogram is created. */ { struct group *group; struct track *track; char *freezeName = NULL; boolean hideAll = cgiVarExists("hgt.hideAll"); boolean defaultTracks = cgiVarExists("hgt.reset"); boolean showedRuler = FALSE; boolean showTrackControls = cartUsualBoolean(cart, "trackControlsOnMain", TRUE); long thisTime = 0, lastTime = 0; basesPerPixel = ((float)virtWinBaseCount) / ((float)fullInsideWidth); @@ -8250,71 +8402,103 @@ ) { for(window=windows;window;window=window->next) { struct track *ideoTrack = chromIdeoTrack(window->trackList); if (ideoTrack) { ideoTrack->limitedVisSet = TRUE; ideoTrack->limitedVis = tvHide; /* Don't draw in main gif. */ } } } if (trackImgOnly && !ideogramToo) { + // right-click to change viz 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 . */ + /* 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"); + hPrintf(""); + + if (defaultTracks || hideAll) + cartRemove(cart, "hgS_otherUserSessionLabel"); + char *sessionLabel = cartOptionalString(cart, "hgS_otherUserSessionLabel"); + if (sessionLabel) + { + char *panel = "recTrackSetsPanel"; + isSessChanged = hasSessionChanged(); + + struct dyString *hoverText = dyStringNew(0); + dyStringPrintf(hoverText, "Your browser is displaying the %s track set%s. " + " Click to change to another.", sessionLabel, + isSessChanged ? + ", with changes (added or removed tracks) you have requested" : ""); + // TODO: cleanup layout tweaking for FF on IE10 + hPrintf("    "); + hPrintf(" "); + + hPrintf("", + panel, dyStringCannibalize(&hoverText)); + hPrintf("%s", + isSessChanged ? "gbSessionChanged" : "", sessionLabel); + hPrintf(""); + hPrintf(""); + + jsOnEventById("click", "recTrackSetLabel", "showRecTrackSetsPopup(); return false;"); + jsOnEventById("click", "removeSessionPanel", "removeSessionPanel(); return false;"); + } + 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", @@ -8475,30 +8659,31 @@ hPrintf("<<< >>>\n"); hPrintf("<<< >>>\n"); hPrintf(" \n"); // Without width cell expands table width, forcing others to sides 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) @@ -9670,31 +9855,32 @@ lastDbPosSaveCartSetting("virtModeType"); lastDbPosSaveCartSetting("lastVirtModeType"); lastDbPosSaveCartSetting("lastVirtModeExtraState"); cartSetDbPosition(cart, database, lastDbPosCart); if (cartUsualBoolean(cart, "hgt.psOutput", FALSE)) handlePostscript(); else doTrackForm(NULL, NULL); boolean gotExtTools = extToolsEnabled(); setupHotkeys(gotExtTools); if (gotExtTools) printExtMenuData(chromName); - +if (recTrackSetsEnabled()) + printRecTrackSets(); } 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(); }