changeset 53333:c23cf5520cd9

(Qarrow, Qhand, Qtext, Qpointer): New variables for pointer types. (Qrelative_width, Qalign_to): Remove unused variables. (Vvoid_text_area_pointer): Replace Vshow_text_cursor_in_void. (QCmap, QCpointer, Qrect, Qcircle, Qpoly): New variables for image maps. (x_y_to_hpos_vpos): Return glyph relative coordinates through new dx and dy args. Remove buffer_only_p arg (always 0). Simplify code accordingly. (get_glyph_string_clip_rect): Draw cursor using glyph's rather than row's ascent and height, to get sensible height on tall rows. (build_desired_tool_bar_string): Remove Qimage extern. (get_tool_bar_item): Fix call to x_y_to_hpos_vpos. (produce_image_glyph): Adjust it.ascent to minimum row ascent if image glyph is alone on the last line. (append_glyph, append_composite_glyph, produce_image_glyph) (append_stretch_glyph): Set glyph's ascent and descent. (on_hot_spot_p): New function to check if position is inside an rectangular, circular, or polygon-shaped image hot-spot, (find_hot_spot): New function to search for image hot-spot. (Flookup_image_map): New defun to search for image hot-spot. (define_frame_cursor1): New aux function to determine frame pointer. (note_mode_line_or_margin_highlight, note_mouse_highlight): Handle `pointer' text property and :pointer image property to control frame pointer shape. Detect image hot-spots for pointer and help_echo properties. Use define_frame_cursor1. (note_mouse_highlight): Use Vvoid_text_area_pointer. (syms_of_xdisp): Defsubr new defun. Intern and staticpro new variables. DEFVAR_LISP Vvoid_text_area_pointer instead of Vshow_text_cursor_in_void.
author Kim F. Storm <storm@cua.dk>
date Sun, 28 Dec 2003 00:12:27 +0000
parents 21301d0e0505
children 0432020d46a7
files src/xdisp.c
diffstat 1 files changed, 401 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/src/xdisp.c	Sun Dec 28 00:11:49 2003 +0000
+++ b/src/xdisp.c	Sun Dec 28 00:12:27 2003 +0000
@@ -242,6 +242,9 @@
 /* Cursor shapes */
 Lisp_Object Qbar, Qhbar, Qbox, Qhollow;
 
+/* Pointer shapes */
+Lisp_Object Qarrow, Qhand, Qtext;
+
 Lisp_Object Qrisky_local_variable;
 
 /* Holds the list (error).  */
@@ -290,7 +293,7 @@
 
 /* Names of text properties relevant for redisplay.  */
 
-Lisp_Object Qdisplay, Qrelative_width, Qalign_to;
+Lisp_Object Qdisplay;
 extern Lisp_Object Qface, Qinvisible, Qwidth;
 
 /* Symbols used in text property values.  */
@@ -298,7 +301,7 @@
 Lisp_Object Vdisplay_pixels_per_inch;
 Lisp_Object Qspace, QCalign_to, QCrelative_width, QCrelative_height;
 Lisp_Object Qleft_margin, Qright_margin, Qspace_width, Qraise;
-Lisp_Object Qmargin;
+Lisp_Object Qmargin, Qpointer;
 extern Lisp_Object Qheight;
 extern Lisp_Object QCwidth, QCheight, QCascent;
 extern Lisp_Object Qscroll_bar;
@@ -311,7 +314,7 @@
    i.e. in blank areas after eol and eob.  This used to be
    the default in 21.3.  */
 
-Lisp_Object Vshow_text_cursor_in_void;
+Lisp_Object Vvoid_text_area_pointer;
 
 /* Name of the face used to highlight trailing whitespace.  */
 
@@ -322,6 +325,10 @@
 
 Lisp_Object Qimage;
 
+/* The image map types.  */
+Lisp_Object QCmap, QCpointer;
+Lisp_Object Qrect, Qcircle, Qpoly;
+
 /* Non-zero means print newline to stdout before next mini-buffer
    message.  */
 
@@ -1582,11 +1589,10 @@
    date.  */
 
 static struct glyph *
-x_y_to_hpos_vpos (w, x, y, hpos, vpos, area, buffer_only_p)
+x_y_to_hpos_vpos (w, x, y, hpos, vpos, dx, dy, area)
      struct window *w;
      int x, y;
