src/hg/hgTracks/makeItemsTrack.c 1.9

1.9 2010/04/13 07:02:05 kent
Some work getting makeItems tracks to display better in dense and full mode.
Index: src/hg/hgTracks/makeItemsTrack.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/hgTracks/makeItemsTrack.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -b -B -U 1000000 -r1.8 -r1.9
--- src/hg/hgTracks/makeItemsTrack.c	10 Apr 2010 19:05:24 -0000	1.8
+++ src/hg/hgTracks/makeItemsTrack.c	13 Apr 2010 07:02:05 -0000	1.9
@@ -1,195 +1,220 @@
 /* makeItemsTrack.c - supports tracks of type makeItems.  Users can drag to create an item
  * and click to edit one. */
 
 #include "common.h"
 #include "hash.h"
 #include "linefile.h"
 #include "jksql.h"
 #include "hdb.h"
 #include "hgTracks.h"
 #include "bed.h"
 #include "binRange.h"
 #include "makeItemsItem.h"
 
 static char const rcsid[] = "$Id$";
 
 void makeItemsJsCommand(char *command, struct track *trackList, struct hash *trackHash)
 /* Execute some command sent to us from the javaScript.  All we know for sure is that
  * the first word of the command is "makeItems."  We expect it to be of format:
  *    makeItems <trackName> <chrom> <chromStart> <chromEnd>
  * If it is indeed of this form then we'll create a new makeItemsItem that references this
  * location and stick it in the named track. */
 {
 /* Parse out command into local variables. */
 char *words[6];
 char *dupeCommand = cloneString(command);	/* For parsing. */
 int wordCount = chopLine(dupeCommand, words);
 if (wordCount != 5)
    errAbort("Expecting %d words in jsCommand '%s'", wordCount, command);
 char *trackName = words[1];
 char *chrom = words[2];
 int chromStart = sqlUnsigned(words[3]);
 int chromEnd = sqlUnsigned(words[4]);
 
 /* Create a new item based on command. */
 struct makeItemsItem *item;
 AllocVar(item);
 item->bin = binFromRange(chromStart, chromEnd);
 item->chrom = cloneString(chrom);
 item->chromStart = item->thickStart = chromStart;
 item->chromEnd = item->thickEnd = chromEnd;
 item->name = cloneString("");
 item->score = 1000;
 item->strand[0] = '.';
 item->description = cloneString("");
 
 /* Add item to database. */
 struct track *track = hashMustFindVal(trackHash, trackName);
 struct customTrack *ct = track->customPt;
 char *tableName = ct->dbTableName;
 struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
 makeItemsItemSaveToDbEscaped(conn, item, tableName, 0);
 hFreeConn(&conn);
 
 freez(&dupeCommand);
 }
 
-static int makeItemsExtraHeight(struct track *tg)
+static int makeItemsExtraHeight(struct track *track)
 /* Return extra height of track. */
 {
 return tl.fontHeight+2;
 }
 
 static void updateTextField(char *trackName, struct sqlConnection *conn,
 	char *tableName, char *fieldName, int id)
 /* Update text valued field with new val. */
 {
 char varName[128];
 char sql[256];
 safef(varName, sizeof(varName), "%s_%s", trackName, fieldName);
 char *newVal = cartOptionalString(cart, varName);
 if (newVal != NULL)
     {
     char *escapedVal = sqlEscapeString(newVal);
     safef(sql, sizeof(sql), "update %s set %s='%s' where id=%d",
 	    tableName, fieldName, escapedVal, id);
     sqlUpdate(conn, sql);
     freez(&escapedVal);
     cartRemove(cart, varName);	/* We don't need it any more. */
     }
 }
 
 static void makeItemsEditOrDelete(char *trackName, struct sqlConnection *conn, char *tableName)
 /* Troll through cart variables looking for things that indicate user edited item
  * or deleted it in hgc,  and carry out edits. See hgc/makeItemsClick.c. */
 {
 char varName[128];
 char sql[256];
 safef(varName, sizeof(varName), "%s_%s", trackName, "id");
 char *idString = cartOptionalString(cart, varName);
 if (idString != NULL)
     {
     int id = sqlUnsigned(idString);
     idString = NULL;			// Will be no good after cartRemove
     cartRemove(cart, varName);		// Remove so only do edits once.
 
     /* Handle cancel. */
     safef(varName, sizeof(varName), "%s_%s", trackName, "cancel");
     if (cartVarExists(cart, varName))
         {
 	cartRemove(cart, varName);	// Only want to do cancels once
 	return;
 	}
 
     /* Handle delete. */
     safef(varName, sizeof(varName), "%s_%s", trackName, "delete");
     if (cartVarExists(cart, varName))
         {
 	cartRemove(cart, varName);	// Especially only want to do deletes once!
 	safef(sql, sizeof(sql), "delete from %s where id=%d", tableName, id);
 	sqlUpdate(conn, sql);
 	return;
 	}
 
     /* Handle edits. */
     updateTextField(trackName, conn, tableName, "name", id);
     updateTextField(trackName, conn, tableName, "description", id);
     }
 }
 
