Exemplo n.º 1
0
static int
gtk_get_button_size (struct frame *f, Lisp_Object window,
		     struct toolbar_button *tb, int vert, int pos)
{
  int shadow_thickness = 2;
  int size;

  if (tb->blank)
    {
      if (!NILP (tb->down_glyph))
	size = XINT (tb->down_glyph);
      else
	size = DEFAULT_TOOLBAR_BLANK_SIZE;
    }
  else
    {
      struct window *w = XWINDOW (window);
      Lisp_Object glyph = get_toolbar_button_glyph (w, tb);

      /* Unless, of course, the user has done something stupid like
         change the glyph out from under us.  Use a blank placeholder
         in that case. */
      if (NILP (glyph))
	return XINT (f->toolbar_size[pos]);

      if (vert)
	size = glyph_height (glyph, window);
      else
	size = glyph_width (glyph, window);
    }

  if (!size)
    {
      /* If the glyph doesn't have a size we'll insert a blank
         placeholder instead. */
      return XINT (f->toolbar_size[pos]);
    }

  size += shadow_thickness * 2;

  return (size);
}
Exemplo n.º 2
0
Arquivo: w32menu.c Projeto: 0xAX/emacs
void
set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
{
  HMENU menubar_widget = f->output_data.w32->menubar_widget;
  Lisp_Object items;
  widget_value *wv, *first_wv, *prev_wv = 0;
  int i, last_i;
  int *submenu_start, *submenu_end;
  int *submenu_top_level_items, *submenu_n_panes;

  /* We must not change the menubar when actually in use.  */
  if (f->output_data.w32->menubar_active)
    return;

  XSETFRAME (Vmenu_updating_frame, f);

  if (! menubar_widget)
    deep_p = true;

  if (deep_p)
    {
      /* Make a widget-value tree representing the entire menu trees.  */

      struct buffer *prev = current_buffer;
      Lisp_Object buffer;
      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
      int previous_menu_items_used = f->menu_bar_items_used;
      Lisp_Object *previous_items
	= (Lisp_Object *) alloca (previous_menu_items_used
				  * word_size);

      /* If we are making a new widget, its contents are empty,
	 do always reinitialize them.  */
      if (! menubar_widget)
	previous_menu_items_used = 0;

      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
      specbind (Qinhibit_quit, Qt);
      /* Don't let the debugger step into this code
	 because it is not reentrant.  */
      specbind (Qdebug_on_next_call, Qnil);

      record_unwind_save_match_data ();

      if (NILP (Voverriding_local_map_menu_flag))
	{
	  specbind (Qoverriding_terminal_local_map, Qnil);
	  specbind (Qoverriding_local_map, Qnil);
	}

      set_buffer_internal_1 (XBUFFER (buffer));

      /* Run the hooks.  */
      safe_run_hooks (Qactivate_menubar_hook);
      safe_run_hooks (Qmenu_bar_update_hook);
      fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));

      items = FRAME_MENU_BAR_ITEMS (f);

      /* Save the frame's previous menu bar contents data.  */
      if (previous_menu_items_used)
	memcpy (previous_items, XVECTOR (f->menu_bar_vector)->contents,
		previous_menu_items_used * word_size);

      /* Fill in menu_items with the current menu bar contents.
	 This can evaluate Lisp code.  */
      save_menu_items ();

      menu_items = f->menu_bar_vector;
      menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
      submenu_start = (int *) alloca (ASIZE (items) * sizeof (int));
      submenu_end = (int *) alloca (ASIZE (items) * sizeof (int));
      submenu_n_panes = (int *) alloca (ASIZE (items) * sizeof (int));
      submenu_top_level_items = (int *) alloca (ASIZE (items) * sizeof (int));
      init_menu_items ();
      for (i = 0; i < ASIZE (items); i += 4)
	{
	  Lisp_Object key, string, maps;

	  last_i = i;

	  key = AREF (items, i);
	  string = AREF (items, i + 1);
	  maps = AREF (items, i + 2);
	  if (NILP (string))
	    break;

	  submenu_start[i] = menu_items_used;

	  menu_items_n_panes = 0;
	  submenu_top_level_items[i]
	    = parse_single_submenu (key, string, maps);
	  submenu_n_panes[i] = menu_items_n_panes;

	  submenu_end[i] = menu_items_used;
	}

      finish_menu_items ();

      /* Convert menu_items into widget_value trees
	 to display the menu.  This cannot evaluate Lisp code.  */

      wv = make_widget_value ("menubar", NULL, true, Qnil);
      wv->button_type = BUTTON_TYPE_NONE;
      first_wv = wv;

      for (i = 0; i < last_i; i += 4)
	{
	  menu_items_n_panes = submenu_n_panes[i];
	  wv = digest_single_submenu (submenu_start[i], submenu_end[i],
				      submenu_top_level_items[i]);
	  if (prev_wv)
	    prev_wv->next = wv;
	  else
	    first_wv->contents = wv;
	  /* Don't set wv->name here; GC during the loop might relocate it.  */
	  wv->enabled = true;
	  wv->button_type = BUTTON_TYPE_NONE;
	  prev_wv = wv;
	}

      set_buffer_internal_1 (prev);

      /* If there has been no change in the Lisp-level contents
	 of the menu bar, skip redisplaying it.  Just exit.  */

      for (i = 0; i < previous_menu_items_used; i++)
	if (menu_items_used == i
	    || (!EQ (previous_items[i], AREF (menu_items, i))))
	  break;
      if (i == menu_items_used && i == previous_menu_items_used && i != 0)
	{
	  free_menubar_widget_value_tree (first_wv);
	  discard_menu_items ();
          unbind_to (specpdl_count, Qnil);
	  return;
	}

      fset_menu_bar_vector (f, menu_items);
      f->menu_bar_items_used = menu_items_used;

      /* This undoes save_menu_items.  */
      unbind_to (specpdl_count, Qnil);

      /* Now GC cannot happen during the lifetime of the widget_value,
	 so it's safe to store data from a Lisp_String, as long as
	 local copies are made when the actual menu is created.
	 Windows takes care of this for normal string items, but
	 not for owner-drawn items or additional item-info.  */
      wv = first_wv->contents;
      for (i = 0; i < ASIZE (items); i += 4)
	{
	  Lisp_Object string;
	  string = AREF (items, i + 1);
	  if (NILP (string))
	    break;
	  wv->name = SSDATA (string);
	  update_submenu_strings (wv->contents);
	  wv = wv->next;
	}
    }
  else
    {
      /* Make a widget-value tree containing
	 just the top level menu bar strings.  */

      wv = make_widget_value ("menubar", NULL, true, Qnil);
      wv->button_type = BUTTON_TYPE_NONE;
      first_wv = wv;

      items = FRAME_MENU_BAR_ITEMS (f);
      for (i = 0; i < ASIZE (items); i += 4)
	{
	  Lisp_Object string;

	  string = AREF (items, i + 1);
	  if (NILP (string))
	    break;

	  wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
	  wv->button_type = BUTTON_TYPE_NONE;
	  /* This prevents lwlib from assuming this
	     menu item is really supposed to be empty.  */
	  /* The EMACS_INT cast avoids a warning.
	     This value just has to be different from small integers.  */
	  wv->call_data = (void *) (EMACS_INT) (-1);

	  if (prev_wv)
	    prev_wv->next = wv;
	  else
	    first_wv->contents = wv;
	  prev_wv = wv;
	}

      /* Forget what we thought we knew about what is in the
	 detailed contents of the menu bar menus.
	 Changing the top level always destroys the contents.  */
      f->menu_bar_items_used = 0;
    }

  /* Create or update the menu bar widget.  */

  block_input ();

  if (menubar_widget)
    {
      /* Empty current menubar, rather than creating a fresh one.  */
      while (DeleteMenu (menubar_widget, 0, MF_BYPOSITION))
	;
    }
  else
    {
      menubar_widget = CreateMenu ();
    }
  fill_in_menu (menubar_widget, first_wv->contents);

  free_menubar_widget_value_tree (first_wv);

  {
    HMENU old_widget = f->output_data.w32->menubar_widget;

    f->output_data.w32->menubar_widget = menubar_widget;
    SetMenu (FRAME_W32_WINDOW (f), f->output_data.w32->menubar_widget);
    /* Causes flicker when menu bar is updated
    DrawMenuBar (FRAME_W32_WINDOW (f)); */

    /* Force the window size to be recomputed so that the frame's text
       area remains the same, if menubar has just been created.  */
    if (old_widget == NULL)
      {
	windows_or_buffers_changed = 23;
	adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines);
      }
  }

  unblock_input ();
}
Exemplo n.º 3
0
int
column_at_point (struct buffer *buf, Bufpos init_pos, int cur_col)
{
  int col;
  int tab_seen;
  int tab_width = XINT (buf->tab_width);
  int post_tab;
  Bufpos pos = init_pos;
  Emchar c;

  if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
  col = tab_seen = post_tab = 0;

  while (1)
    {
      if (pos <= BUF_BEGV (buf))
	break;

      pos--;
      c = BUF_FETCH_CHAR (buf, pos);
      if (c == '\t')
	{
	  if (tab_seen)
	    col = ((col + tab_width) / tab_width) * tab_width;

	  post_tab += col;
	  col = 0;
	  tab_seen = 1;
	}
      else if (c == '\n' ||
	       (EQ (buf->selective_display, Qt) && c == '\r'))
	break;
      else
	{
	  /* #### This needs updating to handle the new redisplay. */
	  /* #### FSFmacs looks at ctl_arrow, display tables.
	     We need to do similar. */
#if 0
	  displayed_glyphs = glyphs_from_bufpos (sel_frame, buf,
						 XWINDOW (selected_window),
						 pos, dp, 0, col, 0, 0, 0);
	  col += (displayed_glyphs->columns
		  - (displayed_glyphs->begin_columns
		     + displayed_glyphs->end_columns));
#else /* XEmacs */
#ifdef MULE
	  col += CHAR_COLUMNS (c);
#else
	  col ++;
#endif /* MULE */
#endif /* XEmacs */
	}
    }

  if (tab_seen)
    {
      col = ((col + tab_width) / tab_width) * tab_width;
      col += post_tab;
    }

  if (cur_col)
    {
      last_known_column_buffer = buf;
      last_known_column = col;
      last_known_column_point = init_pos;
      last_known_column_modified = BUF_MODIFF (buf);
    }

  return col;
}
Exemplo n.º 4
0
/*
 * This is the only callback provided for horizontal scrollbars.  It
 * should be able to handle all of the scrollbar events in
 * scroll_action (see lwlib.h).  The client data will be of type
 * scroll_event (see lwlib.h). */