-     int *hpos, *vpos, *area;
-     int buffer_only_p;
+     int *hpos, *vpos, *dx, *dy, *area;
 {
   struct glyph *glyph, *end;
   struct glyph_row *row = NULL;
@@ -1637,23 +1643,22 @@
   /* Find glyph containing X.  */
   glyph = row->glyphs[*area];
   end = glyph + row->used[*area];
-  while (glyph < end)
-    {
-      if (x < x0 + glyph->pixel_width)
-	{
-	  if (w->pseudo_window_p)
-	    break;
-	  else if (!buffer_only_p || BUFFERP (glyph->object))
-	    break;
-	}
-
-      x0 += glyph->pixel_width;
+  x -= x0;
+  while (glyph < end && x >= glyph->pixel_width)
+    {
+      x -= glyph->pixel_width;
       ++glyph;
     }
 
   if (glyph == end)
     return NULL;
 
+  if (dx)
+    {
+      *dx = x;
+      *dy = y - (row->y + row->ascent - glyph->ascent);
+    }
+
   *hpos = glyph - row->glyphs[*area];
   return glyph;
 }
@@ -1741,20 +1746,28 @@
 
   r.y = WINDOW_TO_FRAME_PIXEL_Y (s->w, r.y);
 
-#ifdef HAVE_NTGUI
-  /* ++KFS: From W32 port, but it looks ok for all platforms to me.  */
   /* If drawing the cursor, don't let glyph draw outside its
      advertised boundaries. Cleartype does this under some circumstances.  */
   if (s->hl == DRAW_CURSOR)
     {
+      struct glyph *glyph = s->first_glyph;
+      int height;
+
       if (s->x > r.x)
 	{
 	  r.width -= s->x - r.x;
 	  r.x = s->x;
 	}
-      r.width = min (r.width, s->first_glyph->pixel_width);
-    }
-#endif
+      r.width = min (r.width, glyph->pixel_width);
+
+      /* Don't draw cursor glyph taller than our actual glyph.  */
+      height = max (FRAME_LINE_HEIGHT (s->f), glyph->ascent + glyph->descent);
+      if (height < r.height)
+	{
+	  r.y = s->ybase + glyph->descent - height;
+	  r.height = height;
+	}
+    }
 
 #ifdef CONVERT_FROM_XRECT
   CONVERT_FROM_XRECT (r, *nr);
@@ -8279,7 +8292,7 @@
       int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
       int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
       int hmargin, vmargin, relief, idx, end;
-      extern Lisp_Object QCrelief, QCmargin, QCconversion, Qimage;
+      extern Lisp_Object QCrelief, QCmargin, QCconversion;
 
       /* If image is a vector, choose the image according to the
 	 button state.  */
@@ -8696,7 +8709,7 @@
   int area;
 
   /* Find the glyph under X/Y.  */
-  *glyph = x_y_to_hpos_vpos (w, x, y, hpos, vpos, &area, 0);
+  *glyph = x_y_to_hpos_vpos (w, x, y, hpos, vpos, 0, 0, &area);
   if (*glyph == NULL)
     return -1;
 
@@ -17685,6 +17698,8 @@
       glyph->charpos = CHARPOS (it->position);
       glyph->object = it->object;
       glyph->pixel_width = it->pixel_width;
+      glyph->ascent = it->ascent;
+      glyph->descent = it->descent;
       glyph->voffset = it->voffset;
       glyph->type = CHAR_GLYPH;
       glyph->multibyte_p = it->multibyte_p;
@@ -17719,6 +17734,8 @@
       glyph->charpos = CHARPOS (it->position);
       glyph->object = it->object;
       glyph->pixel_width = it->pixel_width;
+      glyph->ascent = it->ascent;
+      glyph->descent = it->descent;
       glyph->voffset = it->voffset;
       glyph->type = COMPOSITE_GLYPH;
       glyph->multibyte_p = it->multibyte_p;
