1028183b63771ec4f9f23fdc4edeabf649ff14c4
kate
  Wed Oct 10 14:01:31 2018 -0700
Finish implementation of split labels in pack mode. refs #21917

diff --git src/hg/hgTracks/interactTrack.c src/hg/hgTracks/interactTrack.c
index beed63d..ecf21f5 100644
--- src/hg/hgTracks/interactTrack.c
+++ src/hg/hgTracks/interactTrack.c
@@ -100,49 +100,79 @@
             {
             if (!(sOnScreen || tOnScreen))
                 continue;
             }
         }
     slAddHead(&filteredItems, inter);
     }
 
 slReverse(&filteredItems);
 // consider sorting by score/value so highest scored items draw last (on top)
 if (slCount(filteredItems) != count)
     labelTrackAsFiltered(tg);
 tg->items = filteredItems;
 }
 
-void interactDrawLeftLabels(struct track *tg, int seqStart, int seqEnd,
+void interactNoLeftLabels(struct track *tg, int seqStart, int seqEnd,
     struct hvGfx *hvg, int xOff, int yOff, int width, int height,
     boolean withCenterLabels, MgFont *font,
     Color color, enum trackVisibility vis)
 /* Override default */
 {
 }
 
+/* Interaction displayed as a linked feature */
+
+struct interactLfEndNames
+/* Stash source and target names when converted to linked feature */
+    {
+    char *source;
+    char *target;
+    };
+
+static void setInteractLfEndNames(struct linkedFeatures *lf, char *sourceName, char *targetName)
+/* Stash endpoint names in linked feature */
+{
+struct interactLfEndNames *ends = NULL;
+AllocVar(ends);
+ends->source = sourceName;
+ends->target = targetName;
+lf->original = ends;    // coopt this void * field (would have preferred to use extra)
+}
+
+static struct interactLfEndNames *getInteractLfEndNames(struct linkedFeatures *lf)
+/* Retrieve endpoint names from linked feature */
+{
+return (struct interactLfEndNames *)lf->original;
+}
+
 void interactFreeItems(struct track *tg)
 /* Free up interact items track */
 {
 interactFreeList((struct interact **)(&tg->items));
 }
 
 static struct linkedFeatures *interactToLf(struct interact *inter, boolean doColor)
 /* Convert interact BED to linkedFeatures */
 {
 struct bed *bed = interactToBed(inter);
 struct linkedFeatures *lf = lfFromBed(bed);
+
+// save source and target names to extra field of linked feature, so we can display them in pack mode
+// TODO: code to free
+setInteractLfEndNames(lf, cloneString(inter->sourceName), cloneString(inter->targetName));
+
 // not sure why this is needed -- lfFromBed seems to reorder blocks, sometimes ?
 linkedFeaturesSortAndBound(lf);
 if (doColor)
     {
     lf->extra = (void *)USE_ITEM_RGB;   /* signal for coloring */
     lf->filterColor = bed->itemRgb;
     }
 bedFree(&bed);
 // TODO: use lfFromBedExtra with scoreMin, scoreMax ?
 return lf;
 }
 
 static boolean isLinkedFeaturesMode(struct track *tg)
 /* Determine if linked feature display will be used */
 {
@@ -235,67 +265,98 @@
         if (labelStart <= prevLabelEnd && 
                 !(labelStart == prevLabelStart && labelEnd == prevLabelEnd && 
                     sameString(otherChrom, prevLabel)))
             tInfo->doOtherLabels = FALSE;
         prevLabelStart = labelStart;
         prevLabelEnd = labelEnd;
         prevLabel = otherChrom;
         }
     }
 tInfo->fontHeight = vgGetFontPixelHeight(hvg->vg, font);
 tInfo->otherHeight = (tInfo->otherCount) ? 3 * tInfo->fontHeight : 0;
 tInfo->sameHeight = (tInfo->sameCount) ? tg->height - tInfo->otherHeight : 0;
 }
 
 static int interactRightPixels(struct track *tg, void *item)
