5f6658a63df1cb265727849cdb061c8bfbe74326
tdreszer
  Thu Dec 16 15:02:36 2010 -0800
Made Track Name and Restricted Until sortable columns in the subtrack list of hgTrackUi.
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 03717a9..2b90d6a 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -2977,79 +2977,134 @@
 members_t*members=subgroupMembersGet(parentTdb, name);
 //const members_t *members = membersFindByTag(parentTdb,name); // Can't use because of dimension dependence
 if(members==NULL)
     return FALSE;
 *value = cloneString(members->groupTitle);
 //subgroupMembersFree(&members);
 return TRUE;
 }
 
 void subgroupFree(char **value)
 /* frees subgroup memory */
 {
 if(value && *value)
     freez(value);
 }
+#define SORT_ON_TRACK_NAME "trackName"
+#define SORT_ON_RESTRICTED "dateUnrestricted"
 
 sortOrder_t *sortOrderGet(struct cart *cart,struct trackDb *parentTdb)
 /* Parses any list sort order instructions for parent of subtracks (from cart or trackDb)
    Some trickiness here.  sortOrder->sortOrder is from cart (changed by user action), as is sortOrder->order,
    But columns are in original tdb order (unchanging)!  However, if cart is null, all is from trackDb.ra */
 {
 int ix;
 char *setting = trackDbSetting(parentTdb, "sortOrder");
 if(setting == NULL) // Must be in trackDb or not a sortable table
     return NULL;
 
 sortOrder_t *sortOrder = needMem(sizeof(sortOrder_t));
 sortOrder->htmlId = needMem(strlen(parentTdb->track)+15);
 safef(sortOrder->htmlId, (strlen(parentTdb->track)+15), "%s.sortOrder", parentTdb->track);
 char *cartSetting = NULL;
 if(cart != NULL)
     cartSetting = cartCgiUsualString(cart, sortOrder->htmlId, setting);
-if(cart != NULL && strlen(cartSetting) == strlen(setting))
+if(cart != NULL && strlen(cartSetting) > strlen(setting)) // If setting is bigger, then it may be due to a trackDb change
     sortOrder->sortOrder = cloneString(cartSetting);  // cart order
 else
     sortOrder->sortOrder = cloneString(setting);      // old cart value is abandoned!
-sortOrder->column  = needMem(12*sizeof(char*)); // There aren't going to be more than 3 or 4!
+
 sortOrder->setting = cloneString(setting);
-sortOrder->count   = chopByWhite(sortOrder->setting, sortOrder->column,12);
+sortOrder->count   = chopByWhite(sortOrder->setting,NULL,0);  // Get size
+#ifdef SORT_ON_TRACK_NAME
+if (cart && !stringIn(SORT_ON_TRACK_NAME,setting))
+    sortOrder->count += 1;
+#endif///def SORT_ON_TRACK_NAME
+#ifdef SORT_ON_RESTRICTED
+if (cart && !stringIn(SORT_ON_RESTRICTED,setting))
+    sortOrder->count += 1;
+#endif///def SORT_ON_RESTRICTED
+sortOrder->column  = needMem(sortOrder->count*sizeof(char*));
+int foundColumns = chopByWhite(sortOrder->setting, sortOrder->column,sortOrder->count);
 sortOrder->title   = needMem(sortOrder->count*sizeof(char*));
 sortOrder->forward = needMem(sortOrder->count*sizeof(boolean));
 sortOrder->order   = needMem(sortOrder->count*sizeof(int));
+#if defined(SORT_ON_TRACK_NAME) || defined(SORT_ON_RESTRICTED)
+if (cart && foundColumns < sortOrder->count)
+    {
+    int columnCount = foundColumns;
+    int size = 0;
+    char *moreOrder = NULL;
+    #ifdef SORT_ON_TRACK_NAME
+    if (cart && columnCount < sortOrder->count && !stringIn(SORT_ON_TRACK_NAME,setting))
+        {
+        assert(sortOrder->column[columnCount] == NULL);
+        sortOrder->column[columnCount] = cloneString(SORT_ON_TRACK_NAME "=+");
+        if (!stringIn(SORT_ON_TRACK_NAME,sortOrder->sortOrder))
+            {
+            size = strlen(sortOrder->sortOrder) + strlen(sortOrder->column[columnCount]) + 5; // little bit more
+            moreOrder = needMem(size);
+            safef(moreOrder,size,"%s %s",sortOrder->sortOrder, sortOrder->column[columnCount]);
+            freeMem(sortOrder->sortOrder);
+            sortOrder->sortOrder = moreOrder;
+            }
+        columnCount++;
+        }
+    #endif///def SORT_ON_TRACK_NAME
+    #ifdef SORT_ON_RESTRICTED
+    if (cart && columnCount < sortOrder->count && !stringIn(SORT_ON_RESTRICTED,setting))
+        {
+        assert(sortOrder->column[columnCount] == NULL);
+        sortOrder->column[columnCount] = cloneString(SORT_ON_RESTRICTED "=+");
+        if (!stringIn(SORT_ON_RESTRICTED,sortOrder->sortOrder))
+            {
+            size = strlen(sortOrder->sortOrder) + strlen(sortOrder->column[columnCount]) + 5; // little bit more
+            moreOrder = needMem(size);
+            safef(moreOrder,size,"%s %s",sortOrder->sortOrder, sortOrder->column[columnCount]);
+            freeMem(sortOrder->sortOrder);
+            sortOrder->sortOrder = moreOrder;
+            }
+        columnCount++;
+        }
+    #endif///def SORT_ON_RESTRICTED
+    }
+#endif///def SORT_ON_TRACK_NAME
 for (ix = 0; ix<sortOrder->count; ix++)
     {
     strSwapChar(sortOrder->column[ix],'=',0);  // Don't want 'CEL=+' but 'CEL' and '+'
     char *pos = stringIn(sortOrder->column[ix], sortOrder->sortOrder);// find tdb substr in cart current order string
     //assert(pos != NULL && pos[strlen(sortOrder->column[ix])] == '=');
     if(pos != NULL && pos[strlen(sortOrder->column[ix])] == '=')
         {
         int ord=1;
         char* pos2 = sortOrder->sortOrder;
         for(;*pos2 && pos2 < pos;pos2++)
             {
             if(*pos2 == '=') // Discovering sort order in cart
                 ord++;
             }
         sortOrder->forward[ix] = (pos[strlen(sortOrder->column[ix]) + 1] == '+');
         sortOrder->order[ix] = ord;
         }
     else  // give up on cartSetting
         {
         sortOrder->forward[ix] = TRUE;
         sortOrder->order[ix] = ix+1;
         }
+#ifdef SORT_ON_TRACK_NAME
+    if (ix < foundColumns)
+#endif///def SORT_ON_TRACK_NAME
     subgroupFindTitle(parentTdb,sortOrder->column[ix],&(sortOrder->title[ix]));
     }
 return sortOrder;  // NOTE cloneString:words[0]==*sortOrder->column[0] and will be freed when sortOrder is freed
 }
 void sortOrderFree(sortOrder_t **sortOrder)
 /* frees any previously obtained sortOrder settings */
 {
 if(sortOrder && *sortOrder)
     {
     int ix;
     for(ix=0;ix<(*sortOrder)->count;ix++) { subgroupFree(&((*sortOrder)->title[ix])); }
     freeMem((*sortOrder)->sortOrder);
     freeMem((*sortOrder)->htmlId);
     freeMem((*sortOrder)->column);
     freeMem((*sortOrder)->forward);
@@ -3793,30 +3848,43 @@
 
 // Look for dividers, heirarchy, dimensions, sort and dragAndDrop!
 char **lastDivide = NULL;
 dividers_t *dividers = dividersSettingGet(parentTdb);
 if (dividers)
     lastDivide = needMem(sizeof(char*)*dividers->count);
 hierarchy_t *hierarchy = hierarchySettingGet(parentTdb);
 
 membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,NULL);
 int dimCount=0,di;
 for(di=0;di<membersForAll->dimMax;di++) { if (membersForAll->members[di]) dimCount++; }
 sortOrder_t* sortOrder = sortOrderGet(cart,parentTdb);
 boolean preSorted = FALSE;
 boolean useDragAndDrop = sameOk("subTracks",trackDbSetting(parentTdb, "dragAndDrop"));
 
+// Determine whether there is a restricted until date column
+boolean restrictions = FALSE;
+for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
+    {
+    subtrack = subtrackRef->val;
+    (void)metadataForTable(db,subtrack,NULL);
+    if (NULL != metadataFindValue(subtrack,"dateUnrestricted"))
+        {
+        restrictions = TRUE;
+        break;
+        }
+    }
+
 // Table wraps around entire list so that "Top" link can float to the correct place.
 printf("<table><tr><td class='windowSize'>");
 printf("<A NAME='DISPLAY_SUBTRACKS'></A>");
 makeTopLink(parentTdb);
 
 // Now we can start in on the table of subtracks  It may be sortable and/or dragAndDroppable
 printf("\n<TABLE CELLSPACING='2' CELLPADDING='0' border='0'");
 dyStringClear(dyHtml);
 if (sortOrder != NULL)
     dyStringPrintf(dyHtml, "sortable");
 if (useDragAndDrop)
     {
     if (dyStringLen(dyHtml) > 0)
         dyStringAppendC(dyHtml,' ');
     dyStringPrintf(dyHtml, "tableWithDragAndDrop");
@@ -3827,77 +3895,110 @@
     colorIx = COLOR_BG_ALTDEFAULT_IX;
     }
 if (sortOrder != NULL)
     puts("><THEAD class=sortable>");
 else
     puts("><THEAD>");
 
 // First table row contains the display "selected/visible" or "all" radio buttons
 boolean displayAll = sameString(cartUsualString(cart, "displaySubtracks", "all"), "all");
 boolean doColorPatch = trackDbSettingOn(parentTdb, "showSubtrackColorOnUi");
 int colspan = 3;
 if (sortOrder != NULL)
     colspan = sortOrder->count+2;
 if (doColorPatch)
     colspan += 1;
+int columnCount = 0;
 printf("<TR%s>",useDragAndDrop?" id='noDrag' class='nodrop nodrag'":"");
 printf("<TD colspan='%d'><B>List subtracks:&nbsp;", colspan);
 char javascript[JBUFSIZE];
 safef(javascript, sizeof(javascript), "onclick=\"showOrHideSelectedSubtracks(true);\"");
 cgiMakeOnClickRadioButton("displaySubtracks", "selected", !displayAll,javascript);
 puts("only selected/visible &nbsp;&nbsp;");
 safef(javascript, sizeof(javascript), "onclick=\"showOrHideSelectedSubtracks(false);\"");
 cgiMakeOnClickRadioButton("displaySubtracks", "all", displayAll,javascript);
 printf("all</B>");
 if (slCount(subtrackRefList) > 5)
     printf("&nbsp;&nbsp;&nbsp;&nbsp;(<FONT class='subCBcount'></font>)");
 puts("</TD>");
+columnCount++;
 
 // Add column headers which are sort button links
 if (sortOrder != NULL)
     {
     puts("<TD colspan=5>&nbsp;</TD></TR>");
     printf("<TR id=\"subtracksHeader\" class='nodrop nodrag sortable'>\n");
-    printf("<TH>&nbsp;<INPUT TYPE=HIDDEN NAME='%s' class='sortOrder' VALUE=\"%s\"></TH>\n", sortOrder->htmlId, sortOrder->sortOrder); // keeing track of sortOrder
+    printf("<TH>&nbsp;<INPUT TYPE=HIDDEN NAME='%s' class='sortOrder' VALUE='%s'></TH>\n", sortOrder->htmlId, sortOrder->sortOrder); // keeing track of sortOrder
     // Columns in tdb order (unchanging), sort in cart order (changed by user action)
     int sIx=0;
     for(sIx=0;sIx<sortOrder->count;sIx++)
         {
-        printf("<TH id='%s' class='sortable%s sort%d' abbr='use' title='Sort list on this column' onclick='tableSortAtButtonPress(this);'>%s",
+#ifdef SORT_ON_TRACK_NAME
+        if (sameString(SORT_ON_TRACK_NAME,sortOrder->column[sIx]))
+            break; // All wrangler requested sort orders have been done.
+#endif///def SORT_ON_TRACK_NAME
+#ifdef SORT_ON_RESTRICTED
+        if (sameString(SORT_ON_RESTRICTED,sortOrder->column[sIx]))
+            break; // All wrangler requested sort orders have been done.
+#endif///def SORT_ON_RESTRICTED
+        printf("<TH id='%s' class='sortable%s sort%d' abbr='use' onclick='tableSortAtButtonPress(this);'>%s",
             sortOrder->column[sIx],(sortOrder->forward[sIx]?"":" sortRev"),sortOrder->order[sIx],sortOrder->title[sIx]);
         printf("<sup>%s",(sortOrder->forward[sIx]?"&darr;":"&uarr;"));
         if (sortOrder->count > 1)
             printf ("%d",sortOrder->order[sIx]);
         printf("</sup>");
         puts ("</TH>");
+        columnCount++;
         }
+
+    // longLabel column
+#ifdef SORT_ON_TRACK_NAME
+    assert(sameString(SORT_ON_TRACK_NAME,sortOrder->column[sIx]));
+    printf("<TH id='%s' class='sortable%s sort%d' onclick='tableSortAtButtonPress(this);' align='left'>&nbsp;&nbsp;Track Name",
+           sortOrder->column[sIx],(sortOrder->forward[sIx]?"":" sortRev"),sortOrder->order[sIx]);
+    printf("<sup>%s%d</sup>",(sortOrder->forward[sIx]?"&darr;":"&uarr;"),sortOrder->order[sIx]);
+    puts ("</TH>");
+#else///ifndef SORT_ON_TRACK_NAME
     puts("<TD>&nbsp;</TD>");
+#endif///ndef SORT_ON_TRACK_NAME
+    columnCount++;
     }
-puts("<TH>&nbsp;</TH>");
+puts("<TH>&nbsp;</TH>"); // schema column
+columnCount++;
 
-// Determine whether there is a restricted until date column
-for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
+// Finally there may be a restricted until column
+if (restrictions)
     {
-    subtrack = subtrackRef->val;
-    (void)metadataForTable(db,subtrack,NULL);
-    if (NULL != metadataFindValue(subtrack,"dateUnrestricted"))
+#ifdef SORT_ON_RESTRICTED
+    if (sortOrder != NULL)
+        {
+        int sIx=sortOrder->count-1;
+        assert(sameString(SORT_ON_RESTRICTED,sortOrder->column[sIx]));
+        printf("<TH id='%s' class='sortable%s sort%d' onclick='tableSortAtButtonPress(this);' align='left'>&nbsp;Restricted Until",
+            sortOrder->column[sIx],(sortOrder->forward[sIx]?"":" sortRev"),sortOrder->order[sIx]);
+        printf("<sup>%s%d</sup>",(sortOrder->forward[sIx]?"&darr;":"&uarr;"),sortOrder->order[sIx]);
+        //printf("<span class='bgLevel1' style='height:100%%;'><A HREF=\'%s\' TARGET=BLANK>&nbsp;?&nbsp;</A></span>", ENCODE_DATA_RELEASE_POLICY);
+        puts ("</TH>");
+        }
+    else
+#endif///def SORT_ON_RESTRICTED
         {
         printf("<TH align='center'>&nbsp;");
         printf("<A HREF=\'%s\' TARGET=BLANK>Restricted Until</A>", ENCODE_DATA_RELEASE_POLICY);
         puts("&nbsp;</TH>");
-        break; // Don't need more than one
         }
+    columnCount++;
     }
 puts("</TR></THEAD>"); // The end of the header section.
 
 // The subtracks need to be sorted by priority but only sortable and dragable will have non-default (cart) priorities to sort on
 if (sortOrder != NULL || useDragAndDrop)
     {
     preSorted = tdbRefSortPrioritiesFromCart(cart, &subtrackRefList); // preserves user's prev sort/drags
     printf("<TBODY class='%saltColors'>\n",(sortOrder != NULL ? "sortable " : "") );
     }
 else
     {
     slSort(&subtrackRefList, trackDbRefCmp);  // straight from trackDb.ra
     preSorted = TRUE;
     puts("<TBODY>");
     }
@@ -4060,37 +4161,48 @@
     makeSchemaLink(db,subtrack,"schema");
     printf("&nbsp;");
 
     // Do we have a restricted until date?
     char *dateDisplay = encodeRestrictionDateDisplay(db,subtrack);
     if (dateDisplay)
         printf("</TD>\n<TD align='center'>&nbsp;%s&nbsp;", dateDisplay);
 
     // End of row and free ourselves of this subtrack
     puts("</TD></TR>\n");
     checkBoxIdFree(&id);
     subgroupMembershipFree(&membership);
     }
 
 // End of the table
-puts("</TBODY><TFOOT></TFOOT>");
-puts("</TABLE>");
-printf("</td></tr></table>");
+puts("</TBODY><TFOOT>");
+printf("<TR valign='top'><TD colspan=%d>",columnCount-1);
 
 // Count of subtracks is filled in by javascript.
+printf("&nbsp;&nbsp;&nbsp;&nbsp;");
 if (slCount(subtrackRefList) > 5)
-    puts("&nbsp;&nbsp;&nbsp;&nbsp;<FONT class='subCBcount'></font>");
+    printf("<span class='subCBcount'></span>\n");
+
+// Restruction policy needs a link
+#ifdef SORT_ON_RESTRICTED
+if (restrictions && sortOrder != NULL)
+    printf("<TH><A HREF='%s' TARGET=BLANK style='font-size:.9em;'>Restriction Policy</A></TH>", ENCODE_DATA_RELEASE_POLICY);
+#endif///def SORT_ON_RESTRICTED
+
+printf("</TD></TR>\n");
+puts("</TFOOT></TABLE>");
+printf("</td></tr></table>");
+
 puts("<P>");
 
 // Tying subtracks with matrix and subtrack cfgs with views requires javascript help
 puts("<script type='text/javascript'>matInitializeMatrix();</script>");
 #ifndef SUBTRACK_CFG_POPUP
 if (dependentCfgsNeedBinding)
     cfgLinkToDependentCfgs(cart,parentTdb,parentTdb->track);
 #endif//ndef SUBTRACK_CFG_POPUP
 
 // Finally we are free of all this
 membersForAllSubGroupsFree(parentTdb,&membersForAll);
 dyStringFree(&dyHtml)
 sortOrderFree(&sortOrder);
 dividersFree(&dividers);
 hierarchyFree(&hierarchy);