@@ -17767,6 +17784,7 @@
 {
   struct image *img;
   struct face *face;
+  int face_ascent, glyph_ascent;
 
   xassert (it->what == IT_IMAGE);
 
@@ -17778,10 +17796,15 @@
   PREPARE_FACE_FOR_DISPLAY (it->f, face);
   prepare_image_for_display (it->f, img);
 
-  it->ascent = it->phys_ascent = image_ascent (img, face);
+  it->ascent = it->phys_ascent = glyph_ascent = image_ascent (img, face);
   it->descent = it->phys_descent = img->height + 2 * img->vmargin - it->ascent;
   it->pixel_width = img->width + 2 * img->hmargin;
 
+  /* If this glyph is alone on the last line, adjust it.ascent to minimum row ascent.  */
+  face_ascent = face->font ? FONT_BASE (face->font) : FRAME_BASELINE_OFFSET (it->f);
+  if (face_ascent > it->ascent)
+    it->ascent = it->phys_ascent = face_ascent;
+
   it->nglyphs = 1;
 
   if (face->box != FACE_NO_BOX)
@@ -17811,6 +17834,8 @@
 	  glyph->charpos = CHARPOS (it->position);
 	  glyph->object = it->object;
 	  glyph->pixel_width = it->pixel_width;
+	  glyph->ascent = glyph_ascent;
+	  glyph->descent = it->descent;
 	  glyph->voffset = it->voffset;
 	  glyph->type = IMAGE_GLYPH;
 	  glyph->multibyte_p = it->multibyte_p;
@@ -17850,6 +17875,8 @@
       glyph->charpos = CHARPOS (it->position);
       glyph->object = object;
       glyph->pixel_width = width;
+      glyph->ascent = ascent;
+      glyph->descent = height - ascent;
       glyph->voffset = it->voffset;
       glyph->type = STRETCH_GLYPH;
       glyph->multibyte_p = it->multibyte_p;
@@ -19945,6 +19972,189 @@
 }
 
 