static void
x_update_horizontal_scrollbar_callback (Widget widget, LWLIB_ID id,
					XtPointer client_data)
{
  scroll_event *data = (scroll_event *) client_data;
  struct device *d = get_device_from_display (XtDisplay (widget));
  struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
  Lisp_Object win, frame;
  struct window_mirror *mirror;

  if (!f)
    return;

  mirror = find_scrollbar_window_mirror (f, id);
  if (!mirror)
    return;

  win = real_window (mirror, 1);

  if (NILP (win))
    return;
  frame = WINDOW_FRAME (XWINDOW (win));

  /* It seems that this is necessary whenever signal_special_Xt_user_event()
     is called.  #### Why??? */
  DEVICE_X_MOUSE_TIMESTAMP (d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (d);

  switch (data->action)
    {
    case SCROLLBAR_LINE_UP:
      signal_special_Xt_user_event (frame, Qscrollbar_char_left, win);
      break;
    case SCROLLBAR_LINE_DOWN:
      signal_special_Xt_user_event (frame, Qscrollbar_char_right, win);
      break;
    case SCROLLBAR_PAGE_UP:
      signal_special_Xt_user_event (frame, Qscrollbar_page_left, win);
      break;
    case SCROLLBAR_PAGE_DOWN:
      signal_special_Xt_user_event (frame, Qscrollbar_page_right, win);
      break;
    case SCROLLBAR_TOP:
      signal_special_Xt_user_event (frame, Qscrollbar_to_left, win);
      break;
    case SCROLLBAR_BOTTOM:
      signal_special_Xt_user_event (frame, Qscrollbar_to_right, win);
      break;
    case SCROLLBAR_CHANGE:
      inhibit_slider_size_change = 0;
      break;
    case SCROLLBAR_DRAG:
      inhibit_slider_size_change = 1;
      /* #### Fix the damn toolkit code so they all work the same way.
         Lucid is the one mostly wrong.*/
#if defined (LWLIB_SCROLLBARS_LUCID) || defined (LWLIB_SCROLLBARS_ATHENA3D)
      signal_special_Xt_user_event (frame, Qscrollbar_horizontal_drag,
				    (Fcons
				     (win, make_int (data->slider_value))));
#else
      signal_special_Xt_user_event (frame, Qscrollbar_horizontal_drag,
				    (Fcons
				     (win,
				      make_int (data->slider_value - 1))));
#endif
      break;
    default:
      break;
    }
}
Exemplo n.º 5
0
/*
 * This is the only callback provided for vertical scrollbars.  It
 * should be able to handle all of the scrollbar events in
 * scroll_action (see lwlib.h).  The client data will be of type
 * scroll_event (see lwlib.h). */
static void
x_update_vertical_scrollbar_callback (Widget widget, LWLIB_ID id,
				      XtPointer client_data)
{
  /* This function can GC */
  scroll_event *data = (scroll_event *) client_data;
  struct device *d = get_device_from_display (XtDisplay (widget));
  struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
  Lisp_Object win, frame;
  struct scrollbar_instance *instance;
  struct window_mirror *mirror;

  if (!f)
    return;

  mirror = find_scrollbar_window_mirror (f, id);
  if (!mirror)
    return;

  win = real_window (mirror, 1);

  if (NILP (win))
    return;
  instance = mirror->scrollbar_vertical_instance;
  frame = WINDOW_FRAME (XWINDOW (win));

  /* It seems that this is necessary whenever signal_special_Xt_user_event()
     is called.  #### Why??? */
  DEVICE_X_MOUSE_TIMESTAMP (d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (d);

  switch (data->action)
    {
    case SCROLLBAR_LINE_UP:
      signal_special_Xt_user_event (frame, Qscrollbar_line_up, win);
      break;

    case SCROLLBAR_LINE_DOWN:
      signal_special_Xt_user_event (frame, Qscrollbar_line_down, win);
      break;

      /* The Athena scrollbar paging behavior is that of xterms.
         Depending on where you click the size of the page varies.
         Motif always does a standard Emacs page. */
    case SCROLLBAR_PAGE_UP:
#if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
    !defined (LWLIB_SCROLLBARS_ATHENA3D)
      {
	double tmp = ((double) data->slider_value /
		      (double) SCROLLBAR_X_POS_DATA(instance).scrollbar_height);
	double line = tmp *
	  (double) window_displayed_height (XWINDOW (win));

	if (line > -1.0)
	  line = -1.0;
	signal_special_Xt_user_event (frame, Qscrollbar_page_up,
				      Fcons (win, make_int ((int) line)));
      }
#else
      signal_special_Xt_user_event (frame, Qscrollbar_page_up,
				    Fcons (win, Qnil));
#endif
      break;

    case SCROLLBAR_PAGE_DOWN:
#if !defined (LWLIB_SCROLLBARS_MOTIF) && !defined (LWLIB_SCROLLBARS_LUCID) && \
    !defined (LWLIB_SCROLLBARS_ATHENA3D)
      {
	double tmp = ((double) data->slider_value /
		      (double) SCROLLBAR_X_POS_DATA(instance).scrollbar_height);
	double line = tmp *
	  (double) window_displayed_height (XWINDOW (win));

	if (SCROLLBAR_X_POS_DATA(instance).maximum >
	    (SCROLLBAR_X_POS_DATA(instance).slider_size + SCROLLBAR_X_POS_DATA(instance).slider_position))
	  {
	    if (line < 1.0)
	      line = 1.0;
	    signal_special_Xt_user_event (frame, Qscrollbar_page_down,
					  Fcons (win,
						 make_int ((int) line)));
	  }
      }
#else
      signal_special_Xt_user_event (frame, Qscrollbar_page_down,
				    Fcons (win, Qnil));
#endif
      break;

    case SCROLLBAR_TOP:
      signal_special_Xt_user_event (frame, Qscrollbar_to_top, win);
      break;

    case SCROLLBAR_BOTTOM:
      signal_special_Xt_user_event (frame, Qscrollbar_to_bottom, win);
      break;


    case SCROLLBAR_CHANGE:
      inhibit_slider_size_change = 0;
#if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
      vertical_drag_in_progress = 0;
      SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) = data->slider_value;
      SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance) =
	XINT (Fwindow_start (win));
#else
      stupid_vertical_scrollbar_drag_hack = 0;
#endif
      break;

    case SCROLLBAR_DRAG:
      {
	int value;

	inhibit_slider_size_change = 1;

#if defined (LWLIB_SCROLLBARS_MOTIF) || defined (LWLIB_SCROLLBARS_LUCID)
	/* Doing drags with Motif-like scrollbars is a mess, since we
	   want to avoid having the window position jump when you
	   first grab the scrollbar, but we also want to ensure that
	   you can scroll all the way to the top or bottom of the
	   buffer.  This can all be replaced with something sane when
	   we get line-based scrolling. */

	vertical_drag_in_progress = 1;

	if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) < 0)
	  {
	    SCROLLBAR_X_VDRAG_ORIG_VALUE (instance) = data->slider_value;
	    SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance) =
	      XINT (Fwindow_start (win));
	  }

	/* Could replace this piecewise linear scrolling with a
	   quadratic through the three points, but I'm not sure that
	   would feel any nicer in practice. */
	if (data->slider_value < SCROLLBAR_X_VDRAG_ORIG_VALUE (instance))
	  {
	    /* We've dragged up; slide linearly from original position to
	       window-start=data.minimum, slider-value=data.minimum. */

	    if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
		<= SCROLLBAR_X_POS_DATA (instance).minimum)
	      {
		/* shouldn't get here, but just in case */
		value = SCROLLBAR_X_POS_DATA (instance).minimum;
	      }
	    else
	      {
		value = (int)
		  (SCROLLBAR_X_POS_DATA (instance).minimum
		   + (((double)
		       (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance)
			- SCROLLBAR_X_POS_DATA (instance).minimum)
		       * (data->slider_value -
			  SCROLLBAR_X_POS_DATA (instance).minimum))
		      / (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
			 - SCROLLBAR_X_POS_DATA (instance).minimum)));
	      }
	  }
	else
	  {
	    /* We've dragged down; slide linearly from original position to
	       window-start=data.maximum, slider-value=data.maximum. */

	    if (SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)
		>= (SCROLLBAR_X_POS_DATA (instance).maximum -
		    SCROLLBAR_X_POS_DATA (instance).slider_size))
	      {
		/* avoid divide by zero */
		value = SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance);
	      }
	    else
	      {
		value = (int)
		  (SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance)
		   + (((double)
		       (SCROLLBAR_X_POS_DATA (instance).maximum
			- SCROLLBAR_X_VDRAG_ORIG_WINDOW_START (instance))
		       * (data->slider_value
			  - SCROLLBAR_X_VDRAG_ORIG_VALUE (instance)))
		      / (SCROLLBAR_X_POS_DATA (instance).maximum
			 - SCROLLBAR_X_POS_DATA (instance).slider_size
			 - SCROLLBAR_X_VDRAG_ORIG_VALUE (instance))));
	      }
	  }
