]> git.eshelyaron.com Git - emacs.git/commitdiff
(Qarrow, Qhand, Qtext, Qpointer): New variables for
authorKim F. Storm <storm@cua.dk>
Sun, 28 Dec 2003 00:12:27 +0000 (00:12 +0000)
committerKim F. Storm <storm@cua.dk>
Sun, 28 Dec 2003 00:12:27 +0000 (00:12 +0000)
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.

src/xdisp.c

index c2717552cc5340f7ddd8aaecd62d111241c252ed..8a6f36ef17bb5ec539b004219484b6a8b07ea49f 100644 (file)
@@ -242,6 +242,9 @@ Lisp_Object Qbuffer_position, Qposition, Qobject;
 /* 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 @@ int inhibit_eval_during_redisplay;
 
 /* 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 @@ extern Lisp_Object Qface, Qinvisible, Qwidth;
 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 @@ Lisp_Object Vshow_trailing_whitespace;
    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 Qtrailing_whitespace;
 
 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 @@ glyph_to_pixel_coords (w, hpos, vpos, frame_x, frame_y)
    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 @@ x_y_to_hpos_vpos (w, x, y, hpos, vpos, area, buffer_only_p)
   /* Find glyph containing X.  */
   glyph = row->glyphs[*area];
   end = glyph + row->used[*area];
-  while (glyph < end)
+  x -= x0;
+  while (glyph < end && x >= glyph->pixel_width)
     {
-      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 -= 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 @@ get_glyph_string_clip_rect (s, nr)
 
   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);
+      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;
+       }
     }
-#endif
 
 #ifdef CONVERT_FROM_XRECT
   CONVERT_FROM_XRECT (r, *nr);
@@ -8279,7 +8292,7 @@ build_desired_tool_bar_string (f)
       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 @@ get_tool_bar_item (f, x, y, glyph, hpos, vpos, prop_idx)
   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 @@ append_glyph (it)
       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 @@ append_composite_glyph (it)
       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 @@ produce_image_glyph (it)
 {
   struct image *img;
   struct face *face;
+  int face_ascent, glyph_ascent;
 
   xassert (it->what == IT_IMAGE);
 
@@ -17778,10 +17796,15 @@ produce_image_glyph (it)
   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 @@ produce_image_glyph (it)
          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 @@ append_stretch_glyph (it, object, width, height, ascent)
       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 @@ fast_find_string_pos (w, pos, object, hpos, vpos, x, y, right_p)
 }
 
 
+/* 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 @@ note_mode_line_or_margin_highlight (w, x, y, area)
   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 @@ note_mode_line_or_margin_highlight (w, x, y, area)
          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 @@ note_mode_line_or_margin_highlight (w, x, y, area)
            cursor = dpyinfo->vertical_scroll_bar_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;
 
-  rif->define_frame_cursor (f, cursor);
+         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 @@ note_mouse_highlight (f, x, y)
   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 @@ note_mouse_highlight (f, x, y)
     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 @@ note_mouse_highlight (f, x, y)
       && 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 @@ note_mouse_highlight (f, x, y)
       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 @@ note_mouse_highlight (f, x, y)
        {
          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 @@ note_mouse_highlight (f, x, y)
       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 @@ note_mouse_highlight (f, x, y)
     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 @@ note_mouse_highlight (f, x, y)
          }
       }
 
+      /* 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 @@ note_mouse_highlight (f, x, y)
 
  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 @@ syms_of_xdisp ()
 #endif
 #ifdef HAVE_WINDOW_SYSTEM
   defsubr (&Stool_bar_lines_needed);
+  defsubr (&Slookup_image_map);
 #endif
   defsubr (&Sformat_mode_line);
 
@@ -21076,16 +21406,14 @@ syms_of_xdisp ()
   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 @@ syms_of_xdisp ()
   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 @@ syms_of_xdisp ()
   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 @@ wide as that tab on the display.  */);
 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.