+/* See if position X, Y is within a hot-spot of an image.  */
+
+static int
+on_hot_spot_p (hot_spot, x, y)
+     Lisp_Object hot_spot;
+     int x, y;
+{
+  if (!CONSP (hot_spot))
+    return 0;
+
+  if (EQ (XCAR (hot_spot), Qrect))
+    {
+      /* CDR is (Top-Left . Bottom-Right) = ((x0 . y0) . (x1 . y1))  */
+      Lisp_Object rect = XCDR (hot_spot);
+      Lisp_Object tem;
+      if (!CONSP (rect))
+	return 0;
+      if (!CONSP (XCAR (rect)))
+	return 0;
+      if (!CONSP (XCDR (rect)))
+	return 0;
+      if (!(tem = XCAR (XCAR (rect)), INTEGERP (tem) && x >= XINT (tem)))
+	return 0;
+      if (!(tem = XCDR (XCAR (rect)), INTEGERP (tem) && y >= XINT (tem)))
+	return 0;
+      if (!(tem = XCAR (XCDR (rect)), INTEGERP (tem) && x <= XINT (tem)))
+	return 0;
+      if (!(tem = XCDR (XCDR (rect)), INTEGERP (tem) && y <= XINT (tem)))
+	return 0;
+      return 1;
+    }
+  else if (EQ (XCAR (hot_spot), Qcircle))
+    {
+      /* CDR is (Center . Radius) = ((x0 . y0) . r) */
+      Lisp_Object circ = XCDR (hot_spot);
+      Lisp_Object lr, lx0, ly0;
+      if (CONSP (circ)
+	  && CONSP (XCAR (circ))
+	  && (lr = XCDR (circ), INTEGERP (lr) || FLOATP (lr))
+	  && (lx0 = XCAR (XCAR (circ)), INTEGERP (lx0))
+	  && (ly0 = XCDR (XCAR (circ)), INTEGERP (ly0)))
+	{
+	  double r = XFLOATINT (lr);
+	  double dx = XINT (lx0) - x;
+	  double dy = XINT (ly0) - y;
+	  return (dx * dx + dy * dy <= r * r);
+	}
+    }
+  else if (EQ (XCAR (hot_spot), Qpoly))
+    {
+      /* CDR is [x0 y0 x1 y1 x2 y2 ...x(n-1) y(n-1)] */
+      if (VECTORP (XCDR (hot_spot)))
+	{
+	  struct Lisp_Vector *v = XVECTOR (XCDR (hot_spot));
+	  Lisp_Object *poly = v->contents;
+	  int n = v->size;
+	  int i;
+	  int inside = 0;
+	  Lisp_Object lx, ly;
+	  int x0, y0;
+
+	  /* Need an even number of coordinates, and at least 3 edges.  */
+	  if (n < 6 || n & 1) 
+	    return 0;
+
+	  /* Count edge segments intersecting line from (X,Y) to (X,infinity).
+	     If count is odd, we are inside polygon.  Pixels on edges
+	     may or may not be included depending on actual geometry of the
+	     polygon.  */
+	  if ((lx = poly[n-2], !INTEGERP (lx))
+	      || (ly = poly[n-1], !INTEGERP (lx)))
+	    return 0;
+	  x0 = XINT (lx), y0 = XINT (ly);
+	  for (i = 0; i < n; i += 2)
+	    {
+	      int x1 = x0, y1 = y0;
+	      if ((lx = poly[i], !INTEGERP (lx))
+		  || (ly = poly[i+1], !INTEGERP (ly)))
+		return 0;
+	      x0 = XINT (lx), y0 = XINT (ly);
+
+	      /* Does this segment cross the X line?  */
+	      if (x0 >= x)
+		{
+		  if (x1 >= x)
+		    continue;
+		}
+	      else if (x1 < x)
+		continue;
+	      if (y > y0 && y > y1)
+		continue;
+	      if (y < y0 + ((y1 - y0) * (x - x0)) / (x1 - x0))
+		inside = !inside;
+	    }
+	  return inside;
+	}
+    }
+  else
+    return 0;
+}
+
+Lisp_Object
+find_hot_spot (map, x, y)
+     Lisp_Object map;
+     int x, y;
+{
+  while (CONSP (map))
+    {
+      if (CONSP (XCAR (map))
+	  && on_hot_spot_p (XCAR (XCAR (map)), x, y))
+	return XCAR (map);
+      map = XCDR (map);
+    }
+  
+  return Qnil;
+}
+
+DEFUN ("lookup-image-map", Flookup_image_map, Slookup_image_map,
+       3, 3, 0,
+       doc: /* Lookup in image map MAP coordinates X and Y.  
+An image map is an alist where each element has the format (AREA ID PLIST).
+An AREA is specified as either a rectangle, a circle, or a polygon:
+A rectangle is a cons (rect . ((x0 . y0) . (x1 . y1))) specifying the
+pixel coordinates of the upper left and bottom right corners.
+A circle is a cons (circle . ((x0 . y0) . r)) specifying the center
+and the radius of the circle; r may be a float or integer.
+A polygon is a cons (poly . [x0 y0 x1 y1 ...]) where each pair in the
+vector describes one corner in the polygon.
+Returns the alist element for the first matching AREA in MAP.  */)
+  (map, x, y)
+     Lisp_Object map;
+     Lisp_Object x, y;
+{
+  int ix, iy;
+  if (NILP (map))
+    return Qnil;
+
+  if (!INTEGERP (x))
+    wrong_type_argument (Qintegerp, x);
+  if (!INTEGERP (y))
+    wrong_type_argument (Qintegerp, y);
+
+  return find_hot_spot (map, XINT (x), XINT (y));
+}
+
+
+/* Display frame CURSOR, optionally using shape defined by POINTER.  */
+static void
+define_frame_cursor1 (f, cursor, pointer)
+     struct frame *f;
+     Cursor cursor;
+     Lisp_Object pointer;
+{
+  if (!NILP (pointer))
+    {
+      if (EQ (pointer, Qarrow))
+	cursor = FRAME_X_OUTPUT (f)->nontext_cursor;
+      else if (EQ (pointer, Qhand))
+	cursor = FRAME_X_OUTPUT (f)->hand_cursor;
+      else if (EQ (pointer, Qtext))
+	cursor = FRAME_X_OUTPUT (f)->text_cursor;
+      else if (EQ (pointer, intern ("hdrag")))
+	cursor = FRAME_X_OUTPUT (f)->horizontal_drag_cursor;
+#ifdef HAVE_X_WINDOWS
+      else if (EQ (pointer, intern ("vdrag")))
+	cursor = FRAME_X_DISPLAY_INFO (f)->vertical_scroll_bar_cursor;
+#endif
+      else if (EQ (pointer, intern ("hourglass")))
+	cursor = FRAME_X_OUTPUT (f)->hourglass_cursor;
+      else if (EQ (pointer, Qmodeline))
+	cursor = FRAME_X_OUTPUT (f)->modeline_cursor;
+      else
+	cursor = FRAME_X_OUTPUT (f)->nontext_cursor;
+    }
+
+#ifndef HAVE_CARBON
+  if (cursor != No_Cursor)
+#else
+  if (bcmp (&cursor, &No_Cursor, sizeof (Cursor)))
+#endif
+    rif->define_frame_cursor (f, cursor);
+}
+
 /* Take proper action when mouse has moved to the mode or header line
    or marginal area AREA of window W, x-position X and y-position Y.
    X is relative to the start of the text display area of W, so the
@@ -19960,18 +20170,24 @@
   struct frame *f = XFRAME (w->frame);
   Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
   Cursor cursor = FRAME_X_OUTPUT (f)->nontext_cursor;
-  int charpos;
-  Lisp_Object string, help, map, pos;
+  Lisp_Object pointer = Qnil;
+  int charpos, dx, dy;
+  Lisp_Object string;
+  Lisp_Object pos, help, image;
 
   if (area == ON_MODE_LINE || area == ON_HEADER_LINE)
     string = mode_line_string (w, &x, &y, 0, 0, area, &charpos);
   else
-    string = marginal_area_string (w, &x, &y, 0, 0, area, &charpos);
+    {
+      x -= WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (w);
+      string = marginal_area_string (w, &x, &y, &dx, &dy, area, &charpos);
+    }
+
+  help = Qnil;
 
   if (STRINGP (string))
     {
       pos = make_number (charpos);
-
       /* If we're on a string with `help-echo' text property, arrange
 	 for the help to be displayed.  This is done by setting the
 	 global variable help_echo_string to the help string.  */
