b4538b995c8b87f8663861aa9cde4b8c68e83eb1 braney Tue Apr 7 12:52:56 2026 -0700 Fix Get DNA position text box being ignored when table is specified, and accept single-base positions. refs #10316, #15336, #37325 Co-Authored-By: Claude Opus 4.6 (1M context) diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index 4d2d8457048..84614360b20 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -5533,49 +5533,66 @@ { struct trackDb *tdbList = hTrackDb(database); struct trackDb *ctdbList = tdbForCustomTracks(); struct trackDb *utdbList = tdbForUserPsl(); struct grp *pGrpList = NULL; struct trackDb *hubList = hubCollectTracks(database, &pGrpList); ctdbList = slCat(ctdbList, tdbList); ctdbList = slCat(ctdbList, hubList); tdbList = slCat(utdbList, ctdbList); return tdbList; } +static boolean parseDnaPos(char *db, char *pos, char **retChrom, int *retStart, int *retEnd) +/* Parse a position string that may be chrom:start-end or just chrom:pos (single base). */ +{ +if (hgParseChromRange(db, pos, retChrom, retStart, retEnd)) + return TRUE; +/* check for single position like chrX:67774500 (colon but no dash) */ +char *colon = strchr(pos, ':'); +if (colon != NULL && strchr(colon, '-') == NULL) + { + struct dyString *dy = dyStringCreate("%s-%s", pos, colon + 1); + boolean result = hgParseChromRange(db, dy->string, retChrom, retStart, retEnd); + dyStringFree(&dy); + return result; + } +return FALSE; +} + void doGetDnaExtended1() /* Do extended case/color get DNA options. */ { boolean revComp = cartUsualBoolean(cart, "hgSeq.revComp", FALSE); boolean maskRep = cartUsualBoolean(cart, "hgSeq.maskRepeats", FALSE); int padding5 = cartUsualInt(cart, "hgSeq.padding5", 0); int padding3 = cartUsualInt(cart, "hgSeq.padding3", 0); int lineWidth = cartUsualInt(cart, "lineWidth", 60); char *casing = cartUsualString(cart, "hgSeq.casing", ""); char *repMasking = cartUsualString(cart, "hgSeq.repMasking", ""); boolean caseUpper= FALSE; char *pos = NULL; struct trackDb *tdbList = loadTracks(); cartWebStart(cart, database, "Extended DNA Case/Color"); if (NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos")))) - hgParseChromRange(database, pos, &seqName, &winStart, &winEnd); + parseDnaPos(database, pos, &seqName, &winStart, &winEnd); if (winEnd - winStart > 5000000) { printf("Please zoom in to 5 million bases or less to color the DNA"); return; } printf("

Extended DNA Case/Color Options

\n"); puts( "Use this page to highlight features in genomic DNA text. " "DNA covered by a particular track can be highlighted by " "case, underline, bold, italic, or color. See below for " "details about color, and for examples. Tracks in " ""hide" display mode are not shown in the grid below.

"); if (cgiBooleanDefined("hgSeq.maskRepeats")) @@ -5833,54 +5850,58 @@ if (sameString(action, EXTENDED_DNA_BUTTON)) { doGetDnaExtended1(); return; } // This output probably should be just text/plain but // trying to support the fancy warn handler box requires html. // But we want to keep it very simple and close to a plain text dump. cartHtmlStart("DNA"); puts("

");
 if (tbl[0] == 0)
     {
     itemCount = 1;
     if ( NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))) &&
-         hgParseChromRange((dbIsFound ? database : NULL), pos, &chrom, &start, &end))
+         parseDnaPos((dbIsFound ? database : NULL), pos, &chrom, &start, &end))
         {
         hgSeqRange(database, chrom, start, end, '?', "dna");
         }
     else
         {
         hgSeqRange(database, seqName, cartInt(cart, "l"), cartInt(cart, "r"),
                    '?', "dna");
         }
     }
 else
     {
     struct hTableInfo *hti = NULL;
     char rootName[HDB_MAX_TABLE_STRING];
     char parsedChrom[HDB_MAX_CHROM_STRING];
 
     /* use the values from the dnaPos dialog box */
     if (!( NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))) &&
-         hgParseChromRange(database, pos, &chrom, &start, &end)))
+         parseDnaPos(database, pos, &chrom, &start, &end)))
 	 {
 	 /* if can't get DnaPos from dialog box, use "o" and "t" */
 	 start = cartInt(cart, "o");
 	 end = cartInt(cart, "t");
 	 }
+    else
+	 {
+	 seqName = chrom;
+	 }
 
     /* Table might be a custom track if it's not in the database,
      * or bigBed if it is in the database but has only one column called 'fileName';
      * in which case, just get DNA as if no table were given. */
     hParseTableName(database, tbl, rootName, parsedChrom);
     if (!trackHubDatabase(database))
 	hti = hFindTableInfo(database, seqName, rootName);
     if (hti == NULL || hti->startField[0] == 0)
 	{
 	itemCount = 1;
 	hgSeqRange(database, seqName, start, end, '?', tbl);
 	}
     else
 	{
 	char *where = NULL;
@@ -6189,31 +6210,31 @@
 int i;
 boolean isRc = cartUsualBoolean(cart, "hgSeq.revComp", FALSE);
 boolean defaultUpper = sameString(cartString(cart, "hgSeq.casing"), "upper");
 int winSize;
 int lineWidth = cartInt(cart, "lineWidth");
 struct rgbColor *colors;
 
 struct trackDb *tdbList = loadTracks();
 
 char *pos = NULL;
 Bits *uBits;	/* Underline bits. */
 Bits *iBits;    /* Italic bits. */
 Bits *bBits;    /* Bold bits. */
 
 if (NULL != (pos = stripCommas(cartOptionalString(cart, "getDnaPos"))))
-    hgParseChromRange(database, pos, &seqName, &winStart, &winEnd);
+    parseDnaPos(database, pos, &seqName, &winStart, &winEnd);
 
 winSize = winEnd - winStart;
 uBits = bitAlloc(winSize);	/* Underline bits. */
 iBits = bitAlloc(winSize);	/* Italic bits. */
 bBits = bitAlloc(winSize);	/* Bold bits. */
 
 cartWebStart(cart, database, "Extended DNA Output");
 printf("
");
 printf(">%s:%d-%d %s\n", seqName, winStart+1, winEnd,
        (isRc ? "(reverse complement)" : ""));
 seq = hDnaFromSeq(database, seqName, winStart, winEnd, dnaLower);
 if (isRc)
     reverseComplement(seq->dna, seq->size);
 if (defaultUpper)
     touppers(seq->dna);