318d4af062fce87394472706e0ba1094698a2c96
markd
  Thu Sep 5 22:09:30 2024 -0700
wordwrap track control short lables unless a label has words longer than 20 charters, then character-wrap that label.  Also a slight increates in max-width to accomidates adding small images in the label.   #34398

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 11df09e..b6a3ce0 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -7485,47 +7485,76 @@
 if (img)
     {
     if (wantSmallImage)
         *img = (isOpen ? "../images/remove_sm.gif" : "../images/add_sm.gif");
     else
         *img = (isOpen ? "../images/remove.gif" : "../images/add.gif");
     }
 }
 
 void collapseGroup(char *name, boolean doCollapse)
 /* Set cart variable to cause group to collapse */
 {
 cartSetBoolean(cart, collapseGroupVar(name), doCollapse);
 }
 
-void myControlGridStartCell(struct controlGrid *cg, boolean isOpen, char *id)
+static boolean shouldBreakAll(char *text)
+/* Check if any word in the label exceeds the cell width and should
+ * be broken. */
+{
+/* Must match htdocs/style/HGStyle.css .trackListId between min and max width
+ * it CSS to allow room for different character width calculations in web
+ * browser. */
+static const int MAX_WIDTH_CHARS = 20;
+int wordLen = 0;
+for (char *textPtr = text; *textPtr != '\0'; textPtr++)
+    {
+    if (isspace(*textPtr))
+        {
+        if (wordLen > MAX_WIDTH_CHARS)
+            return TRUE;  // early exit
+        wordLen = 0;
+        }
+    else
+        {
+        wordLen++;
+        }
+    }
+// check gain if there are no spaces.
+return (wordLen > MAX_WIDTH_CHARS);
+}
+
+void myControlGridStartCell(struct controlGrid *cg, boolean isOpen, char *id, boolean breakAll)
 /* Start a new cell in control grid; support Javascript open/collapsing by including id's in tr's.
-   id is used as id prefix (a counter is added to make id's unique). */
+   id is used as id prefix (a counter is added to make id's unique). The breakAll arguments
+   indicates if breaking anywhere to prevent cell overflow is allowed vs only word-break.
+*/
 {
 static int counter = 1;
 if (cg->columnIx == cg->columns)
     controlGridEndRow(cg);
 if (!cg->rowOpen)
     {
     // use counter to ensure unique tr id's (prefix is used to find tr's in javascript).
     printf("<tr %sid='%s-%d'>", isOpen ? "" : "style='display: none' ", id, counter++);
     cg->rowOpen = TRUE;
     }
+char *cls = breakAll ? "trackLabelTd trackLabelTdBreakAll" : "trackLabelTd";
 if (cg->align)
-    printf("<td class='trackLabelTd' align=%s>", cg->align);
+    printf("<td class='%s' align=%s>", cls, cg->align);
 else
-    printf("<td class='trackLabelTd'>");
+    printf("<td class='%s'>", cls);
 }
 
 static void pruneRedundantCartVis(struct track *trackList)
 /* When the config page or track form has been submitted, there usually
  * are many track visibility cart variables that have not been changed
  * from the default.  To keep down cart bloat, prune those out before we
  * save the cart.  changeTrackVis does this too, but this is for the
  * more common case where track visibilities are tweaked. */
 {
 struct track *track;
 for (track = trackList; track != NULL; track = track->next)
     {
     if (track->parent)  // has super track
         pruneRedundantCartVis(track->parent);
         
@@ -9572,61 +9601,63 @@
                     "document.disconnectHubForm.elements['hubId'].value='%s';"
                     "document.disconnectHubForm.submit();return true;",
 		    hubName + strlen(hubTrackPrefix));
 		}
 
             hPrintf("<input type='submit' name='hgt.refresh' value='Refresh' "
                     "title='Update image with your changes'>\n");
             hPrintf("</td></tr></table></th>\n");
             controlGridEndRow(cg);
 
             /* Base Position track goes into map group, which will always exist. */
             if (!showedRuler && sameString(group->name, "map") )
 		{
 		char *url = trackUrl(RULER_TRACK_NAME, chromName);
 		showedRuler = TRUE;
-		myControlGridStartCell(cg, isOpen, group->name);
+		myControlGridStartCell(cg, isOpen, group->name, FALSE);
 		hPrintf("<A HREF=\"%s\">", url);
 		hPrintf(" %s<BR> ", RULER_TRACK_LABEL);
 		hPrintf("</A>");
 		hDropListClassWithStyle("ruler", rulerMenu,
 			sizeof(rulerMenu)/sizeof(char *), rulerMenu[rulerMode],
 			rulerMode == tvHide ? "hiddenText" : "normalText",
 			TV_DROPDOWN_STYLE);
 		controlGridEndCell(cg);
 		freeMem(url);
 		}
 
 	    /* Add supertracks to track list, sort by priority and
 	     * determine if they have visible member tracks */
 	    groupTrackListAddSuper(cart, group, superHash);
 
 	    /* Display track controls */
             if (group->errMessage)
                 {
-		myControlGridStartCell(cg, isOpen, group->name);
+		myControlGridStartCell(cg, isOpen, group->name,
+                                       shouldBreakAll(group->errMessage));
                 hPrintf("%s", group->errMessage);
 		controlGridEndCell(cg);
                 }
 
 	    for (tr = group->trackList; tr != NULL; tr = tr->next)
 		{
 		struct track *track = tr->track;
 		if (tdbIsSuperTrackChild(track->tdb))
 		    /* don't display supertrack members */
 		    continue;
-		myControlGridStartCell(cg, isOpen, group->name);
+		myControlGridStartCell(cg, isOpen, group->name,
+                                       shouldBreakAll(track->shortLabel));
 
                 printTrackLink(track);
 
 		if (hTrackOnChrom(track->tdb, chromName))
 		    {
 		    if (tdbIsSuper(track->tdb))
 			superTrackDropDown(cart, track->tdb,
 					    superTrackHasVisibleMembers(track->tdb));
 		    else
                         {
                         /* check for option of limiting visibility to one mode */
                         hTvDropDownClassVisOnly(track->track, track->visibility,
                                                 rTdbTreeCanPack(track->tdb),
                                                 (track->visibility == tvHide) ? "hiddenText"
                                                                               : "normalText",