@@ -19984,9 +20200,13 @@
 	  help_echo_pos = charpos;
 	}
 
+      if (NILP (pointer))
+	pointer = Fget_text_property (pos, Qpointer, string);
+
      /* Change the mouse pointer according to what is under X/Y.  */
-      if (area == ON_MODE_LINE)
-	{
+      if (NILP (pointer) && area == ON_MODE_LINE)
+	{
+	  Lisp_Object map;
 	  map = Fget_text_property (pos, Qlocal_map, string);
 	  if (!KEYMAPP (map))
 	    map = Fget_text_property (pos, Qkeymap, string);
@@ -19994,8 +20214,42 @@
 	    cursor = dpyinfo->vertical_scroll_bar_cursor;
 	}
     }
-
-  rif->define_frame_cursor (f, cursor);
+  else if (IMAGEP (string))
+    {
+      Lisp_Object image_map, hotspot;
+      if ((image_map = Fplist_get (XCDR (string), QCmap),
+	   !NILP (image_map))
+	  && (hotspot = find_hot_spot (image_map, dx, dy),
+	      CONSP (hotspot))
+	  && (hotspot = XCDR (hotspot), CONSP (hotspot)))
+	{
+	  Lisp_Object area_id, plist;
+
+	  area_id = XCAR (hotspot);
+	  /* Could check AREA_ID to see if we enter/leave this hot-spot.
+	     If so, we could look for mouse-enter, mouse-leave
+	     properties in PLIST (and do something...).  */
+	  if ((plist = XCDR (hotspot), CONSP (plist)))
+	    {
+	      pointer = Fplist_get (plist, Qpointer);
+	      if (NILP (pointer))
+		pointer = Qhand;
+	      help = Fplist_get (plist, Qhelp_echo);
+	      if (!NILP (help))
+		{
+		  help_echo_string = help;
+		  /* Is this correct?  ++kfs */
+		  XSETWINDOW (help_echo_window, w);
+		  help_echo_object = w->buffer;
+		  help_echo_pos = charpos;
+		}
+	    }
+	  if (NILP (pointer))
+	    pointer = Fplist_get (XCDR (string), QCpointer);
+	}
+    }
+
+  define_frame_cursor1 (f, cursor, pointer);
 }
 
 
@@ -20015,6 +20269,7 @@
   Lisp_Object window;
   struct window *w;
   Cursor cursor = No_Cursor;
+  Lisp_Object pointer = Qnil;  /* Takes precedence over cursor!  */
   struct buffer *b;
 
   /* When a menu is active, don't highlight because this looks odd.  */
@@ -20052,7 +20307,6 @@
     return;
 
   /* Reset help_echo_string. It will get recomputed below.  */
-  /* ++KFS: X version didn't do this, but it looks harmless.  */
   help_echo_string = Qnil;
 
   /* Convert to window-relative pixel coordinates.  */
