4d91bce1e029162895c6acde3100bd375ceee24e
braney
  Tue Mar 10 18:14:44 2026 -0700
Gate new mafFrag MAF click display behind hg.conf mafClickMafFrag setting

The new stitched mafFrag display (species filtering, insertion removal,
space-for-match in diff mode) is now controlled by the hg.conf boolean
mafClickMafFrag. When off (default), the original block-by-block display
is used. Set mafClickMafFrag=on to enable the new behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

diff --git src/hg/hgc/mafClick.c src/hg/hgc/mafClick.c
index 05679b5be12..950171b68d2 100644
--- src/hg/hgc/mafClick.c
+++ src/hg/hgc/mafClick.c
@@ -6,48 +6,54 @@
 #include "common.h"
 #include "hash.h"
 #include "linefile.h"
 #include "hgc.h"
 #include "maf.h"
 #include "obscure.h"
 #include "cheapcgi.h"
 #include "genePred.h"
 #include "botDelay.h"
 #include "hgMaf.h"
 #include "hui.h"
 #include "hCommon.h"
 #include "hubConnect.h"
 #include "trackHub.h"
 #include "chromAlias.h"
+#include "hgConfig.h"
 
 extern boolean issueBotWarning;
 
 #define ADDEXONCAPITAL
 
 /* Javascript to help make a selection from a drop-down
  * go back to the server. */
 static char *autoSubmit = "document.gpForm.submit();";
 
 static void blueCapWrite(FILE *f, char *s, int size, char *r)
 /* Write capital letters in blue. */
 {
 boolean isBlue = FALSE;
 int i;
 for (i=0; i<size; ++i)
     {
     if (r!=NULL && s[i]==r[i])
+	{
+	if (cfgOptionBooleanDefault("mafClickMafFrag", FALSE))
 	    fprintf(f, " ");
+	else
+	    fprintf(f, ".");
+	}
     else
 	{
 	char c = s[i];
 	if (isupper(c))
             {
             if (!isBlue)
                 {
                 fprintf(f, "<span style='color:#0000FF;'>");
                 isBlue = TRUE;
                 }
             }
 	else if (islower(c))
             {
             if (isBlue)
                 {
@@ -531,42 +537,45 @@
 
 static void conservationStatsLink(struct trackDb *tdb,
                                     char *label, char *table)
 /* write link that to display statistics of phastCons table */
 {
 char *chrom = cartCgiUsualString(cart, "c", "chr7");
 printf("<A HREF=\"%s&g=%s&i=%s&c=%s&l=%d&r=%d&o=%d&db=%s"
         "&parentWigMaf=%s\" TARGET=\"_blank\">%s</A>",
         hgcPathAndSettings(), table, table, chrom,
 winStart, winEnd, winStart, database, tdb->track, label);
 }
 
 static void mafOrAxtClick2(struct sqlConnection *conn, struct sqlConnection *conn2, struct trackDb *tdb, char *axtOtherDb, char *fileName)
 /* Display details for MAF or AXT tracks. */
 {
+boolean useMafFrag = cfgOptionBooleanDefault("mafClickMafFrag", FALSE);
 if (issueBotWarning)
     {
     char *ip = getenv("REMOTE_ADDR");
     botDelayMessage(ip, botDelayMillis);
     }
 if (winEnd - winStart > 30000)
     {
     printf("Zoom so that window is 30,000 bases or less to see alignments and conservation statistics\n");
     }
 else
     {
-    struct mafAli *maf = NULL;
+    struct mafAli *mafList = NULL, *maf, *subList = NULL;
+    int aliIx = 0, realCount = 0;
+    char dbChrom[64];
     char option[128];
     char *capTrack;
     struct consWiggle *consWig, *consWiggles;
     struct hash *speciesOffHash = NULL;
     char *speciesOrder = NULL;
     char *speciesTarget = trackDbSetting(tdb, SPECIES_TARGET_VAR);
     char buffer[1024];
     int useTarg = FALSE;
     int useIrowChains = FALSE;
     struct hash *labelHash = mafGetLabelHash(tdb);
     struct slName *orderList = NULL;
 
     safef(option, sizeof(option), "%s.%s", tdb->track, MAF_CHAIN_VAR);
     if (cartCgiUsualBoolean(cart, option, FALSE) &&
 	trackDbSetting(tdb, "irows") != NULL)
@@ -574,31 +583,35 @@
 
     safef(buffer, sizeof(buffer), "%s.vis",tdb->track);
     if (useIrowChains)
 	{
 	if (!cartVarExists(cart, buffer) && (speciesTarget != NULL))
 	    useTarg = TRUE;
 	else
 	    {
 	    char *val;
 
 	    val = cartUsualString(cart, buffer, "useCheck");
             useTarg = sameString("useTarg",val);
             }
         }
 
-    /* Determine species order from trackDb settings, matching hgTracks logic */
+    if (useMafFrag)
+        {
+        /* New mafFrag code path: determine species order from trackDb settings,
+         * matching hgTracks logic, then use hgMafFrag to stitch into a single
+         * continuous alignment in reference coordinates */
         char *speciesGroup = trackDbSetting(tdb, SPECIES_GROUP_VAR);
         char *speciesUseFile = trackDbSetting(tdb, SPECIES_USE_FILE);
         speciesOrder = trackDbSetting(tdb, SPECIES_ORDER_VAR);
 
         /* Check cart override for speciesOrder */
         safef(option, sizeof(option), "%s.speciesOrder", tdb->track);
         char *cartOrder = cartUsualString(cart, option, NULL);
         if (cartOrder != NULL)
             speciesOrder = cartOrder;
 
         if (speciesUseFile)
             speciesOrder = cartGetOrderFromFile(database, cart, speciesUseFile);
 
         /* Build hash of species that default to off */
         char *speciesOff = trackDbSetting(tdb, SPECIES_DEFAULT_OFF_VAR);
@@ -667,41 +680,160 @@
         if (sameString(tdb->type, "bigMaf"))
             {
             char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
             struct bbiFile *bbi = bigBedFileOpenAlias(bigDataUrl, chromAliasFindAliases);
             maf = hgBigMafFrag(database, bbi, seqName, winStart, winEnd, '+', NULL, orderList);
             bbiFileClose(&bbi);
             }
         else if (axtOtherDb == NULL && fileName == NULL)
             {
             /* Regular MAF from database */
             maf = hgMafFrag(database, tdb->table, seqName, winStart, winEnd, '+', NULL, orderList);
             }
         else
             {
             /* AXT or MAF with external file - load blocks, then stitch */
-        struct mafAli *mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName,
+            mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName,
                                         winStart, winEnd, axtOtherDb, fileName);
             maf = hgMafFragFromMafList(database, seqName, winStart, winEnd, '+',
                                         mafList, NULL, orderList);
+            mafList = NULL;  /* consumed by hgMafFragFromMafList */
             }
 
         /* Remove insertion columns (where reference has gaps) */
         if (maf != NULL)
             mafStripRefGaps(maf);
 
         if (maf != NULL)
+            slAddHead(&subList, maf);
+        realCount = (subList != NULL) ? 1 : 0;
+        }
+    else
+        {
+        /* Original block-by-block code path */
+        if (sameString(tdb->type, "bigMaf"))
+            {
+            char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
+            struct bbiFile *bbi = bigBedFileOpenAlias(bigDataUrl, chromAliasFindAliases);
+            mafList = bigMafLoadInRegion(bbi, seqName, winStart, winEnd);
+            }
+        else
+            mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName, winStart, winEnd,
+                                            axtOtherDb, fileName);
+        safef(dbChrom, sizeof(dbChrom), "%s.%s", hubConnectSkipHubPrefix(database), seqName);
+
+        safef(option, sizeof(option), "%s.speciesOrder", tdb->track);
+        speciesOrder = cartUsualString(cart, option, NULL);
+        if (speciesOrder == NULL)
+            speciesOrder = trackDbSetting(tdb, "speciesOrder");
+
+        int speciesCt = 0;
+        char *species[2048];
+        struct mafComp **newOrder;
+        if (speciesOrder)
+            {
+            speciesCt = chopLine(cloneString(speciesOrder), species);
+            newOrder = needMem((speciesCt + 1) * sizeof (struct mafComp *));
+
+            int ii;
+            struct hash *nameHash = newHash(5);
+            for(ii=0; ii < speciesCt; ii++)
+                {
+                if (hashLookup(nameHash, species[ii]))
+                    errAbort("speciesOrder contains %s more than once.", species[ii]);
+                hashStore(nameHash, species[ii]);
+                }
+            }
+
+        for (maf = mafList; maf != NULL; maf = maf->next)
+            {
+            int mcCount = 0;
+            struct mafComp *mc;
+            struct mafAli *subset;
+            struct mafComp *nextMc;
+
+            if (!useTarg)
+                {
+                for (mc = maf->components->next; mc != NULL; mc = nextMc)
+                    {
+                    char buf[64];
+                    char *organism;
+                    mafSrcDb(mc->src, buf, sizeof buf);
+                    organism = hOrganism(buf);
+                    if (!organism)
+                        organism = buf;
+                    nextMc = mc->next;
+                    safef(option, sizeof(option), "%s.%s", tdb->track, buf);
+                    if (!cartUsualBoolean(cart, option, TRUE))
+                        {
+                        if (speciesOffHash == NULL)
+                            speciesOffHash = newHash(4);
+                        hashStoreName(speciesOffHash, organism);
+                        }
+                    if (!cartUsualBoolean(cart, option, TRUE))
+                        slRemoveEl(&maf->components, mc);
+                    else
+                        mcCount++;
+                    }
+                }
+            if (mcCount == 0)
+                continue;
+
+            if (speciesCt)
+                {
+                struct mafComp *mcThis;
+                int i;
+
+                mcCount = 0;
+                speciesCt = chopLine(cloneString(speciesOrder), species);
+                newOrder = needMem((speciesCt + 1) * sizeof (struct mafComp *));
+                newOrder[mcCount++] = maf->components;
+
+                for (i = 0; i < speciesCt; i++)
+                    {
+                    if ((mcThis = mafMayFindCompSpecies(maf, species[i], '.')) == NULL)
+                        continue;
+                    if (mcThis == maf->components)
+                        errAbort("Reference species (%s) shouldn't be in speciesOrder in trackDb", species[i]);
+                    newOrder[mcCount++] = mcThis;
+                    }
+
+                maf->components = NULL;
+                for (i = 0; i < mcCount; i++)
+                    {
+                    newOrder[i]->next = 0;
+                    slAddHead(&maf->components, newOrder[i]);
+                    }
+
+                slReverse(&maf->components);
+                }
+            subset = mafSubsetE(maf, dbChrom, winStart, winEnd, TRUE);
+            if (subset != NULL)
+                {
+                mafMoveComponentToTop(subset, dbChrom);
+                if (subset->components->strand == '-')
+                    mafFlipStrand(subset);
+                subset->score = mafScoreMultiz(subset);
+                slAddHead(&subList, subset);
+                ++realCount;
+                }
+            }
+        slReverse(&subList);
+        mafAliFreeList(&mafList);
+        }
+
+    if (subList != NULL)
 	{
 	char *showVarName = "hgc.showMultiBase";
 	char *showVarVal = cartUsualString(cart, showVarName, "all");
 	boolean onlyDiff = sameWord(showVarVal, "diff");
 #ifdef ADDEXONCAPITAL
 	char *codeVarName = "hgc.multiCapCoding";
 	char *codeVarVal = cartUsualString(cart, codeVarName, "coding");
 	boolean onlyCds = sameWord(codeVarVal, "coding");
 #endif
         /* add links for conservation score statistics */
         consWiggles = wigMafWiggles(database, tdb);
         int wigCount = slCount(consWiggles);
         if (wigCount == 1)
             {
             conservationStatsLink(tdb, "Conservation score statistics", consWiggles->table);
@@ -803,41 +935,49 @@
 	puts("for aligned species, click on 'D' to get DNA for aligned species.<BR>");
 
 	printf("<TT><PRE>");
 
         /* notify if species removed from alignment */
         if (speciesOffHash)
             {
             char *species;
             struct hashCookie hc = hashFirst(speciesOffHash);
             puts("<B>Components not displayed:</B> ");
             while ((species = hashNextName(&hc)) != NULL)
                 printf("%s ", species);
             puts("<BR>");
             }
 
+	for (maf = subList; maf != NULL; maf = maf->next)
+	    {
 	    mafLowerCase(maf);
 #ifdef ADDEXONCAPITAL
 	    if (capTrack != NULL)
                 capMafOnTrack(maf, capTrack, onlyCds);
 #endif
+            if (useMafFrag)
                 printf("<B>Alignment %d - %d, %d bps </B>\n",
                        maf->components->start + 1,
                        maf->components->start + maf->components->size,
                        maf->components->size);
-        mafPrettyOut(stdout, maf, 70, onlyDiff, 1, labelHash);
-	mafAliFree(&maf);
+            else
+                printf("<B>Alignment block %d of %d in window, %d - %d, %d bps </B>\n",
+                       ++aliIx,realCount,maf->components->start + 1,
+                       maf->components->start + maf->components->size, maf->components->size);
+            mafPrettyOut(stdout, maf, 70, onlyDiff, aliIx, labelHash);
+            }
+	mafAliFreeList(&subList);
 	}
     else
 	{
         printf("No multiple alignment in browser window");
 	}
     printf("</PRE></TT>");
     slNameFreeList(&orderList);
     }
 }
 
 static void mafOrAxtClick(struct sqlConnection *conn, struct trackDb *tdb, char *axtOtherDb)
 {
 struct sqlConnection *conn2 = NULL;
 if (!(isHubTrack(tdb->track) || trackHubDatabase(database)))
     conn2 = hAllocConn(database);