d3f4b110a0c7befaff49a3bf40871ec7c88ca565
angie
  Mon Jan 10 16:56:55 2011 -0800
Track #1864 (SNPs 132 (dbSNP)):
1. Added new-in-snp132 columns (exceptions, submitters, alleles) to
hgc.  Wrapped an html table around several parts of the details page
so that they could be made expandable/collapsible using utils.js's
setTableRowVisibility.  Also wrapped some SNP attributes in an html
table for readability.

2. Changed attribute-filter cart variables in hg/lib/snp125Ui.c,
hgTrackUi.c and hgTracks/variation.c to use one checkbox group per
attribute, with the cart variable based on the track name, instead
of one checkbox per attribute value with hardcoded cart var name
that applies to all SNP tracks.  Refactored to remove a lot of
redundant extern global variables.  Still need to make other SNP
control cart vars based on track name.

diff --git src/hg/hgTracks/variation.c src/hg/hgTracks/variation.c
index 8a3e09b..a0854a1 100644
--- src/hg/hgTracks/variation.c
+++ src/hg/hgTracks/variation.c
@@ -1,25 +1,45 @@
 /* variation.c - hgTracks routines that are specific to the tracks in
  * the variation group */
 
 #include "variation.h"
 #include "imageV2.h"
 
 static char const rcsid[] = "$Id: variation.c,v 1.148 2010/06/07 16:54:21 angie Exp $";
 