@@ -20090,7 +20344,7 @@
       && XFASTINT (w->last_modified) == BUF_MODIFF (b)
       && XFASTINT (w->last_overlay_modified) == BUF_OVERLAY_MODIFF (b))
     {
-      int hpos, vpos, pos, i, area;
+      int hpos, vpos, pos, i, dx, dy, area;
       struct glyph *glyph;
       Lisp_Object object;
       Lisp_Object mouse_face = Qnil, overlay = Qnil, position;
@@ -20100,7 +20354,45 @@
       int obegv, ozv, same_region;
 
       /* Find the glyph under X/Y.  */
-      glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, &area, 0);
+      glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, &dx, &dy, &area);
+
+      /* Look for :pointer property on image.  */
+      if (glyph != NULL && glyph->type == IMAGE_GLYPH)
+	{
+	  struct image *img = IMAGE_FROM_ID (f, glyph->u.img_id);
+	  if (img != NULL && IMAGEP (img->spec))
+	    {
+	      Lisp_Object image_map, hotspot;
+	      if ((image_map = Fplist_get (XCDR (img->spec), QCmap),
+		   !NILP (image_map))
+		  && (hotspot = find_hot_spot (image_map, dx, dy),
+		      CONSP (hotspot))
+		  && (hotspot = XCDR (hotspot), CONSP (hotspot)))
+		{
+		  Lisp_Object area_id, plist;
+
+		  area_id = XCAR (hotspot);
+		  /* Could check AREA_ID to see if we enter/leave this hot-spot.
+		     If so, we could look for mouse-enter, mouse-leave
+		     properties in PLIST (and do something...).  */
+		  if ((plist = XCDR (hotspot), CONSP (plist)))
+		    {
+		      pointer = Fplist_get (plist, Qpointer);
+		      if (NILP (pointer))
+			pointer = Qhand;
+		      help_echo_string = Fplist_get (plist, Qhelp_echo);
+		      if (!NILP (help_echo_string))
+			{
+			  help_echo_window = window;
+			  help_echo_object = glyph->object;
+			  help_echo_pos = glyph->charpos;
+			}
+		    }
+		}
+	      if (NILP (pointer))
+		pointer = Fplist_get (XCDR (img->spec), QCpointer);
+	    }
+	}
 
       /* Clear mouse face if X/Y not over text.  */
       if (glyph == NULL
@@ -20109,8 +20401,13 @@
 	{
 	  if (clear_mouse_face (dpyinfo))
 	    cursor = No_Cursor;
-	  if (NILP (Vshow_text_cursor_in_void))
-	    cursor = FRAME_X_OUTPUT (f)->nontext_cursor;
+	  if (NILP (pointer))
+	    {
+	      if (area != TEXT_AREA)
+		cursor = FRAME_X_OUTPUT (f)->nontext_cursor;
+	      else
+		pointer = Vvoid_text_area_pointer;
+	    }
 	  goto set_cursor;
 	}
 
@@ -20123,9 +20420,6 @@
       if (BUFFERP (object) && pos > BUF_Z (b))
 	goto set_cursor;
 
-      if (glyph->type == IMAGE_GLYPH)
-	cursor = FRAME_X_OUTPUT (f)->nontext_cursor;
-
       /* Make the window's buffer temporarily current for
 	 overlays_at and compute_char_face.  */
       obuf = current_buffer;
@@ -20369,7 +20663,7 @@
     check_help_echo:
 
       /* Look for a `help-echo' property.  */
-      {
+      if (NILP (help_echo_string)) {
 	Lisp_Object help, overlay;
 
 	/* Check overlays first.  */
@@ -20435,6 +20729,46 @@
 	  }
       }
 
+      /* Look for a `pointer' property.  */
+      if (NILP (pointer))
+	{
+	  /* Check overlays first.  */
+	  for (i = noverlays - 1; i >= 0 && NILP (pointer); --i)
+	    pointer = Foverlay_get (overlay_vec[i], Qpointer);
+
+	  if (NILP (pointer))
+	    {
+	      Lisp_Object object = glyph->object;
+	      int charpos = glyph->charpos;
+
+	      /* Try text properties.  */
+	      if (STRINGP (object)
+		  && charpos >= 0
+		  && charpos < SCHARS (object))
+		{
+		  pointer = Fget_text_property (make_number (charpos),
+						Qpointer, object);
+		  if (NILP (pointer))
+		    {
+		      /* If the string itself doesn't specify a pointer,
+			 see if the buffer text ``under'' it does.  */
+		      struct glyph_row *r
+			= MATRIX_ROW (w->current_matrix, vpos);
+		      int start = MATRIX_ROW_START_CHARPOS (r);
+		      int pos = string_buffer_position (w, object, start);
+		      if (pos > 0)
+			pointer = Fget_char_property (make_number (pos),
+						      Qpointer, w->buffer);
+		    }
+		}
+	      else if (BUFFERP (object)
+		       && charpos >= BEGV
+		       && charpos < ZV)
+		pointer = Fget_text_property (make_number (charpos),
+					      Qpointer, object);
+	    }
+	}
+
       BEGV = obegv;
       ZV = ozv;
       current_buffer = obuf;
@@ -20442,12 +20776,7 @@
 
  set_cursor:
 
-#ifndef HAVE_CARBON
-  if (cursor != No_Cursor)
-#else
-  if (bcmp (&cursor, &No_Cursor, sizeof (Cursor)))
-#endif
-    rif->define_frame_cursor (f, cursor);
+  define_frame_cursor1 (f, cursor, pointer);
 }
 
 
