diff src/glyphs-widget.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 9d177e8d4150
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/glyphs-widget.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,925 @@
+/* Widget-specific glyph objects.
+   Copyright (C) 1998, 1999 Andy Piper.
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* written by Andy Piper <andy@xemacs.org> */
+
+#include <config.h>
+#include "lisp.h"
+#include "lstream.h"
+#include "console.h"
+#include "device.h"
+#include "faces.h"
+#include "glyphs.h"
+#include "objects.h"
+#include "bytecode.h"
+#include "window.h"
+#include "buffer.h"
+#include "frame.h"
+#include "insdel.h"
+#include "opaque.h"
+
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (button);
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (combo_box);
+Lisp_Object Qcombo_box;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (edit_field);
+Lisp_Object Qedit_field;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (scrollbar);
+Lisp_Object Qscrollbar;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (widget);
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (label);
+Lisp_Object Qlabel;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (progress_gauge);
+Lisp_Object Qprogress_gauge;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (tree_view);
+Lisp_Object Qtree_view;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (tab_control);
+Lisp_Object Qtab_control;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (layout);
+Lisp_Object Qlayout;
+
+Lisp_Object Q_descriptor, Q_height, Q_width, Q_properties, Q_items;
+Lisp_Object Q_image, Q_text, Q_percent, Q_orientation, Q_justify, Q_border;
+Lisp_Object Qetched_in, Qetched_out, Qbevel_in, Qbevel_out;
+
+#define WIDGET_BORDER_HEIGHT 4
+#define WIDGET_BORDER_WIDTH 4
+
+#ifdef DEBUG_WIDGETS
+int debug_widget_instances;
+#endif
+
+/* TODO:
+   - more complex controls.
+   - tooltips for controls.
+ */
+
+/* In windows normal windows work in pixels, dialog boxes work in
+   dialog box units. Why? sigh. We could reuse the metrics for dialogs
+   if this were not the case. As it is we have to position things
+   pixel wise. I'm not even sure that X has this problem at least for
+   buttons in groups. */
+Lisp_Object
+widget_face_font_info (Lisp_Object domain, Lisp_Object face,
+		       int *height, int *width)
+{
+  Lisp_Object font_instance = FACE_FONT (face, domain, Vcharset_ascii);
+
+  if (height)
+    *height = XFONT_INSTANCE (font_instance)->height;
+  if (width)
+    *width = XFONT_INSTANCE (font_instance)->width;
+  
+  return font_instance;
+}
+
+void
+widget_text_to_pixel_conversion (Lisp_Object domain, Lisp_Object face,
+				 int th, int tw,
+				 int* height, int* width)
+{
+  int ch=0, cw=0;
+  widget_face_font_info (domain, face, &ch, &cw);
+  if (height)
+    *height = th * ch + 2 * WIDGET_BORDER_HEIGHT;
+  if (width)
+    *width = tw * cw + 2 * WIDGET_BORDER_WIDTH;
+}
+
+static int
+widget_possible_dest_types (void)
+{
+  return IMAGE_WIDGET_MASK;
+}
+
+static void
+check_valid_glyph_or_instantiator (Lisp_Object data)
+{
+  Lisp_Object glyph = data;
+  if (SYMBOLP (data))
+    glyph = XSYMBOL (data)->value;
+
+  if (IMAGE_INSTANCEP (glyph))
+    CHECK_IMAGE_INSTANCE (glyph);
+  else if (!CONSP (glyph) && !VECTORP (glyph))
+    CHECK_BUFFER_GLYPH (glyph);
+}
+
+static void
+check_valid_orientation (Lisp_Object data)
+{
+  if (!EQ (data, Qhorizontal)
+      &&
+      !EQ (data, Qvertical))
+    signal_simple_error ("unknown orientation for layout", data);
+}
+
+static void
+check_valid_justification (Lisp_Object data)
+{
+  if (!EQ (data, Qleft) && !EQ (data, Qright) && !EQ (data, Qcenter))
+    signal_simple_error ("unknown justification for layout", data);
+}
+
+static void
+check_valid_border (Lisp_Object data)
+{
+  if (!EQ (data, Qt) && !EQ (data, Qetched_in) && !EQ (data, Qetched_out)
+      && !EQ (data, Qbevel_in) && !EQ (data, Qbevel_out)
+      && !GLYPHP (data) && !VECTORP (data))
+    signal_simple_error ("unknown border style for layout", data);
+}
+
+static void
+check_valid_anything (Lisp_Object data)
+{
+}
+
+static void
+check_valid_callback (Lisp_Object data)
+{
+    if (!SYMBOLP (data)
+	&& !COMPILED_FUNCTIONP (data)
+	&& !CONSP (data))
+    {
+	signal_simple_error (":callback must be a function or expression", data);
+    }
+}
+
+static void
+check_valid_symbol (Lisp_Object data)
+{
+    CHECK_SYMBOL (data);
+}
+
+static void
+check_valid_string_or_vector (Lisp_Object data)
+{
+    if (!STRINGP (data) && !VECTORP (data))
+	signal_simple_error (":descriptor must be a string or a vector", data);
+}
+
+void
+check_valid_item_list_1 (Lisp_Object items)
+{
+  Lisp_Object rest;
+
+  CHECK_LIST (items);
+  EXTERNAL_LIST_LOOP (rest, items)
+    {
+      if (STRINGP (XCAR (rest)))
+	CHECK_STRING (XCAR (rest));
+      else if (VECTORP (XCAR (rest)))
+	gui_parse_item_keywords (XCAR (rest));
+      else if (LISTP (XCAR (rest)))
+	check_valid_item_list_1 (XCAR (rest));
+      else
+	signal_simple_error ("Items must be vectors, lists or strings", items);
+    }
+}
+
+static void
+check_valid_item_list (Lisp_Object data)
+{
+  Lisp_Object items;
+
+  Fcheck_valid_plist (data);
+  items = Fplist_get (data, Q_items, Qnil);
+
+  check_valid_item_list_1 (items);
+}
+
+static void
+check_valid_glyph_or_instantiator_list (Lisp_Object data)
+{
+  Lisp_Object rest;
+
+  CHECK_LIST (data);
+  EXTERNAL_LIST_LOOP (rest, data)
+    {
+      check_valid_glyph_or_instantiator (XCAR (rest));
+    }
+}
+
+static Lisp_Object
+glyph_instantiator_to_glyph (Lisp_Object sym)
+{
+  /* This function calls lisp. */
+  Lisp_Object glyph = sym;
+  struct gcpro gcpro1;
+	  
+  GCPRO1 (glyph);
+  /* if we have a symbol get at the actual data */
+  if (SYMBOLP (glyph))
+    glyph = XSYMBOL (glyph)->value;
+	  
+  if (CONSP (glyph))
+    glyph = Feval (glyph);
+
+  /* Be really helpful to the user. */
+  if (VECTORP (glyph))
+    {
+      glyph = call1 (intern ("make-glyph"), glyph);
+    }
+
+  /* substitute the new glyph */
+  RETURN_UNGCPRO (glyph);
+}
+
+static void 
+substitute_keyword_value (Lisp_Object inst, Lisp_Object key, Lisp_Object val)
+{
+  int i;
+  /* substitute the new glyph */
+  for (i = 0; i < XVECTOR_LENGTH (inst); i++)
+    {
+      if (EQ (key, XVECTOR_DATA (inst)[i]))
+	{
+	  XVECTOR_DATA (inst)[i+1] = val;
+	  break;
+	}
+    }
+}
+
+/* wire widget property invocations to specific widgets ...  The
+ problem we are solving here is that when instantiators get converted
+ to instances they lose some type information (they just become
+ subwindows or widgets for example). For widgets we need to preserve
+ this type information so that we can do widget specific operations on
+ the instances. This is encoded in the widget type
+ field. widget_property gets invoked by decoding the primary type
+ (Qwidget), widget property then invokes based on the secondary type
+ (Qedit_field for example). It is debatable that we should wire things in this
+ generalised way rather than treating widgets specially in
+ image_instance_property. */
+static Lisp_Object 
+widget_property (Lisp_Object image_instance, Lisp_Object prop)
+{
+  struct Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
+  struct image_instantiator_methods* meths;
+
+  /* first see if its a general property ... */
+  if (!NILP (Fplist_member (IMAGE_INSTANCE_WIDGET_PROPS (ii), prop)))
+    return Fplist_get (IMAGE_INSTANCE_WIDGET_PROPS (ii), prop, Qnil);
+
+  /* .. then try device specific methods ... */
+  meths = decode_device_ii_format (IMAGE_INSTANCE_DEVICE (ii), 
+				   IMAGE_INSTANCE_WIDGET_TYPE (ii), 
+				   ERROR_ME_NOT);
+  if (meths && HAS_IIFORMAT_METH_P (meths, property))
+    return IIFORMAT_METH (meths, property, (image_instance, prop));
+  /* ... then format specific methods ... */
+  meths = decode_device_ii_format (Qnil, IMAGE_INSTANCE_WIDGET_TYPE (ii), 
+				   ERROR_ME_NOT);
+  if (meths && HAS_IIFORMAT_METH_P (meths, property))
+    return IIFORMAT_METH (meths, property, (image_instance, prop));
+  /* ... then fail */
+  return Qunbound;
+}
+
+static Lisp_Object 
+widget_set_property (Lisp_Object image_instance, Lisp_Object prop, Lisp_Object val)
+{
+  struct Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
+  struct image_instantiator_methods* meths;
+  Lisp_Object ret;
+
+  /* try device specific methods first ... */
+  meths = decode_device_ii_format (IMAGE_INSTANCE_DEVICE (ii), 
+				   IMAGE_INSTANCE_WIDGET_TYPE (ii), 
+				   ERROR_ME_NOT);
+  if (meths && HAS_IIFORMAT_METH_P (meths, set_property)
+      &&
+      !UNBOUNDP (ret = 
+		 IIFORMAT_METH (meths, set_property, (image_instance, prop, val))))
+    {
+      return ret;
+    }
+  /* ... then format specific methods ... */
+  meths = decode_device_ii_format (Qnil, IMAGE_INSTANCE_WIDGET_TYPE (ii), 
+				   ERROR_ME_NOT);
+  if (meths && HAS_IIFORMAT_METH_P (meths, set_property)
+      &&
+      !UNBOUNDP (ret = 
+		 IIFORMAT_METH (meths, set_property, (image_instance, prop, val))))
+    {
+      return ret;
+    }
+  /* we didn't do any device specific properties, so shove the property in our plist */
+  IMAGE_INSTANCE_WIDGET_PROPS (ii)
+    = Fplist_put (IMAGE_INSTANCE_WIDGET_PROPS (ii), prop, val);
+  return val;
+}
+
+static void
+widget_validate (Lisp_Object instantiator)
+{
+  Lisp_Object desc = find_keyword_in_vector (instantiator, Q_descriptor);
+
+  if (NILP (desc))
+    signal_simple_error ("Must supply :descriptor", instantiator);
+
+  if (VECTORP (desc))
+    gui_parse_item_keywords (desc);
+
+  if (!NILP (find_keyword_in_vector (instantiator, Q_width))
+      && !NILP (find_keyword_in_vector (instantiator, Q_pixel_width)))
+    signal_simple_error ("Must supply only one of :width and :pixel-width", instantiator);
+
+  if (!NILP (find_keyword_in_vector (instantiator, Q_height))
+	     && !NILP (find_keyword_in_vector (instantiator, Q_pixel_height)))
+    signal_simple_error ("Must supply only one of :height and :pixel-height", instantiator);
+}
+
+static void
+combo_box_validate (Lisp_Object instantiator)
+{
+  widget_validate (instantiator);
+  if (NILP (find_keyword_in_vector (instantiator, Q_properties)))
+    signal_simple_error ("Must supply item list", instantiator);
+}
+
+/* we need to convert things like glyphs to images, eval expressions
+   etc.*/
+static Lisp_Object
+widget_normalize (Lisp_Object inst, Lisp_Object console_type)
+{
+  /* This function can call lisp */
+  Lisp_Object glyph = find_keyword_in_vector (inst, Q_image);
+
+  /* we need to eval glyph if its an expression, we do this for the
+     same reasons we normalize file to data. */
+  if (!NILP (glyph))
+    {
+      substitute_keyword_value (inst, Q_image, glyph_instantiator_to_glyph (glyph));
+    }
+
+  return inst;
+}
+
+static void
+initialize_widget_image_instance (struct Lisp_Image_Instance *ii, Lisp_Object type)
+{
+  /*  initialize_subwindow_image_instance (ii);*/
+  IMAGE_INSTANCE_WIDGET_TYPE (ii) = type;
+  IMAGE_INSTANCE_WIDGET_PROPS (ii) = Qnil;
+  IMAGE_INSTANCE_WIDGET_FACE (ii) = Vwidget_face;
+  IMAGE_INSTANCE_WIDGET_ITEMS (ii) = allocate_gui_item ();
+}
+
+/* Instantiate a button widget. Unfortunately instantiated widgets are
+   particular to a frame since they need to have a parent. It's not
+   like images where you just select the image into the context you
+   want to display it in and BitBlt it. So image instances can have a
+   many-to-one relationship with things you see, whereas widgets can
+   only be one-to-one (i.e. per frame) */
+void
+widget_instantiate_1 (Lisp_Object image_instance, Lisp_Object instantiator,
+		      Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		      int dest_mask, Lisp_Object domain, int default_textheight,
+		      int default_pixheight, int default_textwidth)
+{
+  struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object face = find_keyword_in_vector (instantiator, Q_face);
+  Lisp_Object height = find_keyword_in_vector (instantiator, Q_height);
+  Lisp_Object width = find_keyword_in_vector (instantiator, Q_width);
+  Lisp_Object pixwidth = find_keyword_in_vector (instantiator, Q_pixel_width);
+  Lisp_Object pixheight = find_keyword_in_vector (instantiator, Q_pixel_height);
+  Lisp_Object desc = find_keyword_in_vector (instantiator, Q_descriptor);
+  Lisp_Object glyph = find_keyword_in_vector (instantiator, Q_image);
+  Lisp_Object props = find_keyword_in_vector (instantiator, Q_properties);
+  int pw=0, ph=0, tw=0, th=0;
+  
+  /* this just does pixel type sizing */
+  subwindow_instantiate (image_instance, instantiator, pointer_fg, pointer_bg,
+			 dest_mask, domain);
+
+  if (!(dest_mask & IMAGE_WIDGET_MASK))
+    incompatible_image_types (instantiator, dest_mask, IMAGE_WIDGET_MASK);
+
+  initialize_widget_image_instance (ii, XVECTOR_DATA (instantiator)[0]);
+
+  /* retrieve the fg and bg colors */
+  if (!NILP (face))
+    IMAGE_INSTANCE_WIDGET_FACE (ii) = Fget_face (face);
+  
+  /* data items for some widgets */
+  IMAGE_INSTANCE_WIDGET_PROPS (ii) = props;
+
+  /* retrieve the gui item information. This is easy if we have been
+     provided with a vector, more difficult if we have just been given
+     keywords */
+  if (STRINGP (desc) || NILP (desc))
+    {
+      /* big cheat - we rely on the fact that a gui item looks like an instantiator */
+      IMAGE_INSTANCE_WIDGET_ITEMS (ii) = 
+	gui_parse_item_keywords_no_errors (instantiator);
+      IMAGE_INSTANCE_WIDGET_TEXT (ii) = desc;
+    }
+  else
+    IMAGE_INSTANCE_WIDGET_ITEMS (ii) =
+      gui_parse_item_keywords_no_errors (desc);
+
+  /* parse more gui items out of the properties */
+  if (!NILP (props))
+    {
+      Lisp_Object items = Fplist_get (props, Q_items, Qnil);
+      if (!NILP (items))
+	IMAGE_INSTANCE_WIDGET_ITEMS (ii) = 
+	  Fcons (IMAGE_INSTANCE_WIDGET_ITEMS (ii), 
+		 parse_gui_item_tree_children (items));
+    }
+
+  /* normalize size information */
+  if (!NILP (width))
+    tw = XINT (width);
+  if (!NILP (height))
+    th = XINT (height);
+  if (!NILP (pixwidth))
+    pw = XINT (pixwidth);
+  if (!NILP (pixheight))
+    ph = XINT (pixheight);
+
+  /* for a widget with an image pick up the dimensions from that */
+  if (!NILP (glyph))
+    {
+      if (!pw && !tw)
+	pw = glyph_width (glyph, Qnil, DEFAULT_INDEX, domain) 
+	  + 2 * WIDGET_BORDER_WIDTH;
+      if (!ph && !th)
+	ph = glyph_height (glyph, Qnil, DEFAULT_INDEX, domain) 
+	  + 2 * WIDGET_BORDER_HEIGHT;
+    }
+
+  /* if we still don' t have sizes, guess from text size */
+  if (!tw && !pw)
+    {
+      if (default_textwidth)
+	tw = default_textwidth;
+      else if (!NILP (IMAGE_INSTANCE_WIDGET_TEXT (ii)))
+	tw = XSTRING_LENGTH (IMAGE_INSTANCE_WIDGET_TEXT (ii));
+    }
+
+  if (!th && !ph)
+    {
+      if (default_textheight)
+	th = default_textheight;
+      else if (!NILP (IMAGE_INSTANCE_WIDGET_TEXT (ii)))
+	th = 1;
+      else
+	ph = default_pixheight;
+    }
+  
+  if (tw !=0 || th !=0)
+    widget_text_to_pixel_conversion (domain,
+				     IMAGE_INSTANCE_WIDGET_FACE (ii),
+				     th, tw, th ? &ph : 0, tw ? &pw : 0);
+
+  IMAGE_INSTANCE_SUBWINDOW_WIDTH (ii) = pw;
+  IMAGE_INSTANCE_SUBWINDOW_HEIGHT (ii) = ph;
+#ifdef DEBUG_WIDGETS
+  debug_widget_instances++;
+  stderr_out ("instantiated ");
+  debug_print (instantiator);
+  stderr_out ("%d widgets instantiated\n", debug_widget_instances);
+#endif
+}
+
+static void
+widget_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		    Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		    int dest_mask, Lisp_Object domain)
+{
+  widget_instantiate_1 (image_instance, instantiator, pointer_fg,
+			       pointer_bg, dest_mask, domain, 1, 0, 0);
+}
+
+/* tree-view generic instantiation - get the height right */
+static void
+tree_view_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		       Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		       int dest_mask, Lisp_Object domain)
+{
+  Lisp_Object data = Fplist_get (find_keyword_in_vector (instantiator, Q_properties),
+				 Q_items, Qnil);
+  int len;
+  GET_LIST_LENGTH (data, len);
+  widget_instantiate_1 (image_instance, instantiator, pointer_fg,
+			pointer_bg, dest_mask, domain, len + 1, 0, 0);
+}
+
+static void
+tab_control_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		 Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		 int dest_mask, Lisp_Object domain)
+{
+  Lisp_Object data = Fplist_get (find_keyword_in_vector (instantiator, Q_properties),
+				 Q_items, Qnil);
+  Lisp_Object rest;
+  int len = 0;
+
+  LIST_LOOP (rest, data)
+    {
+      len += 3;			/* some bias */
+      if (STRINGP (XCAR (rest)))
+	len += XSTRING_LENGTH (XCAR (rest));
+      else if (VECTORP (XCAR (rest)))
+	{
+	  Lisp_Object gui = gui_parse_item_keywords (XCAR (rest));
+	  len += XSTRING_LENGTH (XGUI_ITEM (gui)->name);
+	}
+    }
+
+  widget_instantiate_1 (image_instance, instantiator, pointer_fg,
+			pointer_bg, dest_mask, domain, 0, 0, len);
+}
+
+/* Instantiate a static control */
+static void
+static_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		    Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		    int dest_mask, Lisp_Object domain)
+{
+  widget_instantiate_1 (image_instance, instantiator, pointer_fg,
+			pointer_bg, dest_mask, domain, 0, 4, 0);
+}
+
+
+/*****************************************************************************
+ *                              widget layout                               *
+ *****************************************************************************/
+static int
+layout_possible_dest_types (void)
+{
+  return IMAGE_LAYOUT_MASK;
+}
+
+/* we need to convert things like glyphs to images, eval expressions
+   etc.*/
+static Lisp_Object
+layout_normalize (Lisp_Object inst, Lisp_Object console_type)
+{
+  /* This function can call lisp */
+  Lisp_Object items = find_keyword_in_vector (inst, Q_items);
+  Lisp_Object border = find_keyword_in_vector (inst, Q_border);
+  /* we need to eval glyph if its an expression, we do this for the
+     same reasons we normalize file to data. */
+  if (!NILP (items))
+    {
+      Lisp_Object rest;
+      LIST_LOOP (rest, items)
+	{
+	  /* substitute the new glyph */
+	  Fsetcar (rest, glyph_instantiator_to_glyph (XCAR (rest)));
+	}
+    }
+  /* normalize the border spec. */
+  if (VECTORP (border) || CONSP (border))
+    {
+      substitute_keyword_value (inst, Q_border, glyph_instantiator_to_glyph (border));
+    }
+  return inst;
+}
+
+/* Instantiate a layout widget. */
+static void
+layout_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		    Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		    int dest_mask, Lisp_Object domain)
+{
+  struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object rest, device = IMAGE_INSTANCE_DEVICE (ii);
+  Lisp_Object frame = FW_FRAME (domain);
+  Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
+  Lisp_Object width = find_keyword_in_vector (instantiator, Q_pixel_width);
+  Lisp_Object height = find_keyword_in_vector (instantiator, Q_pixel_height);
+  Lisp_Object orient = find_keyword_in_vector (instantiator, Q_orientation);
+  Lisp_Object justify = find_keyword_in_vector (instantiator, Q_justify);
+  Lisp_Object border = find_keyword_in_vector (instantiator, Q_border);
+  Lisp_Object children = Qnil;
+  int pw = 0, ph = 0, x, y, maxph = 0, maxpw = 0, nitems = 0,
+    horiz_spacing, vert_spacing, ph_adjust = 0;
+
+  if (NILP (frame))
+    signal_simple_error ("No selected frame", device);
+  
+  if (!(dest_mask & IMAGE_LAYOUT_MASK))
+    incompatible_image_types (instantiator, dest_mask, IMAGE_LAYOUT_MASK);
+
+  if (NILP (orient))
+    orient = Qvertical;
+
+  if (EQ (border, Qt))
+    border = Qetched_in;
+
+  ii->data = 0;
+  IMAGE_INSTANCE_TYPE (ii) = IMAGE_LAYOUT;
+  IMAGE_INSTANCE_SUBWINDOW_ID (ii) = 0;
+  IMAGE_INSTANCE_SUBWINDOW_DISPLAYEDP (ii) = 0;
+  IMAGE_INSTANCE_SUBWINDOW_FRAME (ii) = frame;
+  IMAGE_INSTANCE_LAYOUT_BORDER (ii) = border;
+
+  /* normalize size information */
+  if (!NILP (width))
+    pw = XINT (width);
+  if (!NILP (height))
+    ph = XINT (height);
+
+  /* flip through the items to work out how much stuff we have to display */
+  LIST_LOOP (rest, items)
+    {
+      Lisp_Object glyph = XCAR (rest);
+      int gheight = glyph_height (glyph, Qnil, DEFAULT_INDEX, domain);
+      int gwidth = glyph_width (glyph, Qnil, DEFAULT_INDEX, domain);
+      nitems ++;
+      if (EQ (orient, Qhorizontal))
+	{
+	  maxph = max (maxph, gheight);
+	  maxpw += gwidth;
+	}
+      else if (EQ (orient, Qvertical))
+	{
+	  maxpw = max (maxpw, gwidth);
+	  maxph += gheight;
+	}
+    }
+
+  /* work out spacing between items and bounds of the layout */
+  if (!pw)
+    {
+      /* No user provided width so we just do default spacing. */
+      horiz_spacing = WIDGET_BORDER_WIDTH * 2;
+      if (EQ (orient, Qhorizontal))
+	pw = maxpw + (nitems + 1) * horiz_spacing;
+      else 
+	pw = maxpw + 2 * horiz_spacing;
+    }
+  else if (pw < maxpw)
+    /* The user wants a smaller space than the largest item, so we
+       just provide default spacing and will let the output routines
+       clip.. */
+    horiz_spacing = WIDGET_BORDER_WIDTH * 2;
+  else if (EQ (orient, Qhorizontal))
+    /* We have a larger area to display in so distribute the space
+       evenly. */
+    horiz_spacing = (pw - maxpw) / (nitems + 1);
+  else
+    horiz_spacing = (pw - maxpw) / 2;
+
+  /* Do the border now so that we can adjust the layout. */
+  if (GLYPHP (border))
+    {
+      /* We are going to be sneaky here and add the border text as
+         just another child, the layout and output routines don't know
+         this and will just display at the offsets we prescribe. */
+      Lisp_Object bglyph = glyph_image_instance (border, domain, ERROR_ME, 1);
+
+      children = Fcons (bglyph, children);
+      XIMAGE_INSTANCE_XOFFSET (bglyph) = 10; /* Really, what should this be? */
+      XIMAGE_INSTANCE_YOFFSET (bglyph) = 0;
+
+      ph_adjust = (glyph_height (border, Qnil, DEFAULT_INDEX, domain) / 2);
+      IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (ph_adjust);
+    }
+
+  /* Work out vertical spacings. */
+  if (!ph)
+    {
+      vert_spacing = WIDGET_BORDER_HEIGHT * 2;
+      if (EQ (orient, Qvertical))
+	ph = maxph + (nitems + 1) * vert_spacing + ph_adjust;
+      else 
+	ph = maxph + 2 * vert_spacing + ph_adjust;
+    }
+  else if (ph < maxph)
+    vert_spacing = WIDGET_BORDER_HEIGHT * 2;
+  else if (EQ (orient, Qvertical))
+    vert_spacing = (ph - (maxph + ph_adjust)) / (nitems + 1);
+  else
+    vert_spacing = (ph - (maxph + ph_adjust)) / 2;
+
+  y = vert_spacing + ph_adjust;
+  x = horiz_spacing;
+
+  /* Now flip through putting items where we want them, paying
+     attention to justification. */
+  LIST_LOOP (rest, items)
+    {
+      /* make sure the image is instantiated */
+      Lisp_Object glyph = XCAR (rest);
+      Lisp_Object gii = glyph_image_instance (glyph, domain, ERROR_ME, 1);
+      int gwidth = glyph_width (glyph, Qnil, DEFAULT_INDEX, domain);
+      int gheight = glyph_height (glyph, Qnil, DEFAULT_INDEX, domain);
+
+      children = Fcons (gii, children);
+
+      if (EQ (orient, Qhorizontal))
+	{
+	  if (EQ (justify, Qright))
+	    y = ph - (gheight + vert_spacing);
+	  else if (EQ (justify, Qcenter))
+	    y = (ph - gheight) / 2;
+	}
+      else if (EQ (orient, Qvertical))
+	{
+	  if (EQ (justify, Qright))
+	    x = pw - (gwidth + horiz_spacing);
+	  else if (EQ (justify, Qcenter))
+	    x = (pw - gwidth) / 2;
+	}
+
+      XIMAGE_INSTANCE_XOFFSET (gii) = x;
+      XIMAGE_INSTANCE_YOFFSET (gii) = y;
+
+      if (EQ (orient, Qhorizontal))
+	{
+	  x += (gwidth + horiz_spacing);
+	}
+      else if (EQ (orient, Qvertical))
+	{
+	  y += (gheight + vert_spacing);
+	}
+    }
+
+  IMAGE_INSTANCE_LAYOUT_CHILDREN (ii) = children;
+  assert (pw && ph);
+  IMAGE_INSTANCE_SUBWINDOW_WIDTH (ii) = pw;
+  IMAGE_INSTANCE_SUBWINDOW_HEIGHT (ii) = ph;
+}
+
+
+/************************************************************************/
+/*                            initialization                            */
+/************************************************************************/
+
+void
+syms_of_glyphs_widget (void)
+{
+  defkeyword (&Q_descriptor, ":descriptor");
+  defkeyword (&Q_height, ":height");
+  defkeyword (&Q_width, ":width");
+  defkeyword (&Q_properties, ":properties");
+  defkeyword (&Q_items, ":items");
+  defkeyword (&Q_image, ":image");
+  defkeyword (&Q_percent, ":percent");
+  defkeyword (&Q_text, ":text");
+  defkeyword (&Q_orientation, ":orientation");
+  defkeyword (&Q_justify, ":justify");
+  defkeyword (&Q_border, ":border");
+
+  defsymbol (&Qetched_in, "etched-in");
+  defsymbol (&Qetched_out, "etched-out");
+  defsymbol (&Qbevel_in, "bevel-in");
+  defsymbol (&Qbevel_out, "bevel-out");
+}
+
+void
+image_instantiator_format_create_glyphs_widget (void)
+{
+#define VALID_GUI_KEYWORDS(type) \
+  IIFORMAT_VALID_KEYWORD (type, Q_active, check_valid_anything); \
+  IIFORMAT_VALID_KEYWORD (type, Q_suffix, check_valid_anything); \
+  IIFORMAT_VALID_KEYWORD (type, Q_keys, check_valid_string);		\
+  IIFORMAT_VALID_KEYWORD (type, Q_style, check_valid_symbol);		\
+  IIFORMAT_VALID_KEYWORD (type, Q_selected, check_valid_anything);	\
+  IIFORMAT_VALID_KEYWORD (type, Q_filter, check_valid_anything);		\
+  IIFORMAT_VALID_KEYWORD (type, Q_config, check_valid_symbol);		\
+  IIFORMAT_VALID_KEYWORD (type, Q_included, check_valid_anything);	\
+  IIFORMAT_VALID_KEYWORD (type, Q_key_sequence, check_valid_string);	\
+  IIFORMAT_VALID_KEYWORD (type, Q_accelerator, check_valid_string);	\
+  IIFORMAT_VALID_KEYWORD (type, Q_label, check_valid_anything);		\
+  IIFORMAT_VALID_KEYWORD (type, Q_callback, check_valid_callback); 		\
+  IIFORMAT_VALID_KEYWORD (type, Q_descriptor, check_valid_string_or_vector)
+
+#define VALID_WIDGET_KEYWORDS(type) \
+  IIFORMAT_VALID_KEYWORD (type, Q_width, check_valid_int);		\
+  IIFORMAT_VALID_KEYWORD (type, Q_height, check_valid_int);		\
+  IIFORMAT_VALID_KEYWORD (type, Q_pixel_width, check_valid_int);	\
+  IIFORMAT_VALID_KEYWORD (type, Q_pixel_height, check_valid_int);	\
+  IIFORMAT_VALID_KEYWORD (type, Q_face, check_valid_face)
+
+  /* we only do this for properties */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT_NO_SYM (widget, "widget");
+  IIFORMAT_HAS_METHOD (widget, property);
+  IIFORMAT_HAS_METHOD (widget, set_property);
+
+  /* widget image-instantiator types - buttons */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (button, "button");
+  IIFORMAT_HAS_SHARED_METHOD (button, validate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (button, possible_dest_types, widget);
+  IIFORMAT_HAS_SHARED_METHOD (button, instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (button, normalize, widget);
+  IIFORMAT_VALID_KEYWORD (button, 
+			  Q_image, check_valid_glyph_or_instantiator);
+  VALID_WIDGET_KEYWORDS (button);
+  VALID_GUI_KEYWORDS (button);
+
+  /* edit fields */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (edit_field, "edit-field");
+  IIFORMAT_HAS_SHARED_METHOD (edit_field, validate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (edit_field, possible_dest_types, widget);
+  IIFORMAT_HAS_SHARED_METHOD (edit_field, instantiate, widget);
+  VALID_WIDGET_KEYWORDS (edit_field);
+  VALID_GUI_KEYWORDS (edit_field);
+
+  /* combo box */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (combo_box, "combo-box");
+  IIFORMAT_HAS_METHOD (combo_box, validate);
+  IIFORMAT_HAS_SHARED_METHOD (combo_box, possible_dest_types, widget);
+  VALID_GUI_KEYWORDS (combo_box);
+
+  IIFORMAT_VALID_KEYWORD (combo_box, Q_width, check_valid_int);
+  IIFORMAT_VALID_KEYWORD (combo_box, Q_height, check_valid_int);
+  IIFORMAT_VALID_KEYWORD (combo_box, Q_pixel_width, check_valid_int);
+  IIFORMAT_VALID_KEYWORD (combo_box, Q_face, check_valid_face);
+  IIFORMAT_VALID_KEYWORD (combo_box, Q_properties, check_valid_item_list);
+
+  /* scrollbar */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (scrollbar, "scrollbar");
+  IIFORMAT_HAS_SHARED_METHOD (scrollbar, validate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (scrollbar, possible_dest_types, widget);
+  IIFORMAT_HAS_SHARED_METHOD (scrollbar, instantiate, widget);
+  VALID_GUI_KEYWORDS (scrollbar);
+
+  IIFORMAT_VALID_KEYWORD (scrollbar, Q_pixel_width, check_valid_int);
+  IIFORMAT_VALID_KEYWORD (scrollbar, Q_pixel_height, check_valid_int);
+  IIFORMAT_VALID_KEYWORD (scrollbar, Q_face, check_valid_face);
+
+  /* progress guage */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (progress_gauge, "progress-gauge");
+  IIFORMAT_HAS_SHARED_METHOD (progress_gauge, validate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (progress_gauge, possible_dest_types, widget);
+  IIFORMAT_HAS_SHARED_METHOD (progress_gauge, instantiate, widget);
+  VALID_WIDGET_KEYWORDS (progress_gauge);
+  VALID_GUI_KEYWORDS (progress_gauge);
+
+  /* tree view */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (tree_view, "tree-view");
+  IIFORMAT_HAS_SHARED_METHOD (tree_view, validate, combo_box);
+  IIFORMAT_HAS_SHARED_METHOD (tree_view, possible_dest_types, widget);
+  IIFORMAT_HAS_METHOD (tree_view, instantiate);
+  VALID_WIDGET_KEYWORDS (tree_view);
+  VALID_GUI_KEYWORDS (tree_view);
+  IIFORMAT_VALID_KEYWORD (tree_view, Q_properties, check_valid_item_list);
+
+  /* tab control */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (tab_control, "tab-control");
+  IIFORMAT_HAS_SHARED_METHOD (tab_control, validate, combo_box);
+  IIFORMAT_HAS_SHARED_METHOD (tab_control, possible_dest_types, widget);
+  IIFORMAT_HAS_METHOD (tab_control, instantiate);
+  VALID_WIDGET_KEYWORDS (tab_control);
+  VALID_GUI_KEYWORDS (tab_control);
+  IIFORMAT_VALID_KEYWORD (tab_control, Q_properties, check_valid_item_list);
+
+  /* labels */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (label, "label");
+  IIFORMAT_HAS_SHARED_METHOD (label, possible_dest_types, widget);
+  IIFORMAT_HAS_SHARED_METHOD (label, instantiate, static);
+  VALID_WIDGET_KEYWORDS (label);
+  IIFORMAT_VALID_KEYWORD (label, Q_descriptor, check_valid_string);
+
+  /* layout */
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (layout, "layout");
+  IIFORMAT_HAS_METHOD (layout, possible_dest_types);
+  IIFORMAT_HAS_METHOD (layout, instantiate);
+  IIFORMAT_HAS_METHOD (layout, normalize);
+  IIFORMAT_VALID_KEYWORD (layout, Q_pixel_width, check_valid_int);
+  IIFORMAT_VALID_KEYWORD (layout, Q_pixel_height, check_valid_int);
+  IIFORMAT_VALID_KEYWORD (layout, Q_orientation, check_valid_orientation);
+  IIFORMAT_VALID_KEYWORD (layout, Q_justify, check_valid_justification);
+  IIFORMAT_VALID_KEYWORD (layout, Q_border, check_valid_border);
+  IIFORMAT_VALID_KEYWORD (layout, Q_items, 
+			  check_valid_glyph_or_instantiator_list);
+}
+
+void
+reinit_vars_of_glyphs_widget (void)
+{
+#ifdef DEBUG_WIDGETS
+  debug_widget_instances = 0;
+#endif
+}
+
+void
+vars_of_glyphs_widget (void)
+{
+  reinit_vars_of_glyphs_widget ();
+}