#else
	stupid_vertical_scrollbar_drag_hack = 0;
	value = data->slider_value;
#endif

	if (value >= SCROLLBAR_X_POS_DATA (instance).maximum)
	  value = SCROLLBAR_X_POS_DATA (instance).maximum - 1;
	if (value < SCROLLBAR_X_POS_DATA (instance).minimum)
	  value = SCROLLBAR_X_POS_DATA (instance).minimum;

	signal_special_Xt_user_event (frame, Qscrollbar_vertical_drag,
				      Fcons (win, make_int (value)));
      }
      break;

    }
}
Exemplo n.º 6
0
static struct window_mirror *
x_scrollbar_loop (enum x_scrollbar_loop type, Lisp_Object window,
		  struct window_mirror *mir,
		  LWLIB_ID id, Window x_win)
{
  struct window_mirror *retval = NULL;

  while (mir)
    {
      struct scrollbar_instance *vinstance = mir->scrollbar_vertical_instance;
      struct scrollbar_instance *hinstance = mir->scrollbar_horizontal_instance;
      struct window *w = XWINDOW (window);

      if (mir->vchild)
	retval = x_scrollbar_loop (type, w->vchild, mir->vchild, id, x_win);
      else if (mir->hchild)
	retval = x_scrollbar_loop (type, w->hchild, mir->hchild, id, x_win);
      if (retval)
	return retval;

      if (hinstance || vinstance)
	{
	  switch (type)
	    {
	    case X_FIND_SCROLLBAR_WINDOW_MIRROR:
	      if ((vinstance && SCROLLBAR_X_ID (vinstance) == id) ||
		  (hinstance && SCROLLBAR_X_ID (hinstance) == id))
		return mir;
	      break;
	    case X_UPDATE_FRAME_SCROLLBARS:
	      if (!mir->vchild && !mir->hchild)
		update_window_scrollbars (w, mir, 1, 0);
	      break;
	    case X_SET_SCROLLBAR_POINTER:
	      if (!mir->vchild && !mir->hchild)
		{
                  Widget widget;

		  widget = SCROLLBAR_X_WIDGET (hinstance);
		  if (widget && XtIsManaged (widget))
		    update_one_widget_scrollbar_pointer (w, widget);

		  widget = SCROLLBAR_X_WIDGET (vinstance);
		  if (widget && XtIsManaged (widget))
		    update_one_widget_scrollbar_pointer (w, widget);
		}
	      break;
	    case X_WINDOW_IS_SCROLLBAR:
	      if (!mir->vchild && !mir->hchild)
		{
		  Widget widget;

		  widget = SCROLLBAR_X_WIDGET (hinstance);
		  if (widget && XtIsManaged (widget) &&
		      XtWindow (widget) == x_win)
		    return (struct window_mirror *) 1;

		  widget = SCROLLBAR_X_WIDGET (vinstance);
		  if (widget && XtIsManaged (widget) &&
		      XtWindow (widget) == x_win)
		    return (struct window_mirror *) 1;
		}
	      break;
	    default:
	      ABORT ();
	    }
	}

      mir = mir->next;
      window = w->next;
    }

  return NULL;
}
Exemplo n.º 7
0
Lisp_Object
current_frame_menubar (const struct frame* f)
{
  struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
  return symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
}
Exemplo n.º 8
0
static void
gtk_output_toolbar_button (struct frame *f, Lisp_Object button)
{
  int shadow_thickness = 2;
  int x_adj, y_adj, width_adj, height_adj;
  GdkWindow *x_win = FRAME_GTK_TEXT_WIDGET (f)->window;
  GdkGC *background_gc = get_toolbar_gc (f);
  Lisp_Object instance, frame, window, glyph;
  struct toolbar_button *tb = XTOOLBAR_BUTTON (button);
  struct Lisp_Image_Instance *p;
  struct window *w;
  int vertical = tb->vertical;
  int border_width = tb->border_width;

  if (vertical)
    {
      x_adj = border_width;
      width_adj = - 2 * border_width;
      y_adj = 0;
      height_adj = 0;
    }
  else
    {
      x_adj = 0;
      width_adj = 0;
      y_adj = border_width;
      height_adj = - 2 * border_width;
    }

  XSETFRAME (frame, f);
  window = FRAME_LAST_NONMINIBUF_WINDOW (f);
  w = XWINDOW (window);

  glyph = get_toolbar_button_glyph (w, tb);

  if (tb->enabled)
    {
      if (tb->down)
	{
	  shadow_thickness = -2;
	}
      else
	{
	  shadow_thickness = 2;
	}
    }
  else
    {
      shadow_thickness = 0;
    }

  background_gc = get_toolbar_gc (f);

  /* Clear the entire area. */
  gdk_draw_rectangle (x_win, background_gc, TRUE,
		      tb->x + x_adj,
		      tb->y + y_adj,
		      tb->width + width_adj,
		      tb->height + height_adj);

  /* Draw the outline. */
  if (shadow_thickness)
    gtk_output_shadows (f, tb->x + x_adj, tb->y + y_adj,
			tb->width + width_adj, tb->height + height_adj,
			shadow_thickness);

  /* Do the border. */
  gdk_draw_rectangle (x_win, background_gc, TRUE, tb->x, tb->y,
		      (vertical ? border_width : tb->width),
		      (vertical ? tb->height : border_width));

  gdk_draw_rectangle (x_win, background_gc, TRUE,
		      (vertical ? tb->x + tb->width - border_width : tb->x),
		      (vertical ? tb->y : tb->y + tb->height - border_width),
		      (vertical ? border_width : tb->width),
		      (vertical ? tb->height : border_width));

  background_gc = get_toolbar_gc (f);

  /* #### It is currently possible for users to trash us by directly
     changing the toolbar glyphs.  Avoid crashing in that case. */
  if (GLYPHP (glyph))
    instance = glyph_image_instance (glyph, window, ERROR_ME_NOT, 1);
  else
    instance = Qnil;

  if (IMAGE_INSTANCEP (instance))
    {
      int width = tb->width + width_adj - shadow_thickness * 2;
      int height = tb->height + height_adj - shadow_thickness * 2;
      int x_offset = x_adj + shadow_thickness;
      int y_offset = y_adj + shadow_thickness;

      p = XIMAGE_INSTANCE (instance);

      if (IMAGE_INSTANCE_PIXMAP_TYPE_P (p))
	{
	  if (width > (int) IMAGE_INSTANCE_PIXMAP_WIDTH (p))
	    {
	      x_offset += ((int) (width - IMAGE_INSTANCE_PIXMAP_WIDTH (p))
			   / 2);
	      width = IMAGE_INSTANCE_PIXMAP_WIDTH (p);
	    }
	  if (height > (int) IMAGE_INSTANCE_PIXMAP_HEIGHT (p))
	    {
	      y_offset += ((int) (height - IMAGE_INSTANCE_PIXMAP_HEIGHT (p))
			   / 2);
	      height = IMAGE_INSTANCE_PIXMAP_HEIGHT (p);
	    }

	  gtk_output_gdk_pixmap (f, XIMAGE_INSTANCE (instance), tb->x + x_offset,
				 tb->y + y_offset, 0, 0, 0, 0, width, height,
				 0, 0, 0, background_gc);
	}
      else if (IMAGE_INSTANCE_TYPE (p) == IMAGE_TEXT)
	{
	  /* #### We need to make the face used configurable. */
	  struct face_cachel *cachel =
	    WINDOW_FACE_CACHEL (w, DEFAULT_INDEX);
	  struct display_line dl;
	  Lisp_Object string = IMAGE_INSTANCE_TEXT_STRING (p);
	  unsigned char charsets[NUM_LEADING_BYTES];
	  Emchar_dynarr *buf;
	  struct font_metric_info fm;

	  /* This could be true if we were called via the Expose event
             handler.  Mark the button as dirty and return
             immediately. */
	  if (f->window_face_cache_reset)
	    {
	      tb->dirty = 1;
	      MARK_TOOLBAR_CHANGED;
	      return;
	    }
	  buf = Dynarr_new (Emchar);
	  convert_bufbyte_string_into_emchar_dynarr
	    (XSTRING_DATA (string), XSTRING_LENGTH (string), buf);
	  find_charsets_in_emchar_string (charsets, Dynarr_atp (buf, 0),
					  Dynarr_length (buf));
	  ensure_face_cachel_complete (cachel, window, charsets);
	  face_cachel_charset_font_metric_info (cachel, charsets, &fm);

	  dl.ascent = fm.ascent;
	  dl.descent = fm.descent;
	  dl.ypos = tb->y + y_offset + fm.ascent;

	  if (fm.ascent + fm.descent <= height)
	    {
	      dl.ypos += (height - fm.ascent - fm.descent) / 2;
	      dl.clip = 0;
	    }
	  else
	    {
	      dl.clip = fm.ascent + fm.descent - height;
	    }

	  gtk_output_string (w, &dl, buf, tb->x + x_offset, 0, 0, width,
			     DEFAULT_INDEX, 0, 0, 0, 0);
	  Dynarr_free (buf);
	}

      /* We silently ignore the image if it isn't a pixmap or text. */
    }

  tb->dirty = 0;
}