@@ -21043,6 +21372,7 @@
 #endif
 #ifdef HAVE_WINDOW_SYSTEM
   defsubr (&Stool_bar_lines_needed);
+  defsubr (&Slookup_image_map);
 #endif
   defsubr (&Sformat_mode_line);
 
@@ -21076,16 +21406,14 @@
   staticpro (&Qspace);
   Qmargin = intern ("margin");
   staticpro (&Qmargin);
+  Qpointer = intern ("pointer");
+  staticpro (&Qpointer);
   Qleft_margin = intern ("left-margin");
   staticpro (&Qleft_margin);
   Qright_margin = intern ("right-margin");
   staticpro (&Qright_margin);
-  Qalign_to = intern ("align-to");
-  staticpro (&Qalign_to);
   QCalign_to = intern (":align-to");
   staticpro (&QCalign_to);
-  Qrelative_width = intern ("relative-width");
-  staticpro (&Qrelative_width);
   QCrelative_width = intern (":relative-width");
   staticpro (&QCrelative_width);
   QCrelative_height = intern (":relative-height");
@@ -21104,6 +21432,16 @@
   staticpro (&Qtrailing_whitespace);
   Qimage = intern ("image");
   staticpro (&Qimage);
+  QCmap = intern (":map");
+  staticpro (&QCmap);
+  QCpointer = intern (":pointer");
+  staticpro (&QCpointer);
+  Qrect = intern ("rect");
+  staticpro (&Qrect);
+  Qcircle = intern ("circle");
+  staticpro (&Qcircle);
+  Qpoly = intern ("poly");
+  staticpro (&Qpoly);
   Qmessage_truncate_lines = intern ("message-truncate-lines");
   staticpro (&Qmessage_truncate_lines);
   Qcursor_in_non_selected_windows = intern ("cursor-in-non-selected-windows");
@@ -21128,6 +21466,12 @@
   staticpro (&Qbox);
   Qhollow = intern ("hollow");
   staticpro (&Qhollow);
+  Qhand = intern ("hand");
+  staticpro (&Qhand);
+  Qarrow = intern ("arrow");
+  staticpro (&Qarrow);
+  Qtext = intern ("text");
+  staticpro (&Qtext);
   Qrisky_local_variable = intern ("risky-local-variable");
   staticpro (&Qrisky_local_variable);
   Qinhibit_free_realized_faces = intern ("inhibit-free-realized-faces");
@@ -21181,10 +21525,11 @@
 The face used for trailing whitespace is `trailing-whitespace'.  */);
   Vshow_trailing_whitespace = Qnil;
 
-  DEFVAR_LISP ("show-text-cursor-in-void", &Vshow_text_cursor_in_void,
-    doc: /* Non-nil means show the text cursor in void text areas.
-The default is to show the non-text (typically arrow) cursor.  */);
-  Vshow_text_cursor_in_void = Qnil;
+  DEFVAR_LISP ("void-text-area-pointer", &Vvoid_text_area_pointer,
+    doc: /* The pointer shape to show in void text areas.
+Nil means to show the text pointer.  Other options are `arrow', `text',
+`hand', `vdrag', `hdrag', `modeline', and `hourglass'.  */); 
+  Vvoid_text_area_pointer = Qarrow;
 
   DEFVAR_LISP ("inhibit-redisplay", &Vinhibit_redisplay,
     doc: /* Non-nil means don't actually do any redisplay.