-/* Return number of pixels we need to the right. */
+/* Return number of pixels we need to the right, in linked features mode. */
 {
-struct interact *inter = item;
 struct interactTrackInfo *tInfo = tg->customPt;
-if ((tg->visibility == tvPack || tg->visibility == tvFull) && !tInfo->clusterMode)
-    {
-    char *rightLabel = "";
-    if (tInfo->isDirectional)
+if (tInfo->clusterMode || !(tg->visibility == tvPack || tg->visibility == tvFull))
+    return 0;
+struct linkedFeatures *lf = (struct linkedFeatures *)item;
+struct interactLfEndNames *ends = getInteractLfEndNames(lf);
+char *rightLabel = ends->target;
+if (lf->orientation != 0)
+    rightLabel = (lf->orientation < 0 ? ends->source : ends->target);
+return mgFontStringWidth(tl.font, rightLabel);
+}
+
+static char *interactLfLeftEndName(struct track *tg, void *item)
+/* Return name of left end in single interaction linked feature, for left label */
 {
-        if (inter->targetStart == inter->chromStart)
-            rightLabel = inter->sourceName;
-        if (inter->targetEnd == inter->chromEnd)
-            rightLabel = inter->targetName;
+struct linkedFeatures *lf = (struct linkedFeatures *)item;
+struct interactLfEndNames *ends = getInteractLfEndNames(lf);
+return (lf->orientation < 0 ? ends->target : ends->source);
 }
-    else
-        rightLabel = inter->targetName;
-    return mgFontStringWidth(tl.font, rightLabel);
+
+#ifdef FOO
+static char *interactLfItemName(struct track *tg, void *item)
+/* Return item name of interaction linked feature, for map */
+{
+struct linkedFeatures *lf = (struct linkedFeatures *)item;
+return lf->name;
 }
-return 0;
+#endif
+
+static void interactLfMapItem(struct track *tg, struct hvGfx *hvg, void *item,
+                                char *itemName, char *mapItemName, int start, int end,
+                                int x, int y, int width, int height)
+/* Draw mapbox on interact item when displayed as linked feature */
+{
+struct linkedFeatures *lf = (struct linkedFeatures *)item;
+// restore full item name (using end name for label)
+itemName = mapItemName = lf->name;
+linkedFeaturesMapItem(tg, hvg, item, itemName, mapItemName, start, end, x, y, width, height);
 }
 
 void interactLoadItems(struct track *tg)
 /* Load interact items in interact format */
 {
 loadAndFilterItems(tg);
 
 struct interactTrackInfo *tInfo = NULL;
 AllocVar(tInfo);
 tg->customPt = tInfo;
 tInfo->isDirectional = interactUiDirectional(tg->tdb);
 tInfo->offset = interactUiOffset(tg->tdb);
 tInfo->drawUp = trackDbSettingClosestToHomeOn(tg->tdb, INTERACT_UP);
 tInfo->clusterMode = interactUiClusterMode(cart, tg->track, tg->tdb);
 
-if (tInfo->clusterMode || isLinkedFeaturesMode(tg))
+if (!tInfo->clusterMode && !isLinkedFeaturesMode(tg))
     {
+    // draw curve display
+    tg->mapsSelf = TRUE;
+    tg->totalHeight = interactTotalHeight;
+    tg->drawLeftLabels = interactNoLeftLabels;
+    tg->freeItems = interactFreeItems;
+    return;
+    }
+
 // convert to BEDs for linked feature display
+tg->itemName = interactLfLeftEndName;
+tg->mapItem = interactLfMapItem;
+//tg->mapItemName = interactLfItemName;
 struct interact *inters = tg->items, *inter;
 struct linkedFeatures *lfs = NULL, *lf;
 struct hash *intersCluster = hashNew(0);
 boolean doColor = !tg->colorShades;
 for (inter = inters; inter; inter = inter->next)
     {
     if (tInfo->clusterMode)
         {
         boolean byTarget = sameString(tInfo->clusterMode, INTERACT_CLUSTER_TARGET);
         // hash by source or target name
         char *name = (byTarget ? inter->targetName : inter->sourceName);
         lf = (struct linkedFeatures *) hashFindVal(intersCluster, name);
         if (lf)
             {
             // add a simple feature for the other end (source or target) to the linked feature
@@ -351,38 +412,30 @@
     for (el = els; el; el = el->next)
         {
         lf = (struct linkedFeatures *)el->val;
         linkedFeaturesSortAndBound(lf);
         slAddHead(&lfs, lf);
         }
     slSort(&lfs, linkedFeaturesCmp);
     }
 else
     {
     slReverse(&lfs);
     }
 tg->items = lfs;
 // TODO: consider freeing interact items
 }
-else
-    {
-    tg->mapsSelf = TRUE;
-    tg->totalHeight = interactTotalHeight;
-    tg->drawLeftLabels = interactDrawLeftLabels;
-    tg->freeItems = interactFreeItems;
-    }
-}
 
 char *interactMouseover(struct interact *inter, char *otherChrom)
 /* Make mouseover text for an interaction */
 {
 struct dyString *ds = dyStringNew(0);
 if (isEmptyTextField(inter->name))
     {
     if (!isEmptyTextField(inter->exp))
         dyStringPrintf(ds, "%s ", inter->exp);
     if (otherChrom)
         dyStringPrintf(ds, "%s", otherChrom);
     else
         {
         char buf[4096];
         sprintLongWithCommas(buf, inter->chromEnd - inter->chromStart);
@@ -730,67 +783,72 @@
         // draw grab box and map box on peak
         int maxY = peakHeight + yOff;
         int peakX = ((upperX - lowerX + 1) / 2) + lowerX;
         drawPeakMapbox(tg, hvg, inter->chromStart, inter->chromEnd, inter->name, statusBuf,
                             peakX, maxY, peakColor, highlightColor, drawUp);
         }
     }
 }
 
 void interactLinkedFeaturesDrawAt(struct track *tg, void *item,
                           struct hvGfx *hvg, int xOff, int y, double scale,
                           MgFont *font, Color color, enum trackVisibility vis)
 /* Draw a item with target in contrasting color */
 {
 struct linkedFeatures *lf = item;
-if (tg->visibility == tvDense)
+if (vis == tvDense)
     {
     lf->filterColor = slightlyDarkerColor(hvg, MG_GRAY);
                 // can't distinguish overlapping colors, so force to gray
     }
+struct interactTrackInfo *tInfo = tg->customPt;
+
 linkedFeaturesDrawAt(tg, item, hvg, xOff, y, scale, font, color, vis);
 
 // draw overlapping items in white and add right label
-struct interactTrackInfo *tInfo = tg->customPt;
 if (tInfo->clusterMode)
     {
     struct simpleFeature *sf;
     int shortHeight = tg->heightPer/2;
     for (sf = lf->components;  sf; sf = sf->next)
         {
         if (sf->start > lf->tallStart && sf->end < lf->tallEnd)
             {
             drawScaledBox(hvg, sf->start, sf->end, scale, xOff, y + shortHeight/2,
                                 shortHeight, MG_WHITE);
             }
         }
     }
 else
     {
     struct simpleFeature *sf1 = lf->components, *sf2 = sf1->next;
     if (sf2 && sf2->start < sf1->end)
         {
         drawScaledBox(hvg, sf2->start, sf2->end, scale, xOff, y, tg->heightPer, MG_WHITE);
         }
     else
         {
+        if (vis == tvPack || vis == tvFull)
+            {
             // right label
             int x2 = round((double)((int)lf->end - winStart) * scale) + xOff;
             int x = x2 + tl.mWidth/2;
-        char *label = "foo";
-        int w = mgFontStringWidth(font, label);
-        hvGfxTextCentered(hvg, x, y, w, tg->heightPer, lf->filterColor, font, label);
+            struct interactLfEndNames *ends = getInteractLfEndNames(lf);
+            char *rightLabel = (lf->orientation < 0 ? ends->source : ends->target);
+            int w = mgFontStringWidth(font, rightLabel);
+            hvGfxTextCentered(hvg, x, y, w, tg->heightPer, color, font, rightLabel);
+            }
         }
     }
 }
 
 void interactDrawItems(struct track *tg, int seqStart, int seqEnd,
         struct hvGfx *hvg, int xOff, int yOff, int width, 
         MgFont *font, Color color, enum trackVisibility vis)
 /* Draw a list of interact structures. */
 {
 struct interactTrackInfo *tInfo = (struct interactTrackInfo *)tg->customPt;
 if (tInfo->clusterMode || isLinkedFeaturesMode(tg))
     {
     tg->drawItemAt = interactLinkedFeaturesDrawAt;
     linkedFeaturesDraw(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis);
     }