05c41aa8b79ce2d8d4b6982350264b8691487a07
braney
  Fri May 22 10:03:04 2026 -0700
hgTracks: flag quickLift mouseOvers that substitute source coords, refs #37615

When a bigBed is rendered via quickLift, $chrom/${chromStart}/${chromEnd} in
the mouseOver pattern still resolve against the source-assembly row, so the
tooltip reports the pre-lift position while the position bar shows the lifted
target position.  When we detect a $chrom-bearing pattern under quickLift,
append "(coordinates from source assembly <db>)" to the tooltip so the
discrepancy is explicit rather than confusing.

diff --git src/hg/hgTracks/bigBedTrack.c src/hg/hgTracks/bigBedTrack.c
index d8126de2dc6..95edcc0f7b5 100644
--- src/hg/hgTracks/bigBedTrack.c
+++ src/hg/hgTracks/bigBedTrack.c
@@ -740,30 +740,45 @@
 // mouseOvers can be built constructed via trackDb settings instead
 // of embedded directly in bigBed
 char *mouseOverPattern = NULL;
 char **fieldNames = NULL;
 if (!mouseOverIdx)
     {
     mouseOverPattern = cartOrTdbString(cart, track->tdb, "mouseOver", NULL);
 
     if (mouseOverPattern)
         {
         AllocArray(fieldNames, bbi->fieldCount);
         struct slName *field = NULL, *fields = bbFieldNames(bbi);
         int i =  0;
         for (field = fields; field != NULL; field = field->next)
             fieldNames[i++] = field->name;
+
+        // Under quickLift, $chrom/${chromStart}/${chromEnd} substitutions resolve
+        // against the source assembly's row, so the tooltip will disagree with
+        // the position bar.  Append a note so the discrepancy isn't surprising.
+        if (quickLiftFile && strstr(mouseOverPattern, "$chrom"))
+            {
+            char *sourceDb = trackDbSetting(track->tdb, "quickLiftDb");
+            struct dyString *dy = dyStringNew(0);
+            dyStringAppend(dy, mouseOverPattern);
+            if (sourceDb)
+                dyStringPrintf(dy, "<br><i>(coordinates from source assembly %s)</i>", sourceDb);
+            else
+                dyStringAppend(dy, "<br><i>(coordinates from source assembly)</i>");
+            mouseOverPattern = dyStringCannibalize(&dy);
+            }
         }
     }
 
 // a fake item that is the union of the items that span the current  window
 struct linkedFeatures *spannedLf = NULL;
 unsigned filtered = 0;
 struct bed *bed = NULL, *bedCopy = NULL;
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     struct linkedFeatures *lf = NULL;
     char *bedRow[bbi->fieldCount];
     if (sameString(track->tdb->type, "bigPsl"))
         {
         // fill out bedRow to support mouseOver pattern replacements
         char startBuf[16], endBuf[16];