-void makeItemsLoadItems(struct track *tg)
+void makeItemsLoadItems(struct track *track)
 /* Load up items in track already.  Also make up a pseudo-item that is
  * where you drag to create an item. */
 {
 struct bed *bedList = NULL;
-struct customTrack *ct = tg->customPt;
+struct customTrack *ct = track->customPt;
 char *tableName = ct->dbTableName;
 struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
-makeItemsEditOrDelete(tg->mapName, conn, tableName);
+makeItemsEditOrDelete(track->mapName, conn, tableName);
 int rowOffset;
 struct sqlResult *sr = hRangeQuery(conn, tableName, chromName, winStart, winEnd, NULL, &rowOffset);
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct bed *bed = bedLoadN(row+rowOffset, 9);
     /* Add id to front of name */
     char *id = row[rowOffset + 10];
     char buf[64];
     safef(buf, sizeof(buf), "%s %s", id, bed->name);
     freeMem(bed->name);
     bed->name = cloneString(buf);
     slAddHead(&bedList, bed);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 slReverse(&bedList);
-tg->items = bedList;
+track->items = bedList;
 }
 
-char *makeItemsItemName(struct track *tg, void *item)
+char *makeItemsItemName(struct track *track, void *item)
 /* Return name of one of an item to display on left side. */
 {
 struct bed *bed = item;
 char *name = bed->name;
 name = skipToSpaces(name);
 name = skipLeadingSpaces(name);
 return name;
 }
 
-void makeItemsDrawItems(struct track *tg, int seqStart, int seqEnd,
+void makeItemsDrawItems(struct track *track, int seqStart, int seqEnd,
         struct hvGfx *hvg, int xOff, int yOff, int width,
         MgFont *font, Color color, enum trackVisibility vis)
 /* Draw simple Bed items. */
 {
-int dragBarHeight = makeItemsExtraHeight(tg);
-#ifdef DEBUG
-struct customTrack *ct = tg->customPt;
-char *tableName = ct->dbTableName;
-hvGfxText(hvg, xOff, yOff, color, font, tableName);
-#endif /* DEBUG */
+int dragBarHeight = makeItemsExtraHeight(track);
 hvGfxTextCentered(hvg, xOff, yOff, width, dragBarHeight, color, font, 
 	"--- Drag here or in between items to create a new item. ---");
-bedDrawSimple(tg, seqStart, seqEnd, hvg, xOff, yOff + dragBarHeight, width,
+bedDrawSimple(track, seqStart, seqEnd, hvg, xOff, yOff + dragBarHeight, width,
 	font, color, vis);
 }
 
-int makeItemsTotalHeight(struct track *tg, enum trackVisibility vis)
+
+void makeItemsDrawLeftLabels(struct track *track, int seqStart, int seqEnd,
+    struct hvGfx *hvg, int xOff, int yOff, int width, int height,
+    boolean withCenterLabels, MgFont *font,
+    Color color, enum trackVisibility vis)
+/* Draw left label - just in dense or full mode. Needed to cope with empty space at top of track. */
+{
+int y = yOff + makeItemsExtraHeight(track);
+int fontHeight = mgFontLineHeight(font);
+if (withCenterLabels)
+    y += mgFontLineHeight(font);
+if (vis == tvDense)
+    hvGfxTextRight(hvg, xOff, y, width-1, fontHeight, color, font, track->shortLabel);
+else
+    {
+    struct bed *bed, *bedList = track->items;
+    for (bed = bedList; bed != NULL; bed = bed->next)
+	{
+	if (track->itemLabelColor != NULL)
+	    color = track->itemLabelColor(track, bed, hvg);
+	int itemHeight = track->itemHeight(track, bed);
+	hvGfxTextRight(hvg, xOff, y, width - 1,
+	    itemHeight, color, font, track->itemName(track, bed));
+	y += itemHeight;
+	}
+    }
+}
+
+
+int makeItemsTotalHeight(struct track *track, enum trackVisibility vis)
 /* Most fixed height track groups will use this to figure out the height
  * they use. */
 {
-return tgFixedTotalHeightOptionalOverflow(tg,vis, tl.fontHeight+1, tl.fontHeight, FALSE) + 
-	makeItemsExtraHeight(tg);
+track->height = tgFixedTotalHeightOptionalOverflow(track, vis, tl.fontHeight+1, tl.fontHeight, FALSE) + 
+	makeItemsExtraHeight(track);
+return track->height;
 }
 
 
 void makeItemsMethods(struct track *track)
 /* Set up special methods for makeItems type tracks. */
 {
 bedMethods(track);
 track->totalHeight = makeItemsTotalHeight;
 track->drawItems = makeItemsDrawItems;
+track->drawLeftLabels = makeItemsDrawLeftLabels;
 track->loadItems = makeItemsLoadItems;
 track->itemName = makeItemsItemName;
-track->canPack = TRUE;
 }