05e67c59a20a5d00b810a981aef3b00c5bef82e1 max Fri Sep 20 06:03:18 2024 -0700 more features to hubtools: search in both parent and subdirs, better docs diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index dad0f84..a02985e 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -8568,30 +8568,57 @@ #ifdef NOTNOW static void printAliases(char *name) /* Print out the aliases for this sequence. */ { struct slName *names = chromAliasFindAliases(name); printf("<div id='aliases'><a title='"); for(;names; names = names->next) printf("%s;",names->name); printf("'>Aliases</a></div>"); } #endif +void printGlobalTrackControls() { + +puts("<tr><td colspan='8'>"); +puts("<label for='hubSelect'>Third-party tracks:</label>"); +puts("<select style='width:200px' id='hubSelect' name='hubUrl'>"); +struct sqlConnection *conn = hConnectCentral(); +char query[1000]; +sqlSafef(query, sizeof(query), "SELECT hubUrl, shortLabel FROM hubPublic WHERE dbList like '%%%s%%'", database); +struct sqlResult *sr = sr = sqlGetResult(conn, query); + +char **row = NULL; +while ((row = sqlNextRow(sr)) != NULL) + { + char *hubUrl = row[0]; + char *shortLabel = row[1]; + + printf("<option value='%s'>%s</option>\n", hubUrl, shortLabel); + } +sqlFreeResult(&sr); +puts("</select>"); +hPrintf("<input type='submit' name='hgt.refresh' value='Connect' " + "title='Update image with your changes'>\n"); +hDisconnectCentral(&conn); +puts("</td></tr>"); + +jsInline("$(document).ready(function() { $('#hubSelect').selectize()});\n"); +} static void paraLoadTimeoutFunc(int sig) // signal handler for alarm timeout. Tell parallel loads to stop by errAborts // in udcRead { udcReadStopMessage("Parallel read timeout message"); alarm(0); // disable the alarm } unsigned getParaLoadTimeout() // get the parallel load timeout in milliseconds if any { char *cfg = cfgOption("paraLoadTimeout"); if (cfg == NULL) @@ -9219,30 +9246,39 @@ hPrintf("<span id='%s' class='gbSessionLabelPanel' style='display: inline-block;' title='%s'>", panel, dyStringCannibalize(&hoverText)); hPrintf("<span id='recTrackSetLabel' class='gbSessionLabelText gbSessionChangeIndicator %s' " "style='margin-right: 3px;'>%s</span>", isSessChanged ? "gbSessionChanged" : "", sessionLabel); hPrintf("<i id='removeSessionPanel' title='Close' class='fa fa-remove' " "style='color: #a9a9a9; font-size:smaller; vertical-align: super;'></i>"); hPrintf("</span>"); jsOnEventById("click", "recTrackSetLabel", "showRecTrackSetsPopup(); return false;"); jsOnEventById("click", "removeSessionPanel", "removeSessionPanel(); return false;"); } hPrintf("<BR>\n"); + char* lastHgvsDb = cartOptionalString(cart, "lastHgvsDb"); + if (lastHgvsDb) + { + char* lastHgvsPos = cartOptionalString(cart, "lastHgvsPos"); + char* lastHgvsTerm = cartOptionalString(cart, "lastHgvsTerm"); + if (lastHgvsPos && lastHgvsTerm) + printf("<b>Variant in Focus:</b> %s, %s<br>", lastHgvsPos, lastHgvsTerm); + } + /* This is a clear submit button that browsers will use by default when enter is pressed in position box. */ hPrintf("<INPUT TYPE=IMAGE BORDER=0 NAME=\"hgt.dummyEnterButton\" src=\"../images/DOT.gif\">"); /* 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);"); @@ -9550,30 +9586,33 @@ hButtonWithOnClick("hgt.collapseGroups", "Collapse all", "Collapse all track groups", "return vis.expandAllGroups(false)"); hPrintf("</td>"); hPrintf("<td colspan='%d' class='controlButtons' align='CENTER' nowrap>\n", MAX_CONTROL_COLUMNS - 2); printShortcutButtons(cart, hasCustomTracks, revCmplDisp, multiRegionButtonTop); hPrintf("</td>\n"); hPrintf("<td align='right'>"); hButtonWithOnClick("hgt.expandGroups", "Expand all", "Expand all track groups", "return vis.expandAllGroups(true)"); hPrintf("</td></tr>"); + if (cfgOptionBooleanDefault("newTrackControls", FALSE)) + printGlobalTrackControls(); + cg = startControlGrid(MAX_CONTROL_COLUMNS, "left"); struct hash *superHash = hashNew(8); for (group = groupList; group != NULL; group = group->next) { if ((group->trackList == NULL) && (group->errMessage == 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, @@ -9947,56 +9986,84 @@ 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("<div style=\"margin: 10px\">\n"); printf("<H1>PDF Output</H1>\n"); - printf("PDF images can be printed with Acrobat Reader " + printf("<P>PDF images can be printed with Acrobat Reader " "and edited by many drawing programs such as Adobe " - "Illustrator or Inkscape.<BR>"); + "Illustrator or Inkscape.</P>"); + printf("<H3>Preview</H3>\n"); } + +// user can land at this page from hgTracks, which doesn't set the special variables, or from this page, which sets the special variables +boolean doShowGuides = cartUsualBoolean(cart, "guidelines", TRUE); + +if (cgiVarExists("guidelines_off")) + doShowGuides = FALSE; + +char *guideVal = ""; +if (!doShowGuides) + guideVal = "&guidelines_off=1"; + +printf("<p><div id='wrap' style='width:800px; height:400px'><IFRAME style='width:1600px; height:800px; background-color: grey; overflow:scroll; transform: scale(0.5); transform-origin: top left; border-style:none; border: 2px solid black' src=\"hgRenderTracks?%s=%s%s\"></IFRAME></DIV></p>\n", cartSessionVarName(), cartSessionId(cart), guideVal); + +puts("<p>"); +puts("<form>\n"); + +char *guideAttr = ""; +if (!doShowGuides) + guideAttr = " checked"; + +//printf("doShowGuides %d, guideVal %s guideAttr %s", doShowGuides, guideVal, guideAttr); + +//if (cartCgiUsualBoolean(cart, "PdfNoGuides", FALSE)) + //cartSetString(cart, "guidelines", "off"); + +printf("<label><input type='CHECKBOX' name='guidelines_off' value='off'%s>Hide blue guidelines</label><br>\n", guideAttr); +puts("<input type='hidden' name='hgt.psOutput' value='on' />"); +puts("<input type='submit' value='Update preview image' />"); +puts("</form></p>\n"); + doTrackForm(psTn.forCgi, &ideoPsTn); +//if (cgiBoolean("PdfNoGuides")) + //cartSetString(cart, "guidelines", "on"); + pdfFile = convertEpsToPdf(psTn.forCgi); +char *svgFile = convertPdfToSvg(pdfFile); + if (strlen(ideoPsTn.forCgi)) ideoPdfFile = convertEpsToPdf(ideoPsTn.forCgi); if (pdfFile != NULL) { - printf("<UL style=\"margin-top:5px;\">\n"); - printf("<LI>Download <A TARGET=_blank HREF=\"%s\">" - "the current browser graphic in PDF</A>\n", pdfFile); + printf("<A HREF=\"%s\"><button>Download browser graphic PDF</button></A>\n", pdfFile); + printf("<A HREF=\"%s\"><button>Download browser graphic SVG</button></A>\n", svgFile); if (ideoPdfFile != NULL) - printf("<LI>Download <A TARGET=_blank HREF=\"%s\">" - "the current chromosome ideogram in PDF</A>\n", ideoPdfFile); - printf("</UL>\n"); + printf("<A HREF=\"%s\"><button>Download chromosome ideogram</button></A>\n", ideoPdfFile); freez(&pdfFile); freez(&ideoPdfFile); - printf("EPS (PostScript) output has been discontinued in pursuit of additional features\n"); - printf("that are not PostScript-compatible. If you require PostScript output for your\n"); - printf("workflow, please <a href='https://genome.ucsc.edu/contacts.html'>reach out to us</a>\n"); - printf("and let us know what your needs are - we may be able to help.\n"); - // see redmine #1077 printf("<div style=\"margin-top:15px\">Tips for producing quality images for publication:</div>\n"); printf("<UL style=\"margin-top:0px\">\n"); printf("<LI>Add assembly name and chromosome range to the image on the\n" "<A HREF=\"hgTrackUi?g=ruler\">configuration page of the base position track</A>.\n"); printf("<LI>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("<LI>Increase the font size and remove the light blue vertical guidelines in the \n" "<A HREF=\"hgTracks?hgTracksConfigPage=configure\">image configuration menu</A>."); printf("<LI>In the image configuration menu, change the size of the image,\n" "to make it look more square.\n"); printf("</UL>\n"); printf("</div>\n"); } @@ -10014,30 +10081,34 @@ 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 (cgiVarExists("guidelines_off")) + withGuidelines = FALSE; + 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); revCmplDisp = cartUsualBooleanDb(cart, database, REV_CMPL_DISP, FALSE); emPadding = cartUsualInt(cart, "emPadding", emPadding); gmPadding = cartUsualInt(cart, "gmPadding", gmPadding); withPriorityOverride = cartUsualBoolean(cart, configPriorityOverride, FALSE); fullInsideX = trackOffsetX(); fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX; } @@ -10176,47 +10247,74 @@ safef(cartVar, sizeof cartVar, "%s.%s:%ld-%ld#%s", h->db, MULTI_REGION_VIRTUAL_CHROM_NAME, 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"); } } } static void setupTimeWarning() /* add javascript that outputs a warning message if page takes too long to load */ { -char *maxTimeStr = cfgOption("warnSeconds"); +char *maxTimeStr = cfgOptionDefault("warnSeconds", 0); if (!maxTimeStr) return; - double maxTime = atof(maxTimeStr); + +// this is the code that will be run if document.ready ever fires, so most of the page completes +// and the javascript inline <script> code block will be output -> warning is shown as HTML on our page struct dyString *dy = dyStringNew(150); dyStringPrintf(dy, "var warnTimingTimer = setTimeout( function() { hgtWarnTiming(0)}, %f);\n", maxTime); dyStringPrintf(dy, "$(document).ready( function() { clearTimeout(warnTimingTimer); hgtWarnTiming(%f)});\n", maxTime); jsInline(dy->string); dyStringFree(&dy); + +// this is code that will be run if the page never completes and document ready is never run. +// it will show a slightly different, barebones message as a confirm() dialog and will radically hide all tracks +// as a solution +//printf("<script nonce='%s'>\n", getNonce()); +//printf("window.hgMaxTimeWarning = %f;\n", maxTime); +//// this will trigger one second after the other timer and stops itself on document.ready +////puts("var skipNotification = localStorage.getItem('hgTracks.hideSpeedNotification');\n" +// //"if (skipNotification)\n" +// //" return;\n"); +//puts("function hgtWarnTimingMinimal() {\n" +// "msg = 'This page took more than '+window.hgMaxTimeWarning+' seconds to load. We strive to keep" +// " the UCSC Genome Browser quick and responsive. See our display speed FAQ at https://genome.ucsc.edu/FAQ/FAQtracks.html#speed \\n" +// " If this problem continues, you can create a session link My Data > My Sessions and send the link to genome-www@soe.ucsc.edu. " +// " Hit OK to continue, or cancel to never show this message again. All tracks will be hidden, to make the genome browser fast again. ';\n" +// "var isOk = confirm(msg);\n" +// "window.location.search += '&hideTracks=1';\n" +// "}\n"); +// +//printf("var warnTimingTimer = setTimeout( hgtWarnTimingMinimal, %f);\n", (maxTime+1)*1000.0); +//printf( "$(document).ready( function() { clearTimeout(warnTimingTimer);\n});"); +//puts("</script>\n"); } void tracksDisplay() /* Put up main tracks display. This routine handles zooming and * scrolling. */ { +setupTimeWarning(); +fflush(stdout); + char titleVar[256]; char *oldPosition = cartUsualString(cart, "oldPosition", ""); boolean findNearest = cartUsualBoolean(cart, "findNearest", FALSE); cartRemove(cart, "findNearest"); boolean positionIsVirt = FALSE; position = getPositionFromCustomTracks(); if (NULL == position) { position = cartGetPosition(cart, database, &lastDbPosCart); if (sameOk(cgiOptionalString("position"), "lastDbPos")) { restoreSavedVirtPosition(); } if (startsWith(OLD_MULTI_REGION_CHROM, position)) @@ -10685,31 +10783,30 @@ 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(); if (exportedDataHubsEnabled()) printExportedDataHubs(database); -setupTimeWarning(); } static void chromInfoTotalRow(int count, long long total, boolean hasAlias) /* Make table row with total number of sequences and size from chromInfo. */ { cgiSimpleTableRowStart(); cgiSimpleTableFieldStart(); printf("Total: %d", count); cgiTableFieldEnd(); cgiTableFieldStartAlignRight(); printLongWithCommas(stdout, total); puts(" "); cgiTableFieldEnd(); if (hasAlias) { @@ -11456,35 +11553,37 @@ 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("es5-shim.4.0.3.min.js", NULL); jsIncludeFile("es5-sham.4.0.3.min.js", NULL); jsIncludeFile("lodash.3.10.0.compat.min.js", NULL); jsIncludeFile("autocompleteCat.js", NULL); jsIncludeFile("hgTracks.js", NULL); jsIncludeFile("hui.js", NULL); jsIncludeFile("spectrum.min.js", NULL); + jsIncludeFile("selectize.min.js", NULL); #ifdef LOWELAB jsIncludeFile("lowetooltip.js", NULL); #endif///def LOWELAB + webIncludeResourceFile("selectize.default.min.css"); webIncludeResourceFile("spectrum.min.css"); webIncludeResourceFile("jquery-ui.css"); if (enableMouseOver) webIncludeResourceFile("mouseOver.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); if (cfgOptionBooleanDefault("showTutorial", TRUE)) { puts("<script src=\"https://cdn.jsdelivr.net/npm/shepherd.js@11.0.1/dist/js/shepherd.min.js\"></script>");