021aee9e624dac151ccab845b23458c99be10b48
tdreszer
  Tue Aug 10 16:30:51 2010 -0700
Added cartTdbTreeCleanupOverrides to cleanup subtrack settings when composite/view settings change
diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 279140c..3b0aa44 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -763,6 +763,22 @@
 return cart->userInfo->id;
 }
 
+static int cartRemoveAndCount(struct cart *cart, char *var)
+/* Remove variable from cart, returning count of removed vars. */
+{
+int removed = 0;
+struct hashEl *hel = hashLookup(cart->hash, var);
+while (hel != NULL)
+    {
+    struct hashEl *nextHel = hashLookupNext(hel);
+    freez(&hel->val);
+    hashRemove(cart->hash, var);
+    removed++;
+    hel = nextHel;
+    }
+return removed;
+}
+
 void cartRemove(struct cart *cart, char *var)
 /* Remove variable from cart. */
 {
@@ -1923,11 +1939,66 @@
 return (differentString(newValue,oldValue));
 }
 
+struct slRef *cartNamesLike(struct cart *cart, char *wildCard)
+/* Returns reference list of all variable names that match wildCard. */
+{
+struct hashEl *el, *elList = hashElListHash(cart->hash);
+struct slRef *matches = NULL;
+
+for (el = elList; el != NULL; el = el->next)
+    {
+    if (wildMatch(wildCard, el->name))
+        slAddHead(&matches,slRefNew(el->name));
+    }
+hashElFreeList(&elList);
+return matches;
+}
+
+struct slRef *cartNamesPrefixedBy(struct cart *cart, char *prefix)
+/* Returns reference list of all variable names with given prefix. */
+{
+struct hashEl *el, *elList = hashElListHash(cart->hash);
+struct slRef *matches = NULL;
+
+for (el = elList; el != NULL; el = el->next)
+    {
+    if (startsWith(prefix, el->name))
+        slAddHead(&matches,slRefNew(el->name));
+    }
+hashElFreeList(&elList);
+return matches;
+}
+
+int cartNamesPruneChanged(struct cart *newCart,struct hash *oldVars,
+                          struct slRef **cartNames,boolean ignoreRemoved,boolean unChanged)
+/* Prunes a list of cartNames if the settings have changed between new and old cart.
+   Returns pruned count */
+{
+int pruned = 0;
+struct slRef *oldList = *cartNames;
+struct slRef *newList = NULL;
+struct slRef *oneName = NULL;
+while ((oneName = slPopHead(&oldList)) != NULL)
+    {
+    boolean thisOneChanged = cartValueHasChanged(newCart,oldVars,oneName->val,ignoreRemoved);
+    if (unChanged != thisOneChanged)
+        slAddHead(&newList,oneName);
+    else
+        {
+        freeMem(oneName);
+        pruned++;
+        }
+    }
+*cartNames = newList;
+return pruned;
+}
+
 
-void cartRemoveFromTdbTree(struct cart *cart,struct trackDb *tdb,char *suffix,boolean skipParent)
+int cartRemoveFromTdbTree(struct cart *cart,struct trackDb *tdb,char *suffix,boolean skipParent)
 /* Removes a 'trackName.suffix' from all tdb descendents (but not parent).
    If suffix NULL then removes 'trackName' which holds visibility */
 {
+int removed = 0;
 struct slRef *tdbRef, *tdbRefList = trackDbListGetRefsToDescendants(skipParent?tdb->subtracks:tdb);
 for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
     {
@@ -1937,8 +2008,92 @@
         safef(settingName,sizeof(settingName),"%s.%s",descendentTdb->track,suffix);
     else
         safef(settingName,sizeof(settingName),"%s",descendentTdb->track);
-    cartRemove(cart,settingName);
+    removed += cartRemoveAndCount(cart,settingName);
+    }
+return removed;
     }
+
+
+boolean cartTdbTreeCleanupOverrides(struct trackDb *tdb,struct cart *newCart,struct hash *oldVars)
+/* When composite/view settings changes, remove subtrack specific settings
+   Returns TRUE if any cart vars are removed */
+{
+if (!tdbIsComposite(tdb))
+    return FALSE;
+
+// Build list of current settings for composite
+char setting[512];
+safef(setting,sizeof(setting),"%s.",tdb->track);
+struct slRef *changedSettings = cartNamesPrefixedBy(newCart, setting);
+if (changedSettings == NULL)
+    return FALSE;
+
+// Prune list to only those which have changed
+(void)cartNamesPruneChanged(newCart,oldVars,&changedSettings,TRUE,FALSE);
+if (changedSettings == NULL)
+    return FALSE;
+
+struct slRef *oneName = NULL;
+char * var = NULL;
+boolean clensed = FALSE;
+
+// vis is a special additive case! composite or view level changes then remove subtrack vis
+boolean compositeVisChanged = cartValueHasChanged(newCart,oldVars,tdb->track,TRUE);
+
+
+// Walk through views
+boolean hasViews = FALSE;
+struct trackDb *tdbView = tdb->subtracks;
+for (;tdbView != NULL; tdbView = tdbView->next)
+    {
+    boolean viewVisChanged = FALSE;
+    char * view = NULL;
+    if (!tdbIsView(tdbView,&view))
+        break;
+
+    hasViews = TRUE;
+    safef(setting,sizeof(setting),"%s.%s.",tdb->track,view);
+    struct slRef *leftOvers = NULL;
+    // Walk through settings that match this view
+    while ((oneName = slPopHead(&changedSettings)) != NULL)
+        {
+        if(!startsWith(setting,oneName->val))
+            slAddHead(&leftOvers,oneName);
+        else
+            {
+            var = oneName->val + strlen(setting);
+            if (sameString(var,"vis"))
+                viewVisChanged = TRUE;
+            else {
+                if (cartRemoveFromTdbTree(newCart,tdbView,var,TRUE) > 0)
+                    clensed = TRUE;
+            }
+            freeMem(oneName);
+            }
+        }
+    if  (compositeVisChanged || viewVisChanged)
+        { // vis is a special additive case!
+        if (cartRemoveFromTdbTree(newCart,tdbView,NULL,TRUE) > 0)
+            clensed = TRUE;
+        }
+    changedSettings = leftOvers;
+    }
+
+// Now deal with anything remaining at the composite level
+while ((oneName = slPopHead(&changedSettings)) != NULL)
+    {
+    var = oneName->val + strlen(tdb->track) + 1;
+    if(cartRemoveFromTdbTree(newCart,tdb,var,TRUE) > 0)
+        clensed = TRUE;
+    freeMem(oneName);
+    }
+if  (compositeVisChanged || !hasViews)
+    { // vis is a special additive case!
+    if (cartRemoveFromTdbTree(newCart,tdb,NULL,TRUE) > 0)
+        clensed = TRUE;
+    }
+
+return clensed;
 }