-struct hash *snp125FuncCartColorHash = NULL;
-struct hash *snp125FuncCartNameHash = NULL;
+static double snp125AvHetCutoff = SNP125_DEFAULT_MIN_AVHET;
+static int snp125WeightCutoff = SNP125_DEFAULT_MAX_WEIGHT;
+
+// Globals for caching cart coloring and filtering settings for snp125+ tracks:
+static char **snp125LocTypeCart = NULL;
+static char **snp125ClassCart = NULL;
+static char **snp125MolTypeCart = NULL;
+static char **snp125ValidCart = NULL;
+static struct hash *snp125FuncCartColorHash = NULL;
+static struct hash *snp125FuncCartNameHash = NULL;
+
+static boolean snp125LocTypeFilterOn = FALSE;
+static boolean snp125ClassFilterOn = FALSE;
+static boolean snp125MolTypeFilterOn = FALSE;
+static boolean snp125ValidFilterOn = FALSE;
+static boolean snp125FuncFilterOn = FALSE;
+
+static struct slName *snp125LocTypeFilter = NULL;
+static struct slName *snp125ClassFilter = NULL;
+static struct slName *snp125MolTypeFilter = NULL;
+static struct slName *snp125ValidFilter = NULL;
+static struct slName *snp125FuncFilter = NULL;
 
 void filterSnpMapItems(struct track *tg, boolean (*filter)
 		       (struct track *tg, void *item))
 /* Filter out items from track->itemList. */
 {
 filterSnpItems(tg, filter);
 }
 
 void filterSnpItems(struct track *tg, boolean (*filter)
 		    (struct track *tg, void *item))
 /* Filter out items from track->itemList. */
 {
 struct slList *newList = NULL, *el, *next;
 
 for (el = tg->items; el != NULL; el = next)
@@ -61,208 +81,200 @@
 boolean snpAvHetFilterItem(struct track *tg, void *item)
 /* Return TRUE if item passes filter. */
 {
 struct snp *el = item;
 
 if (el->avHet < atof(cartUsualString(cart, "snpAvHetCutoff", "0.0")))
     return FALSE;
 return TRUE;
 }
 
 boolean snp125AvHetFilterItem(void *item)
 /* Return TRUE if item passes filter. */
 {
 struct snp125 *el = item;
 
-if (el->avHet < atof(cartUsualString(cart, "snp125AvHetCutoff", "0.0")))
+if (el->avHet < snp125AvHetCutoff)
     return FALSE;
 return TRUE;
 }
 
 boolean snp125WeightFilterItem(void *item)
 /* Return TRUE if item passes filter. */
 {
 struct snp125 *el = item;
 
-if (el->weight > atoi(cartUsualString(cart, "snp125WeightCutoff", "3")))
+if (el->weight > snp125WeightCutoff)
     return FALSE;
 return TRUE;
 }
 
 boolean snpSourceFilterItem(struct track *tg, void *item)
 /* Return TRUE if item passes filter, i.e. has no excluded property. */
 {
 struct snp *el = item;
 int    snpSource = 0;
 
 for (snpSource=0; snpSource<snpSourceCartSize; snpSource++)
     if (containsStringNoCase(el->source,snpSourceDataName[snpSource]))
  	if (sameString(snpSourceCart[snpSource], "exclude") )
  	    return FALSE;
 return TRUE;
 }
 
 boolean snpMolTypeFilterItem(struct track *tg, void *item)
 /* Return TRUE if item passes filter, i.e. has no excluded property. */
 {
 struct snp *el = item;
 int    snpMolType = 0;
 
 for (snpMolType=0; snpMolType<snpMolTypeCartSize; snpMolType++)
     if (containsStringNoCase(el->molType,snpMolTypeDataName[snpMolType]))
  	if ( sameString(snpMolTypeCart[snpMolType], "exclude") )
  	    return FALSE;
 return TRUE;
 }
 
-boolean snp125MolTypeFilterItem(void *item)
+static boolean snp125MolTypeFilterItem(void *item)
 /* Return TRUE if item passes filter, i.e. has an included property. */
 {
-struct snp125 *el = item;
-int i;
-
-for (i=0; i<snp125MolTypeLabelsSize; i++)
-    {
-    if (!sameString(snp125MolTypeDataName[i], el->molType))
-	continue;
-    if (snp125MolTypeIncludeCart[i])
+struct snp125 *el = (struct snp125 *)item;
+if (! snp125MolTypeFilterOn)
 	return TRUE;
-    }
-return FALSE;
+else
+    return slNameInList(snp125MolTypeFilter, el->molType);
 }
 
 boolean snpClassFilterItem(struct track *tg, void *item)
 /* Return TRUE if item passes filter, i.e. has no excluded property. */
 {
 struct snp *el = item;
 int    snpClass = 0;
 
 for (snpClass=0; snpClass<snpClassCartSize; snpClass++)
     if (containsStringNoCase(el->class,snpClassDataName[snpClass]))
  	if ( sameString(snpClassCart[snpClass], "exclude") )
  	    return FALSE;
 return TRUE;
 }
 
-boolean snp125ClassFilterItem(void *item)
+static boolean snp125ClassFilterItem(void *item)
 /* Return TRUE if item passes filter, i.e. has an included property. */
 {
-struct snp125 *el = item;
-int i;
-
-for (i=0; i<snp125ClassLabelsSize; i++)
-    {
-    if (!sameString(snp125ClassDataName[i], el->class))
-	continue;
-    if (snp125ClassIncludeCart[i])
+struct snp125 *el = (struct snp125 *)item;
+if (! snp125ClassFilterOn)
 	return TRUE;
-    }
-return FALSE;
+else
+    return slNameInList(snp125ClassFilter, el->class);
 }
 
 boolean snpValidFilterItem(struct track *tg, void *item)
 /* Return TRUE if item passes filter, i.e. has no excluded property. */
 {
 struct snp *el = item;
 int    snpValid = 0;
 
 for (snpValid=0; snpValid<snpValidCartSize; snpValid++)
     if (containsStringNoCase(el->valid,snpValidDataName[snpValid]))
  	if ( sameString(snpValidCart[snpValid], "exclude") )
  	    return FALSE;
 return TRUE;
 }
 
-boolean snp125ValidFilterItem(void *item)
+static boolean snp125ValidFilterItem(void *item)
 /* Return TRUE if item passes filter, i.e. has an included property. */
 {
-struct snp125 *el = item;
-int i;
-
-for (i=0; i<snp125ValidLabelsSize; i++)
+struct snp125 *el = (struct snp125 *)item;
+if (! snp125ValidFilterOn)
+    return TRUE;
+else
     {
-    if (!containsStringNoCase(el->valid, snp125ValidDataName[i]))
-	continue;
-    if (snp125ValidIncludeCart[i])
+    char *s = el->valid, *e;
+    char val[256]; // Longest validation code is much shorter than this
+    while (s != NULL && s[0] != 0)
+	{
+	e = strchr(s, ',');
+	if (e == NULL && slNameInList(snp125ValidFilter, s))
+	    return TRUE;
+	else
+	    {
+	    safencpy(val, sizeof(val), s, e-s);
+	    if (slNameInList(snp125ValidFilter, val))
 	return TRUE;
+	    e += 1;
+	    }
+	s = e;
     }
 return FALSE;
 }
+}
 
 boolean snpFuncFilterItem(struct track *tg, void *item)
 /* Return TRUE if item passes filter, i.e. has no excluded property. */
 {
 struct snp *el = item;
 int    snpFunc = 0;
 
 for (snpFunc=0; snpFunc<snpFuncCartSize; snpFunc++)
     if (containsStringNoCase(el->func,snpFuncDataName[snpFunc]))
  	if ( sameString(snpFuncCart[snpFunc], "exclude") )
  	    return FALSE;
 return TRUE;
 }
 
-boolean snp125FuncFilterItem(void *item)
+static boolean snp125FuncFilterItem(void *item)
 /* Return TRUE if item passes filter, i.e. has an included property. */
 {
-struct snp125 *el = item;
+struct snp125 *el = (struct snp125 *)item;
+if (!snp125FuncFilterOn)
+    return TRUE;
 char *words[128];
 int wordCount, i;
 char funcString[4096];
 safecpy(funcString, sizeof(funcString), el->func);
 wordCount = chopCommas(funcString, words);
 for (i = 0;  i < wordCount;  i++)
     {
     char *simpleFunc = (char *)hashMustFindVal(snp125FuncCartNameHash,
 					       words[i]);
-    int snpFunc = stringArrayIx(simpleFunc,
-				snp125FuncDataName, snp125FuncDataNameSize);
-    if (snpFunc < 0)
-	errAbort("Unrecognized function %s", simpleFunc);
-    if (snp125FuncIncludeCart[snpFunc])
+    if (slNameInList(snp125FuncFilter, simpleFunc))
 	return TRUE;
     }
 return FALSE;
 }
 
 boolean snpLocTypeFilterItem(struct track *tg, void *item)
 /* Return TRUE if item passes filter, i.e. has no excluded property. */
 {
 struct snp *el = item;
 int    snpLocType = 0;
 
 for (snpLocType=0; snpLocType<snpLocTypeCartSize; snpLocType++)
     if (containsStringNoCase(el->locType,snpLocTypeDataName[snpLocType]))
  	if ( sameString(snpLocTypeCart[snpLocType], "exclude") )
  	    return FALSE;
 return TRUE;
 }
 
-boolean snp125LocTypeFilterItem(void *item)
+static boolean snp125LocTypeFilterItem(void *item)
 /* Return TRUE if item passes filter, i.e. has an included property. */
 {
-struct snp125 *el = item;
-int i;
-
-for (i=0; i<snp125LocTypeLabelsSize; i++)
-    {
-    if (!sameString(snp125LocTypeDataName[i], el->locType))
-	continue;
-    if (snp125LocTypeIncludeCart[i])
+struct snp125 *el = (struct snp125 *)item;
+if (! snp125LocTypeFilterOn)
 	return TRUE;
-    }
-return FALSE;
+else
+    return slNameInList(snp125LocTypeFilter, el->locType);
 }
 
 void filterSnp125Items(struct track *tg, int version)
 /* Filter out items from track->itemList using snp125 filters. */
 {
 struct slList *newList = NULL, *el, *next;
 
 for (el = tg->items; el != NULL; el = next)
     {
     next = el->next;
     if (snp125AvHetFilterItem(el) &&
 	snp125WeightFilterItem(el) &&
 	snp125MolTypeFilterItem(el) &&
 	snp125ClassFilterItem(el) &&
 	snp125ValidFilterItem(el) &&
@@ -440,84 +452,82 @@
 void loadSnp125Extended(struct track *tg)
 /* load snps from snp125 table, ortho alleles from snpXXXortho table,
  * and return in extended struct */
 {
 struct sqlConnection   *conn      = hAllocConn(database);
 int                     rowOffset = 0;
 char                  **row       = NULL;
 struct slList          *itemList  = tg->items;
 struct slList          *item      = itemList;
 struct sqlResult       *sr        = NULL;
 struct snp125Extended  *se        = NULL;
 enum   trackVisibility  visLim    = limitVisibility(tg);
 int                     version   = snpVersion(tg->table);
 int                     i         = 0;
 
-snp125AvHetCutoff = atof(cartUsualString(cart, "snp125AvHetCutoff", "0.0"));
-snp125WeightCutoff = atoi(cartUsualString(cart, "snp125WeightCutoff", "3"));
+snp125AvHetCutoff = cartUsualDouble(cart, "snp125AvHetCutoff", SNP125_DEFAULT_MIN_AVHET);
+snp125WeightCutoff = cartUsualInt(cart, "snp125WeightCutoff", SNP125_DEFAULT_MAX_WEIGHT);
 snp125ExtendedNames = cartUsualBoolean(cart, "snp125ExtendedNames", FALSE);
 
-for (i=0; i < snp125MolTypeCartSize; i++)
-    {
+char *track = tg->tdb->track;
+snp125MolTypeFilter = snp125FilterFromCart(cart, track, "molType", &snp125MolTypeFilterOn);
+snp125ClassFilter = snp125FilterFromCart(cart, track, "class", &snp125ClassFilterOn);
+snp125ValidFilter = snp125FilterFromCart(cart, track, "valid", &snp125ValidFilterOn);
+snp125FuncFilter = snp125FilterFromCart(cart, track, "func", &snp125FuncFilterOn);
+snp125LocTypeFilter = snp125FilterFromCart(cart, track, "locType", &snp125LocTypeFilterOn);
+
+AllocArray(snp125MolTypeCart, snp125MolTypeArraySize);
+for (i=0; i < snp125MolTypeArraySize; i++)
     snp125MolTypeCart[i] = cartUsualString(cart, snp125MolTypeStrings[i], snp125MolTypeDefault[i]);
-    snp125MolTypeIncludeCart[i] = cartUsualBoolean(cart, snp125MolTypeIncludeStrings[i], snp125MolTypeIncludeDefault[i]);
-    }
-for (i=0; i < snp125ClassCartSize; i++)
-    {
+AllocArray(snp125ClassCart, snp125ClassArraySize);
+for (i=0; i < snp125ClassArraySize; i++)
     snp125ClassCart[i] = cartUsualString(cart, snp125ClassStrings[i], snp125ClassDefault[i]);
-    snp125ClassIncludeCart[i] = cartUsualBoolean(cart, snp125ClassIncludeStrings[i], snp125ClassIncludeDefault[i]);
-    }
-for (i=0; i < snp125ValidCartSize; i++)
-    {
+AllocArray(snp125ValidCart, snp125ValidArraySize);
+for (i=0; i < snp125ValidArraySize; i++)
     snp125ValidCart[i] = cartUsualString(cart, snp125ValidStrings[i], snp125ValidDefault[i]);
-    snp125ValidIncludeCart[i] = cartUsualBoolean(cart, snp125ValidIncludeStrings[i], snp125ValidIncludeDefault[i]);
-    }
+AllocArray(snp125LocTypeCart, snp125LocTypeArraySize);
+for (i=0; i < snp125LocTypeArraySize; i++)
+    snp125LocTypeCart[i] = cartUsualString(cart, snp125LocTypeStrings[i], snp125LocTypeDefault[i]);
+
 snp125FuncCartColorHash = hashNew(0);
 snp125FuncCartNameHash = hashNew(0);
-for (i=0; i < snp125FuncCartSize; i++)
+for (i=0; i < snp125FuncArraySize; i++)
     {
-    snp125FuncCart[i] = cartUsualString(cart, snp125FuncStrings[i], snp125FuncDefault[i]);
+    char *cartVal = cartUsualString(cart, snp125FuncStrings[i], snp125FuncDefault[i]);
     /* There are many function types, some of which are mapped onto
      * simpler types in snp125Ui.c.  First store the indexes of
      * selected colors of simpler types that we present as coloring
      * choices; then (below) map the more detailed function types'
      * indexes onto the simpler types' indexes. */
     hashAddInt(snp125FuncCartColorHash, snp125FuncDataName[i],
-	       stringArrayIx(snp125FuncCart[i],
-			     snp125ColorLabel, snp125ColorLabelSize));
+	       stringArrayIx(cartVal, snp125ColorLabel, snp125ColorArraySize));
     /* Similarly, map names.  Self-mapping here, synonyms below. */
     hashAdd(snp125FuncCartNameHash, snp125FuncDataName[i],
 	    snp125FuncDataName[i]);
-    snp125FuncIncludeCart[i] = cartUsualBoolean(cart, snp125FuncIncludeStrings[i], snp125FuncIncludeDefault[i]);
     }
 int j, k;
 for (j = 0;  snp125FuncDataSynonyms[j] != NULL;  j++)
     {
     for (k = 1;  snp125FuncDataSynonyms[j][k] != NULL;  k++)
 	{
 	hashAddInt(snp125FuncCartColorHash, snp125FuncDataSynonyms[j][k],
 		   hashIntVal(snp125FuncCartColorHash,
 			      snp125FuncDataSynonyms[j][0]));
 	hashAdd(snp125FuncCartNameHash, snp125FuncDataSynonyms[j][k],
 		snp125FuncDataSynonyms[j][0]);
 	}
     }
-for (i=0; i < snp125LocTypeCartSize; i++)
-    {
-    snp125LocTypeCart[i] = cartUsualString(cart, snp125LocTypeStrings[i], snp125LocTypeDefault[i]);
-    snp125LocTypeIncludeCart[i] = cartUsualBoolean(cart, snp125LocTypeIncludeStrings[i], snp125LocTypeIncludeDefault[i]);
-    }
 
 /* load SNPs */
 sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset);
 
 if(differentString(tg->table,"snp") && differentString(tg->table,"snpMap"))
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	/* use loader for snp125 table format */
 	item = (struct slList *)snp125ExtendedLoad(row + rowOffset);
 	se = (struct snp125Extended *)item;
 	se->color = snp125Color(tg, se, NULL);
 	slAddHead(&itemList, item);
 	}
 else
     while ((row = sqlNextRow(sr)) != NULL)
@@ -670,82 +680,84 @@
     case snp125ColorGray:
         return MG_GRAY;
 	break;
     case snp125ColorBlack:
     default:
  	return MG_BLACK;
  	break;
     }
 }
 
 Color snp125Color(struct track *tg, void *item, struct hvGfx *hvg)
 /* Return color of snp track item. */
 {
 struct snp125 *el = item;
 enum   snp125ColorEnum thisSnpColor = snp125ColorBlack;
-char  *snpColorSource = cartUsualString(cart, snp125ColorSourceDataName[0], snp125ColorSourceDefault[0]);
+char  *snpColorSource = cartUsualString(cart, snp125ColorSourceVarName, snp125ColorSourceDefault);
 int    snpValid = 0;
 int    index1 = 0;
 int    index2 = 0;
 
 index1 = stringArrayIx(snpColorSource,
-		       snp125ColorSourceLabels, snp125ColorSourceLabelsSize);
+		       snp125ColorSourceLabels, snp125ColorSourceArraySize);
 switch (index1)
     {
     case snp125ColorSourceMolType:
-	index2 = stringArrayIx(el->molType,snp125MolTypeDataName,snp125MolTypeDataNameSize);
+//*** seems like we should precompute a mapping directly from el->molType to color 
+//*** (the second stringArrayIx into snp125MolTypeCart shouldn't be necessary)
+	index2 = stringArrayIx(el->molType,snp125MolTypeDataName,snp125MolTypeArraySize);
 	if (index2 < 0)
 	    index2 = 0;
-	thisSnpColor=(enum snp125ColorEnum)stringArrayIx(snp125MolTypeCart[index2],snp125ColorLabel,snp125ColorLabelSize);
+	thisSnpColor=(enum snp125ColorEnum)stringArrayIx(snp125MolTypeCart[index2],snp125ColorLabel,snp125ColorArraySize);
 	break;
     case snp125ColorSourceClass:
-	index2 = stringArrayIx(el->class,snp125ClassDataName,snp125ClassDataNameSize);
+	index2 = stringArrayIx(el->class,snp125ClassDataName,snp125ClassArraySize);
 	if (index2 < 0)
 	    index2 = 0;
-	thisSnpColor=(enum snp125ColorEnum)stringArrayIx(snp125ClassCart[index2],snp125ColorLabel,snp125ColorLabelSize);
+	thisSnpColor=(enum snp125ColorEnum)stringArrayIx(snp125ClassCart[index2],snp125ColorLabel,snp125ColorArraySize);
 	break;
 	/* valid is a set */
     case snp125ColorSourceValid:
-	for (snpValid=0; snpValid<snp125ValidCartSize; snpValid++)
+	for (snpValid=0; snpValid<snp125ValidArraySize; snpValid++)
 	    if (containsStringNoCase(el->valid, snp125ValidDataName[snpValid]))
-		thisSnpColor = (enum snp125ColorEnum) stringArrayIx(snp125ValidCart[snpValid],snp125ColorLabel,snp125ColorLabelSize);
+		thisSnpColor = (enum snp125ColorEnum) stringArrayIx(snp125ValidCart[snpValid],snp125ColorLabel,snp125ColorArraySize);
 	break;
 	/* func is a set */
     case snp125ColorSourceFunc:
 	{
 	char *words[128];
 	int wordCount, i;
 	char funcString[4096];
 	safecpy(funcString, sizeof(funcString), el->func);
 	wordCount = chopCommas(funcString, words);
 	for (i = 0;  i < wordCount;  i++)
 	    {
 	    enum snp125ColorEnum wordColor = (enum snp125ColorEnum)
 		hashIntVal(snp125FuncCartColorHash, words[i]);
 	    /* This sorting function is a reverse-sort, so use it backwards: */
 	    if (snp125ExtendedColorCmpRaw(
 			snp125ColorToMg(wordColor), "wordColor",
 			snp125ColorToMg(thisSnpColor), "thisSnpColor") > 0)
 		thisSnpColor = wordColor;
 	    }
 	}
 	break;
     case snp125ColorSourceLocType:
-	index2 = stringArrayIx(el->locType,snp125LocTypeDataName,snp125LocTypeDataNameSize);
+	index2 = stringArrayIx(el->locType,snp125LocTypeDataName,snp125LocTypeArraySize);
 	if (index2 < 0)
 	    index2 = 0;
-	thisSnpColor=(enum snp125ColorEnum)stringArrayIx(snp125LocTypeCart[index2],snp125ColorLabel,snp125ColorLabelSize);
+	thisSnpColor=(enum snp125ColorEnum)stringArrayIx(snp125LocTypeCart[index2],snp125ColorLabel,snp125ColorArraySize);
 	break;
     default:
 	thisSnpColor = snp125ColorBlack;
 	break;
     }
 return snp125ColorToMg(thisSnpColor);
 }
 
 Color snpColor(struct track *tg, void *item, struct hvGfx *hvg)
 /* Return color of snp track item. */
 {
 struct snp *el = item;
 enum   snpColorEnum thisSnpColor = snpColorBlack;
 char  *snpColorSource = cartUsualString(cart, snpColorSourceDataName[0], snpColorSourceDefault[0]);
 char  *validString = NULL;