c32d6e08d19a0b8ad7ece12aab39d0ba0be7a9e1 chmalee Mon Dec 1 16:26:27 2025 -0800 hgTracks tooltips use the data-tooltip attribute on the area element if present, falling back to the title attribute. Makes title attributes for (hopefully all) tracks with special tooltips put the tooltip string into the data-tooltip attribute instead of the title attribute, refs #35756 diff --git src/hg/hgTracks/imageV2.c src/hg/hgTracks/imageV2.c index bd8753c83ca..9d50ecd1506 100644 --- src/hg/hgTracks/imageV2.c +++ src/hg/hgTracks/imageV2.c @@ -366,117 +366,128 @@ { struct mapItem *item; for (item=map->items;item!=NULL;item=item->next) { if ((abs(item->topLeftX - topLeftX) < 2) && (abs(item->topLeftY - topLeftY) < 2) && (abs(item->bottomRightX - bottomRightX) < 2) && (abs(item->bottomRightY - bottomRightY) < 2)) // coordinates within a pixel is okay return item; } return NULL; } struct mapItem *mapSetItemUpdate(struct mapSet *map,struct mapItem *item,char *link,char *title, int topLeftX,int topLeftY,int bottomRightX,int bottomRightY, - char *id) + char *id, char *tooltip) // Update a single mapItem { if (title != NULL) + { item->title = cloneString(title); + item->tooltip = cloneString(title); + } +if (tooltip != NULL) + item->tooltip = cloneString(tooltip); if (link != NULL) { if (map->linkRoot != NULL && startsWith(map->linkRoot,link)) item->linkVar = cloneString(link + strlen(map->linkRoot)); else item->linkVar = cloneString(link); } item->topLeftX = topLeftX; item->topLeftY = topLeftY; item->bottomRightX = bottomRightX; item->bottomRightY = bottomRightY; freeMem(item->id); item->id = cloneString(id); return item; } struct mapItem *mapSetItemAdd(struct mapSet *map,char *link,char *title,int topLeftX,int topLeftY, - int bottomRightX,int bottomRightY, char *id) + int bottomRightX,int bottomRightY, char *id, char *tooltip) // Add a single mapItem to a growing mapSet { struct mapItem *item; AllocVar(item); if (title != NULL) + { item->title = cloneString(title); + // default the tooltip to the title + item->tooltip = cloneString(title); + } +if (tooltip != NULL) + item->tooltip = cloneString(tooltip); if (link != NULL) { if (map->linkRoot != NULL && startsWith(map->linkRoot,link)) item->linkVar = cloneString(link + strlen(map->linkRoot)); else item->linkVar = cloneString(link); } item->topLeftX = topLeftX; item->topLeftY = topLeftY; item->bottomRightX = bottomRightX; item->bottomRightY = bottomRightY; item->id = cloneString(id); slAddHead(&(map->items),item); //warn("Added map(%s) item '%s' count:%d",map->name,title,slCount(map->items)); return map->items; } struct mapItem *mapSetItemUpdateOrAdd(struct mapSet *map,char *link,char *title, int topLeftX,int topLeftY,int bottomRightX,int bottomRightY, - char *id) + char *id, char *tooltip) // Update or add a single mapItem { struct mapItem *item = mapSetItemFind(map,topLeftX,topLeftY,bottomRightX,bottomRightY); if (item != NULL) - return mapSetItemUpdate(map,item,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY, id); + return mapSetItemUpdate(map,item,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY, id, tooltip); else - return mapSetItemAdd(map,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY, id); + return mapSetItemAdd(map,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY, id, tooltip); } struct mapItem *doMapSetItemFindOrAdd(struct mapSet *map,char *link,char *title, int topLeftX,int topLeftY,int bottomRightX,int bottomRightY, - char *id) + char *id, char *tooltip) // Finds or adds the map item { struct mapItem *item = mapSetItemFind(map,topLeftX,topLeftY,bottomRightX,bottomRightY); if (item != NULL) return item; else - return mapSetItemAdd(map,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY,id); + return mapSetItemAdd(map,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY,id, tooltip); } struct mapItem *mapSetItemFindOrAdd(struct mapSet *map,char *link,char *title, int topLeftX,int topLeftY,int bottomRightX,int bottomRightY, - char *id) + char *id, char *tooltip) // Function to allow conf variable to turn off or on the searching of overlapping // previous boxes. { static struct mapItem *(*mapFunc)() = NULL; if (mapFunc == NULL) { if (cfgOption("restoreMapFind")) mapFunc = doMapSetItemFindOrAdd; else mapFunc = mapSetItemAdd; } -return (*mapFunc)(map,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY,id); +return (*mapFunc)(map,link,title,topLeftX,topLeftY,bottomRightX,bottomRightY,id, tooltip); } void mapItemFree(struct mapItem **pItem) // frees all memory assocated with a single mapItem { if (pItem != NULL && *pItem != NULL) { struct mapItem *item = *pItem; if (item->title != NULL) freeMem(item->title); if (item->linkVar != NULL) freeMem(item->linkVar); if (item->id != NULL) freeMem(item->id); freeMem(item); @@ -1123,31 +1134,31 @@ int bottomY = 0; imgTrackCoordinates(imgTrack,NULL,NULL,NULL,&bottomY); return bottomY; } struct mapSet *imgTrackGetMapByType(struct imgTrack *imgTrack,enum sliceType type) // Gets the map assocated with a specific slice belonging to the imgTrack { struct imgSlice *slice = imgTrackSliceGetByType(imgTrack,type); if (slice == NULL) return NULL; return sliceGetMap(slice,FALSE); // Map could belong to image or could be slice specific } int imgTrackAddMapItem(struct imgTrack *imgTrack,char *link,char *title, - int topLeftX,int topLeftY,int bottomRightX,int bottomRightY, char *id) + int topLeftX,int topLeftY,int bottomRightX,int bottomRightY, char *id, char *tooltip) // Will add a map item to an imgTrack's appropriate slice's map. Since a map item may span // slices, the imgTrack is in the best position to determine where to put the map item // returns count of map items added, which could be 0, 1 or more than one if item spans slices // NOTE: Precedence is given to first map item when adding items with same coordinates! { if (imgTrack == NULL) return 0; struct imgSlice *slice; char *imgFile = NULL; // name of file that hold the image char *neededId = NULL; // id is only added it it is NOT the trackId. if (imgTrack->tdb == NULL || differentStringNullOk(id, imgTrack->tdb->track)) neededId = id; // Trap surprising location s for map items, but only on test machines. if (hIsPrivateHost()) @@ -1174,31 +1185,31 @@ { if (imgFile == NULL) imgFile = slice->parentImg->file; } if (topLeftX < (slice->offsetX + slice->width-1) && bottomRightX > (slice->offsetX + 1) && topLeftY < (slice->offsetY + slice->height-1) && bottomRightY > (slice->offsetY + 1)) // Overlap of a pixel or 2 is tolerated { struct mapSet *map = sliceGetMap(slice,FALSE); if (map!=NULL) { // NOTE: using find or add gives precedence to first of same coordinate map items mapSetItemFindOrAdd(map,link,title,max(topLeftX,slice->offsetX), max(topLeftY,slice->offsetY), min(bottomRightX,slice->offsetX + slice->width), - min(bottomRightY,slice->offsetY + slice->height), neededId); + min(bottomRightY,slice->offsetY + slice->height), neededId, tooltip); count++; } else { // NOTE: This assumes that if there is no map, the entire slice should get the link! char * name = (imgTrack->name != NULL ? imgTrack->name : imgTrack->tdb != NULL ? imgTrack->tdb->track : imgFile); warn("imgTrackAddMapItem(%s,%s) mapItem(lx:%d,rx:%d) is overlapping " "slice:%s(lx:%d,rx:%d)",name,title,topLeftX,bottomRightX, sliceTypeToString(slice->type),slice->offsetX,(slice->offsetX + slice->width - 1)); sliceAddLink(slice,link,title); count++; } } } @@ -1828,30 +1839,33 @@ hPrintf(" HREF=%s",item->linkVar); else if (startsWith("/cgi-bin/hgGene", item->linkVar)) // redmine #4151 hPrintf(" HREF='..%s'",item->linkVar); // FIXME: Chin should get rid else // of this special case! hPrintf(" HREF='%s'",item->linkVar); hPrintf(" class='area'"); } else warn("map item has no url!"); if (item->title != NULL && strlen(item->title) > 0) { char *encodedString = attributeEncode(item->title); if (cfgOptionBooleanDefault("showMouseovers", FALSE)) { + if (isNotEmpty(item->tooltip)) + hPrintf(" title='%s' data-tooltip='%s'", encodedString, attributeEncode(item->tooltip)); + else hPrintf(" TITLE='%s'", encodedString); } else { // for TITLEs, which we use for mouseOvers, since they can't have HTML in // them, we substitute a unicode new line for
after we've encoded it. // This is stop-gap until we start doing mouseOvers entirely in Javascript hPrintf(" TITLE='%s'", replaceChars(encodedString,"<br>", "
")); } } if (item->id != NULL) hPrintf(" id='%s'", item->id); hPrintf(">" ); } hPrintf("\n");