import -ko -b 1.1.3 XEmacs XEmacs-21_2 r21-2-35
[chise/xemacs-chise.git.1] / src / glyphs-widget.c
index 69c3efe..bc3e829 100644 (file)
@@ -1,5 +1,5 @@
 /* Widget-specific glyph objects.
-   Copyright (C) 1998, 1999 Andy Piper.
+   Copyright (C) 1998, 1999, 2000 Andy Piper.
 
 This file is part of XEmacs.
 
@@ -55,55 +55,29 @@ DEFINE_IMAGE_INSTANTIATOR_FORMAT (tab_control);
 Lisp_Object Qtab_control;
 DEFINE_IMAGE_INSTANTIATOR_FORMAT (layout);
 Lisp_Object Qlayout;
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (native_layout);
+Lisp_Object Qnative_layout;
 
 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 Q_image, Q_text, Q_orientation, Q_justify, Q_border;
+Lisp_Object Q_margin_width;
 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.
+   - tooltips for controls, especially buttons.
+   - keyboard traversal.
+   - lisp configurable layout.
  */
 
-/* In windows normal windows work in pixels, dialog boxes work in
+/* In MS-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)
 {
@@ -133,6 +107,19 @@ check_valid_orientation (Lisp_Object data)
 }
 
 static void
+check_valid_tab_orientation (Lisp_Object data)
+{
+  if (!EQ (data, Qtop)
+      &&
+      !EQ (data, Qbottom)
+      &&
+      !EQ (data, Qleft)
+      &&
+      !EQ (data, Qright))
+    signal_simple_error ("unknown orientation for tab control", data);
+}
+
+static void
 check_valid_justification (Lisp_Object data)
 {
   if (!EQ (data, Qleft) && !EQ (data, Qright) && !EQ (data, Qcenter))
@@ -165,6 +152,13 @@ check_valid_callback (Lisp_Object data)
 }
 
 static void
+check_valid_int_or_function (Lisp_Object data)
+{
+  if (!INTP (data) && !CONSP (data))
+    signal_simple_error ("must be an integer or expresssion", data);
+}
+
+static void
 check_valid_symbol (Lisp_Object data)
 {
     CHECK_SYMBOL (data);
@@ -259,21 +253,21 @@ substitute_keyword_value (Lisp_Object inst, Lisp_Object key, Lisp_Object val)
     }
 }
 
-/* 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. */
+/* 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 whether 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);
+  Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
   struct image_instantiator_methods* meths;
 
   /* first see if its a general property ... */
@@ -281,7 +275,7 @@ widget_property (Lisp_Object image_instance, Lisp_Object 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), 
+  meths = decode_device_ii_format (image_instance_device (image_instance),
                                   IMAGE_INSTANCE_WIDGET_TYPE (ii), 
                                   ERROR_ME_NOT);
   if (meths && HAS_IIFORMAT_METH_P (meths, property))
@@ -298,12 +292,20 @@ widget_property (Lisp_Object image_instance, Lisp_Object prop)
 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);
+  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), 
+  /* PIck up any generic properties that we might need to keep hold
+     of. */
+  if (EQ (prop, Q_text))
+    {
+      IMAGE_INSTANCE_WIDGET_TEXT (ii) = val;
+      IMAGE_INSTANCE_TEXT_CHANGED (ii) = 1;
+    }
+
+  /* Now try device specific methods first ... */
+  meths = decode_device_ii_format (image_instance_device (image_instance), 
                                   IMAGE_INSTANCE_WIDGET_TYPE (ii), 
                                   ERROR_ME_NOT);
   if (meths && HAS_IIFORMAT_METH_P (meths, set_property)
@@ -329,6 +331,141 @@ widget_set_property (Lisp_Object image_instance, Lisp_Object prop, Lisp_Object v
   return val;
 }
 
+/* Like the rest of redisplay, we want widget updates to occur
+   asynchronously. Thus toolkit specific methods for setting
+   properties must be called by redisplay instead of by
+   *_set_property. Thus *_set_property records the change and this
+   function actually implements it. We want to be slightly clever
+   about this however by supplying format specific functions for the
+   updates instead of lumping them all into this function. Note that
+   there is no need for format generic functions. */
+void
+update_widget (Lisp_Object widget)
+{
+  Lisp_Image_Instance* ii = XIMAGE_INSTANCE (widget);
+  struct image_instantiator_methods* meths;
+
+  if (!WIDGET_IMAGE_INSTANCEP (widget)
+      || EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qlayout)
+      || EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qnative_layout))
+    return;
+
+  /* Device generic methods. We must update the widget's size as it
+     may have been changed by the the layout routines. We also do this
+     here so that explicit resizing from lisp does not result in
+     synchronous updates. */
+  MAYBE_DEVMETH (DOMAIN_XDEVICE (ii->domain), update_widget, (ii));
+
+  /* Device-format specific methods */
+  meths = decode_device_ii_format (image_instance_device (widget), 
+                                  IMAGE_INSTANCE_WIDGET_TYPE (ii), 
+                                  ERROR_ME_NOT);
+  MAYBE_IIFORMAT_METH (meths, update, (widget));
+
+  /* Pick up the items we recorded earlier. */
+  if (IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii))
+    {
+      IMAGE_INSTANCE_WIDGET_ITEMS (ii) =
+       IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii);
+      IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) = Qnil;
+    }
+}
+
+/* Query for a widgets desired geometry. If no type specific method is
+   provided then use the widget text to calculate sizes. */
+static void 
+widget_query_geometry (Lisp_Object image_instance, 
+                      int* width, int* height,
+                      enum image_instance_geometry disp, Lisp_Object domain)
+{
+  Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
+  struct image_instantiator_methods* meths;
+  Lisp_Object dynamic_width = Qnil;
+  Lisp_Object dynamic_height = Qnil;
+
+  /* First just set up what we already have. */
+  if (width)   *width = IMAGE_INSTANCE_WIDTH (ii);
+  if (height)  *height = IMAGE_INSTANCE_HEIGHT (ii);
+  
+  if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii)
+      ||
+      IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
+    {
+      /* .. then try device specific methods ... */
+      meths = decode_device_ii_format (image_instance_device (image_instance),
+                                      IMAGE_INSTANCE_WIDGET_TYPE (ii), 
+                                      ERROR_ME_NOT);
+      if (meths && HAS_IIFORMAT_METH_P (meths, query_geometry))
+       IIFORMAT_METH (meths, query_geometry, (image_instance, 
+                                              width, height, disp,
+                                              domain));
+      else
+       {
+         /* ... 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, query_geometry))
+           IIFORMAT_METH (meths, query_geometry, (image_instance, 
+                                                  width, height, disp,
+                                                  domain));
+         else 
+           {
+             int w, h;
+             
+             /* Then if we are allowed to resize the widget, make the
+                size the same as the text dimensions. */
+             query_string_geometry (IMAGE_INSTANCE_WIDGET_TEXT (ii),
+                                    IMAGE_INSTANCE_WIDGET_FACE (ii),
+                                    &w, &h, 0, domain);
+             /* Adjust the size for borders. */
+             if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
+               *width = w + 2 * WIDGET_BORDER_WIDTH;
+             if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii))
+               *height = h +  2 * WIDGET_BORDER_HEIGHT;
+           }
+       }
+      /* Finish off with dynamic sizing. */
+      if (!NILP (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii)))
+       {
+         dynamic_width = Feval (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii));
+         if (INTP (dynamic_width))
+           *width = XINT (dynamic_width);
+       }
+      if (!NILP (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii)))
+       {
+         dynamic_height = Feval (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii));
+         if (INTP (dynamic_height))
+           *height = XINT (dynamic_height);
+       }
+    }
+}
+
+static int 
+widget_layout (Lisp_Object image_instance, 
+              int width, int height, Lisp_Object domain)
+{
+  Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
+  struct image_instantiator_methods* meths;
+
+  /* .. then try device specific methods ... */
+  meths = decode_device_ii_format (image_instance_device (image_instance),
+                                  IMAGE_INSTANCE_WIDGET_TYPE (ii), 
+                                  ERROR_ME_NOT);
+  if (meths && HAS_IIFORMAT_METH_P (meths, layout))
+    return IIFORMAT_METH (meths, layout, (image_instance, 
+                                         width, height, domain));
+  else
+    {
+      /* ... 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, layout))
+       return IIFORMAT_METH (meths, layout, (image_instance, 
+                                             width, height, domain));
+    }
+  return 1;
+}
+
 static void
 widget_validate (Lisp_Object instantiator)
 {
@@ -376,13 +513,21 @@ widget_normalize (Lisp_Object inst, Lisp_Object console_type)
 }
 
 static void
-initialize_widget_image_instance (struct Lisp_Image_Instance *ii, Lisp_Object type)
+initialize_widget_image_instance (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;
+  SET_IMAGE_INSTANCE_WIDGET_FACE (ii, Qnil);
   IMAGE_INSTANCE_WIDGET_ITEMS (ii) = allocate_gui_item ();
+  IMAGE_INSTANCE_LAYOUT_CHILDREN (ii) = Qnil;
+  IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) = Qnil;
+  IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii) = Qnil;
+  IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii) = Qnil;
+  IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 1;
+  IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 1;
+  IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) = LAYOUT_HORIZONTAL;
+  IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii) = 0;
 }
 
 /* Instantiate a button widget. Unfortunately instantiated widgets are
@@ -392,12 +537,11 @@ initialize_widget_image_instance (struct Lisp_Image_Instance *ii, Lisp_Object ty
    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)
+widget_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_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);
@@ -406,24 +550,27 @@ widget_instantiate_1 (Lisp_Object image_instance, Lisp_Object instantiator,
   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);
+  Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
+  Lisp_Object orient = find_keyword_in_vector (instantiator, Q_orientation);
+  Lisp_Object mwidth = find_keyword_in_vector (instantiator, Q_margin_width);
   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]);
 
+  IMAGE_INSTANCE_TYPE (ii) = IMAGE_WIDGET;
+  IMAGE_INSTANCE_WIDGET_PROPS (ii) = props;
+
   /* retrieve the fg and bg colors */
   if (!NILP (face))
-    IMAGE_INSTANCE_WIDGET_FACE (ii) = Fget_face (face);
+    SET_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 */
@@ -437,64 +584,108 @@ widget_instantiate_1 (Lisp_Object image_instance, Lisp_Object instantiator,
   else
     IMAGE_INSTANCE_WIDGET_ITEMS (ii) =
       gui_parse_item_keywords_no_errors (desc);
+      
+  /* Pick up the orientation before we do our first layout. */
+  if (EQ (orient, Qleft) || EQ (orient, Qright) || EQ (orient, Qvertical))
+    IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) = LAYOUT_VERTICAL;
 
   /* parse more gui items out of the properties */
-  if (!NILP (props))
+  if (!NILP (props)
+      && 
+      !EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qlayout)
+      && !EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qnative_layout))
     {
-      Lisp_Object items = Fplist_get (props, Q_items, Qnil);
+      if (NILP (items))
+       {
+         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);
+       {
+         IMAGE_INSTANCE_WIDGET_ITEMS (ii) = 
+           Fcons (IMAGE_INSTANCE_WIDGET_ITEMS (ii), 
+                  parse_gui_item_tree_children (items));
+       }
+    }
 
-  /* for a widget with an image pick up the dimensions from that */
-  if (!NILP (glyph))
+  /* Normalize size information. We now only assign sizes if the user
+     gives us some explicitly, or there are some constraints that we
+     can't change later on. Otherwise we postpone sizing until query
+     geometry gets called. */
+  if (!NILP (pixwidth))                /* pixwidth takes precendent */
     {
-      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 (!INTP (pixwidth))
+       IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii) = pixwidth;
+      else
+       {
+         pw = XINT (pixwidth);
+         IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 0;
+       }
     }
-
-  /* if we still don' t have sizes, guess from text size */
-  if (!tw && !pw)
+  else if (!NILP (width))
     {
-      if (default_textwidth)
-       tw = default_textwidth;
-      else if (!NILP (IMAGE_INSTANCE_WIDGET_TEXT (ii)))
-       tw = XSTRING_LENGTH (IMAGE_INSTANCE_WIDGET_TEXT (ii));
+      tw = XINT (width);
+      IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 0;
     }
 
-  if (!th && !ph)
+  if (!NILP (pixheight))
     {
-      if (default_textheight)
-       th = default_textheight;
-      else if (!NILP (IMAGE_INSTANCE_WIDGET_TEXT (ii)))
-       th = 1;
+      if (!INTP (pixheight))
+       IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii) = pixheight;
       else
-       ph = default_pixheight;
+       {
+         ph = XINT (pixheight);
+         IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 0;
+       }
+    }
+  else if (!NILP (height) && XINT (height) > 1)
+    {
+      th = XINT (height);
+      IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 0;
+    }
+
+  /* Taking the default face information when the user has specified
+     size in characters is probably as good as any since the widget
+     face is more likely to be proportional and thus give inadequate
+     results. Using character sizes can only ever be approximate
+     anyway. */
+  if (tw || th)
+    {
+      int charwidth, charheight;
+      default_face_font_info (domain, 0, 0, &charheight, &charwidth, 0);
+      if (tw)
+       pw = charwidth * tw;
+      if (th)
+       ph = charheight * th;
+    }
+
+  /* for a widget with an image pick up the dimensions from that */
+  if (!NILP (glyph))
+    {
+      if (!pw)
+       pw = glyph_width (glyph, domain) + 2 * WIDGET_BORDER_WIDTH;
+      if (!ph)
+       ph = glyph_height (glyph, domain) + 2 * WIDGET_BORDER_HEIGHT;
+      IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 0;
+      IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 0;
     }
-  
-  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;
+  /* Pick up the margin width. */
+  if (!NILP (mwidth))
+    IMAGE_INSTANCE_MARGIN_WIDTH (ii) = XINT (mwidth);
+
+  /* Layout for the layout widget is premature at this point since the
+     children will not have been instantiated. We can't instantiate
+     them until the device instantiation method for the layout has
+     been executed. We do however want to record any specified
+     dimensions. */
+  if (pw)      IMAGE_INSTANCE_WIDTH (ii) = pw;
+  if (ph)      IMAGE_INSTANCE_HEIGHT (ii) = ph;
+}
+
+static void
+widget_post_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+                        Lisp_Object domain)
+{
 #ifdef DEBUG_WIDGETS
   debug_widget_instances++;
   stderr_out ("instantiated ");
@@ -503,75 +694,158 @@ widget_instantiate_1 (Lisp_Object image_instance, Lisp_Object instantiator,
 #endif
 }
 
+/* Get the geometry of a button control. We need to adjust the size
+   depending on the type of button. */
 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)
+button_query_geometry (Lisp_Object image_instance, 
+                      int* width, int* height,
+                      enum image_instance_geometry disp, Lisp_Object domain)
 {
-  widget_instantiate_1 (image_instance, instantiator, pointer_fg,
-                              pointer_bg, dest_mask, domain, 1, 0, 0);
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  int w, h;
+  query_string_geometry (IMAGE_INSTANCE_WIDGET_TEXT (ii),
+                        IMAGE_INSTANCE_WIDGET_FACE (ii),
+                        &w, &h, 0, domain);
+  /* Adjust the size for borders. */
+  if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
+    {
+      *width = w + 2 * WIDGET_BORDER_WIDTH;
+
+      if (EQ (XGUI_ITEM (IMAGE_INSTANCE_WIDGET_ITEM (ii))->style, Qradio)
+         ||
+         EQ (XGUI_ITEM (IMAGE_INSTANCE_WIDGET_ITEM (ii))->style, Qtoggle))
+       /* This is an approximation to the size of the actual button bit. */
+       *width += 12;
+    }
+  if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii))
+    *height = h +  2 * WIDGET_BORDER_HEIGHT;
 }
 
-/* tree-view generic instantiation - get the height right */
+/* tree-view geometry - 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)
+tree_view_query_geometry (Lisp_Object image_instance, 
+                         int* width, int* height,
+                         enum image_instance_geometry disp, 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);
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object items = IMAGE_INSTANCE_WIDGET_ITEMS (ii);
+
+  
+  if (*width)
+    {
+      /* #### what should this be. reconsider when X has tree views. */
+      query_string_geometry (IMAGE_INSTANCE_WIDGET_TEXT (ii),
+                            IMAGE_INSTANCE_WIDGET_FACE (ii),
+                            width, 0, 0, domain);
+    }
+  if (*height)
+    {
+      int len, h;
+      default_face_font_info (domain, 0, 0, &h, 0, 0);
+      GET_LIST_LENGTH (items, len);
+      *height = len * h;
+    }
 }
 
+/* Get the geometry of a tab control. This is based on the number of
+   items and text therin in the tab control. */
 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)
+tab_control_query_geometry (Lisp_Object image_instance, 
+                           int* width, int* height,
+                           enum image_instance_geometry disp, Lisp_Object domain)
 {
-  Lisp_Object data = Fplist_get (find_keyword_in_vector (instantiator, Q_properties),
-                                Q_items, Qnil);
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object items = XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii));
   Lisp_Object rest;
-  int len = 0;
+  unsigned int tw = 0, th = 0;
 
-  LIST_LOOP (rest, data)
+  LIST_LOOP (rest, items)
     {
-      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);
-       }
+      int h, w;
+
+      query_string_geometry (XGUI_ITEM (XCAR (rest))->name,
+                            IMAGE_INSTANCE_WIDGET_FACE (ii),
+                            &w, &h, 0, domain);
+      tw += 5 * WIDGET_BORDER_WIDTH; /* some bias */
+      tw += w;
+      th = max (th, h + 2 * WIDGET_BORDER_HEIGHT);
     }
 
-  widget_instantiate_1 (image_instance, instantiator, pointer_fg,
-                       pointer_bg, dest_mask, domain, 0, 0, len);
+  /* Fixup returned values depending on orientation. */
+  if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii))
+    {
+      if (height)      *height = tw;
+      if (width)       *width = th;
+    }
+  else
+    {
+      if (height)      *height = th;
+      if (width)       *width = tw;
+    }
 }
 
-/* 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)
+/* Get the geometry of a tab control. This is based on the number of
+   items and text therin in the tab control. */
+static Lisp_Object
+tab_control_set_property (Lisp_Object image_instance, 
+                         Lisp_Object prop,
+                         Lisp_Object val)
 {
-  widget_instantiate_1 (image_instance, instantiator, pointer_fg,
-                       pointer_bg, dest_mask, domain, 0, 4, 0);
+  /* Record new items for update. *_tab_control_update will do the
+     rest. */
+  if (EQ (prop, Q_items))
+    {
+      Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
+
+      check_valid_item_list_1 (val);
+      
+      /* Don't set the actual items since we might decide not to use
+         the new ones (because nothing has really changed). If we did
+         set them and didn't use them then we would get into whole
+         heaps of trouble when the old items get GC'd. */
+      IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) =
+       Fcons (XCAR (IMAGE_INSTANCE_WIDGET_ITEMS (ii)), 
+              parse_gui_item_tree_children (val));
+      IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii) = 1;
+
+      return Qt;
+    }
+  return Qunbound;
+}
+
+/* set the properties of a progres guage */
+static Lisp_Object
+progress_gauge_set_property (Lisp_Object image_instance,
+                            Lisp_Object prop,
+                            Lisp_Object val)
+{
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+
+  if (EQ (prop, Q_value))
+    {
+      CHECK_INT (val);
+#ifdef DEBUG_WIDGET_OUTPUT
+      printf ("progress gauge value set to %ld\n", XINT (val));
+#endif
+      IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) =
+       copy_gui_item_tree (IMAGE_INSTANCE_WIDGET_ITEMS (ii));
+#ifdef ERROR_CHECK_GLYPHS
+      assert (GUI_ITEMP (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)));
+#endif
+      if (GUI_ITEMP (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
+       XGUI_ITEM (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii))->value = val;
+
+      IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii) = 1;
+
+      return Qt;
+    }
+  return Qunbound;
 }
 
 \f
 /*****************************************************************************
  *                              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
@@ -599,170 +873,377 @@ layout_normalize (Lisp_Object inst, Lisp_Object console_type)
   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_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
   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);
+  /* Do widget type instantiation first. */
+  widget_instantiate (image_instance, instantiator, pointer_fg, pointer_bg,
+                     dest_mask, domain);
 
   if (NILP (orient))
-    orient = Qvertical;
-
+    {
+      IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) = LAYOUT_VERTICAL;
+    }
+      
   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 */
+    {
+      IMAGE_INSTANCE_LAYOUT_BORDER (ii) = Qetched_in;
+    }
+  else
+    {
+      IMAGE_INSTANCE_LAYOUT_BORDER (ii) = border;
+    }
+  /* We don't do the children yet as we might not have a containing
+     window. */
+}
+
+static void
+layout_post_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+                        Lisp_Object domain)
+{
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
+  Lisp_Object rest, children = Qnil;
+
+  if (GLYPHP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
+    {
+      /* 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 gii = glyph_image_instance 
+       (IMAGE_INSTANCE_LAYOUT_BORDER (ii),
+        image_instance, ERROR_ME, 1);
+
+      if (!IMAGE_INSTANCEP (gii))
+       return;
+      /* make sure we are designated as the parent. */
+      XIMAGE_INSTANCE_PARENT (gii) = image_instance;
+      children = Fcons (gii, children);
+      IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (0);
+    }
+
+  /* Pick up the sub-widgets. */
+  LIST_LOOP (rest, items)
+    {
+      /* make sure the image is instantiated */
+      Lisp_Object gii = glyph_image_instance (XCAR (rest), 
+                                             image_instance, ERROR_ME, 1);
+      if (!IMAGE_INSTANCEP (gii))
+       return;
+      /* make sure we are designated as the parent. */
+      XIMAGE_INSTANCE_PARENT (gii) = image_instance;
+      children = Fcons (gii, children);
+    }
+  /* Make sure elements in the layout are in the order the
+     user expected. */
+  children = Fnreverse (children);
+  IMAGE_INSTANCE_LAYOUT_CHILDREN (ii) = children;
+}
+
+/* Layout widget. Sizing commentary: we have a number of problems that
+   we would like to address. Some consider some of these more
+   important than others. It used to be that size information was
+   determined at instantiation time and was then fixed forever
+   after. Generally this is not what we want. Users want size to be
+   "big enough" to accommodate whatever they are trying to show and
+   this is dependent on text length, lines, font metrics etc. Of
+   course these attributes can change dynamically and so the size
+   should changed dynamically also. Only in a few limited cases should
+   the size be fixed and remain fixed. Of course this actually means
+   that we don't really want to specifiy the size *at all* for most
+   widgets - we want it to be discovered dynamically. Thus we can
+   envisage the following scenarios:
+   
+   1. A button is sized to accommodate its text, the text changes and the
+   button should change size also.  
+
+   2. A button is given an explicit size. Its size should never change.
+
+   3. Layout is put inside an area. The size of the area changes, the
+   layout should change with it. 
+
+   4. A button grows to accommodate additional text. The whitespace
+   around it should be modified to cope with the new layout
+   requirements. 
+
+   5. A button grows. The area surrounding it should grow also if
+   possible. 
+
+   What metrics are important?
+   1. Actual width and height.
+   
+   2. Whether the width and height are what the widget actually wants, or
+   whether it can grow or shrink. 
+
+   Text glyphs are particularly troublesome since their metrics depend
+   on the context in which they are being viewed. For instance they
+   can appear differently depending on the window face, frame face or
+   glyph face. In order to simplify this text glyphs can now only have
+   a glyph-face or image-instance face. All other glyphs are
+   essentially fixed in appearance. Perhaps the problem is that text
+   glyphs are cached on a device basis like most other glyphs. Instead
+   they should be cached per-window and then the instance would be
+   fixed and we wouldn't have to mess around with font metrics and the
+   rest. */
+
+/* Query the geometry of a layout widget. We assume that we can only
+   get here if the size is not already fixed. */
+static void
+layout_query_geometry (Lisp_Object image_instance, int* width,
+                      int* height, enum image_instance_geometry disp,
+                      Lisp_Object domain)
+{
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii), rest;
+  int maxph = 0, maxpw = 0, nitems = 0, ph_adjust = 0;
+  int gheight, gwidth;
+
+  /* If we are not initialized then we won't have any children. */
+  if (!IMAGE_INSTANCE_INITIALIZED (ii))
+      return;
+
+  /* First just set up what we already have. */
+  if (width)   *width = IMAGE_INSTANCE_WIDTH (ii);
+  if (height)  *height = IMAGE_INSTANCE_HEIGHT (ii);
+  
+  /* If we are not allowed to dynamically size then return. */
+  if (!IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii)
+      &&
+      !IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
+    return;
+
+  /* Pick up the border text if we have one. */
+  if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
+    {
+      image_instance_query_geometry (XCAR (items), &gwidth, &gheight, disp, domain);
+      ph_adjust = gheight / 2;
+      items = XCDR (items);
+    }
+  
+  /* 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);
+      image_instance_query_geometry (glyph, &gwidth, &gheight, disp, domain);
+
       nitems ++;
-      if (EQ (orient, Qhorizontal))
+      if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) 
+         == LAYOUT_HORIZONTAL)
        {
          maxph = max (maxph, gheight);
          maxpw += gwidth;
        }
-      else if (EQ (orient, Qvertical))
+      else
        {
          maxpw = max (maxpw, gwidth);
          maxph += gheight;
        }
     }
 
-  /* work out spacing between items and bounds of the layout */
-  if (!pw)
+  /* Work out minimum space we need to fit all the items. This could
+     have been fixed by the user. */
+  if (!NILP (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii)))
     {
-      /* 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;
+      Lisp_Object dynamic_width = 
+       Feval (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii));
+      if (INTP (dynamic_width))
+       *width = XINT (dynamic_width);
     }
-  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;
+  else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+          == LAYOUT_HORIZONTAL)
+    *width = maxpw + ((nitems + 1) * WIDGET_BORDER_WIDTH +
+                     IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2;
+  else 
+    *width = maxpw + 2 * (WIDGET_BORDER_WIDTH * 2 + 
+                         IMAGE_INSTANCE_MARGIN_WIDTH (ii));
 
-  /* Do the border now so that we can adjust the layout. */
-  if (GLYPHP (border))
+  /* Work out vertical spacings. */
+  if (!NILP (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii)))
     {
-      /* 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);
+      Lisp_Object dynamic_height = 
+       Feval (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii));
+      if (INTP (dynamic_height))
+       *height = XINT (dynamic_height);
+    }
+  else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+          == LAYOUT_VERTICAL)
+    *height = maxph + ((nitems + 1) * WIDGET_BORDER_HEIGHT +
+                      IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
+  else
+    *height = maxph + (2 * WIDGET_BORDER_HEIGHT +
+                      IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
+}
 
-      children = Fcons (bglyph, children);
-      XIMAGE_INSTANCE_XOFFSET (bglyph) = 10; /* Really, what should this be? */
-      XIMAGE_INSTANCE_YOFFSET (bglyph) = 0;
+int
+layout_layout (Lisp_Object image_instance, 
+              int width, int height, Lisp_Object domain)
+{
+  Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object rest;
+  Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii);
+  int x, y, maxph = 0, maxpw = 0, nitems = 0,
+    horiz_spacing, vert_spacing, ph_adjust = 0;
+  int gheight, gwidth;
+
+  /* If we are not initialized then we won't have any children. */
+  if (!IMAGE_INSTANCE_INITIALIZED (ii))
+      return 0;
 
-      ph_adjust = (glyph_height (border, Qnil, DEFAULT_INDEX, domain) / 2);
+  /* Pick up the border text if we have one. */
+  if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
+    {
+      Lisp_Object border = XCAR (items);
+      items = XCDR (items);
+      image_instance_query_geometry (border, &gwidth, &gheight,
+                                    IMAGE_DESIRED_GEOMETRY, domain);
+      /* #### Really, what should this be? */
+      XIMAGE_INSTANCE_XOFFSET (border) = 10;
+      XIMAGE_INSTANCE_YOFFSET (border) = 0;
+      ph_adjust = gheight / 2;
       IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (ph_adjust);
+
+      image_instance_layout (border, gwidth, gheight, domain);
     }
 
-  /* Work out vertical spacings. */
-  if (!ph)
+  /* Flip through the items to work out how much stuff we have to display. */
+  LIST_LOOP (rest, items)
     {
-      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;
+      Lisp_Object glyph = XCAR (rest);
+      
+      image_instance_query_geometry (glyph, &gwidth, &gheight, 
+                                    IMAGE_DESIRED_GEOMETRY, domain);
+      nitems ++;
+      if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) 
+         == LAYOUT_HORIZONTAL)
+       {
+         maxph = max (maxph, gheight);
+         maxpw += gwidth;
+       }
+      else
+       {
+         maxpw = max (maxpw, gwidth);
+         maxph += gheight;
+       }
     }
-  else if (ph < maxph)
+
+  /* work out spacing between items and bounds of the layout */
+  if (width < 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 (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) 
+          == LAYOUT_HORIZONTAL)
+    /* We have a larger area to display in so distribute the space
+       evenly. */
+    horiz_spacing = (width - (maxpw + 
+                             IMAGE_INSTANCE_MARGIN_WIDTH (ii) * 2)) 
+      / (nitems + 1);
+  else
+    horiz_spacing = (width - maxpw) / 2
+      - IMAGE_INSTANCE_MARGIN_WIDTH (ii);
+
+  if (height < maxph)
     vert_spacing = WIDGET_BORDER_HEIGHT * 2;
-  else if (EQ (orient, Qvertical))
-    vert_spacing = (ph - (maxph + ph_adjust)) / (nitems + 1);
+  else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) 
+          == LAYOUT_VERTICAL)
+    vert_spacing = (height - (maxph + ph_adjust + 
+                             IMAGE_INSTANCE_MARGIN_WIDTH (ii) * 2)) 
+      / (nitems + 1);
   else
-    vert_spacing = (ph - (maxph + ph_adjust)) / 2;
+    vert_spacing = (height - (maxph + ph_adjust)) / 2 
+      - IMAGE_INSTANCE_MARGIN_WIDTH (ii);
 
-  y = vert_spacing + ph_adjust;
-  x = horiz_spacing;
+  y = vert_spacing + ph_adjust + IMAGE_INSTANCE_MARGIN_WIDTH (ii);
+  x = horiz_spacing + IMAGE_INSTANCE_MARGIN_WIDTH (ii);
 
   /* Now flip through putting items where we want them, paying
-     attention to justification. */
+     attention to justification. Make sure we don't mess with the
+     border glyph. */
   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);
+      image_instance_query_geometry (glyph, &gwidth, &gheight, 
+                                    IMAGE_DESIRED_GEOMETRY, domain);
 
-      if (EQ (orient, Qhorizontal))
+      if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) 
+         == LAYOUT_HORIZONTAL)
        {
-         if (EQ (justify, Qright))
-           y = ph - (gheight + vert_spacing);
-         else if (EQ (justify, Qcenter))
-           y = (ph - gheight) / 2;
+         if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii) 
+             == LAYOUT_JUSTIFY_RIGHT)
+           y = height - (gheight + vert_spacing);
+         if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii) 
+             == LAYOUT_JUSTIFY_CENTER)
+           y = (height - gheight) / 2;
        }
-      else if (EQ (orient, Qvertical))
+      else 
        {
-         if (EQ (justify, Qright))
-           x = pw - (gwidth + horiz_spacing);
-         else if (EQ (justify, Qcenter))
-           x = (pw - gwidth) / 2;
+         if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii) 
+             == LAYOUT_JUSTIFY_RIGHT)
+           x = width - (gwidth + horiz_spacing);
+         if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii) 
+             == LAYOUT_JUSTIFY_CENTER)
+           x = (width - gwidth) / 2;
        }
-
-      XIMAGE_INSTANCE_XOFFSET (gii) = x;
-      XIMAGE_INSTANCE_YOFFSET (gii) = y;
-
-      if (EQ (orient, Qhorizontal))
+       
+      XIMAGE_INSTANCE_XOFFSET (glyph) = x;
+      XIMAGE_INSTANCE_YOFFSET (glyph) = y;
+       
+      if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) 
+         == LAYOUT_HORIZONTAL)
        {
          x += (gwidth + horiz_spacing);
        }
-      else if (EQ (orient, Qvertical))
+      else
        {
          y += (gheight + vert_spacing);
        }
+      
+      /* Now layout subwidgets if they require it. */
+      image_instance_layout (glyph, gwidth, gheight, domain);
     }
+  return 1;
+}
 
-  IMAGE_INSTANCE_LAYOUT_CHILDREN (ii) = children;
-  assert (pw && ph);
-  IMAGE_INSTANCE_SUBWINDOW_WIDTH (ii) = pw;
-  IMAGE_INSTANCE_SUBWINDOW_HEIGHT (ii) = ph;
+/* Layout subwindows if they are real subwindows. */
+static int
+native_layout_layout (Lisp_Object image_instance,
+                     int width, int height,
+                     Lisp_Object domain)
+{
+  Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object rest;
+  
+  /* The first time this gets called, the layout will be only
+     partially instantiated. The children get done in
+     post_instantiate. */
+  if (!IMAGE_INSTANCE_INITIALIZED (ii))
+    return 0;
+
+  /* Defining this overrides the default layout_layout so we first have to call that to get 
+     suitable instances and values set up. */
+  layout_layout (image_instance, width, height, domain);
+
+  LIST_LOOP (rest, IMAGE_INSTANCE_LAYOUT_CHILDREN (ii))
+    {
+      struct display_glyph_area dga;
+      dga.xoffset = 0;
+      dga.yoffset = 0;
+      dga.width = IMAGE_INSTANCE_WIDTH (ii);
+      dga.height = IMAGE_INSTANCE_HEIGHT (ii);
+
+      map_subwindow (XCAR (rest), 
+                    IMAGE_INSTANCE_XOFFSET (ii),
+                    IMAGE_INSTANCE_YOFFSET (ii), &dga);
+    }
+  return 1;
 }
 
 \f
@@ -779,11 +1260,11 @@ syms_of_glyphs_widget (void)
   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");
+  defkeyword (&Q_margin_width, ":margin-width");
 
   defsymbol (&Qetched_in, "etched-in");
   defsymbol (&Qetched_out, "etched-out");
@@ -791,123 +1272,208 @@ syms_of_glyphs_widget (void)
   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) \
+#define VALID_GUI_KEYWORDS(type) do {                                                  \
+  IIFORMAT_VALID_NONCOPY_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_NONCOPY_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_NONCOPY_KEYWORD (type, Q_callback, check_valid_callback);             \
+  IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback_ex, check_valid_callback);          \
+  IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_descriptor, check_valid_string_or_vector);   \
+} while (0)
+
+#define VALID_WIDGET_KEYWORDS(type) do {                               \
   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)
+  IIFORMAT_VALID_KEYWORD (type, Q_pixel_width, check_valid_int_or_function);\
+  IIFORMAT_VALID_KEYWORD (type, Q_pixel_height, check_valid_int_or_function);\
+  IIFORMAT_VALID_KEYWORD (type, Q_face, check_valid_face);             \
+} while (0)
 
-  /* we only do this for properties */
+
+static void image_instantiator_widget (void)
+{ /* 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);
+  IIFORMAT_HAS_METHOD (widget, query_geometry);
+  IIFORMAT_HAS_METHOD (widget, layout);
+}
 
-  /* widget image-instantiator types - buttons */
+static void image_instantiator_buttons (void)
+{
   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, post_instantiate, widget);
   IIFORMAT_HAS_SHARED_METHOD (button, normalize, widget);
-  IIFORMAT_VALID_KEYWORD (button, 
+  IIFORMAT_HAS_SHARED_METHOD (button, governing_domain, subwindow);
+  IIFORMAT_HAS_METHOD (button, query_geometry);
+  IIFORMAT_VALID_KEYWORD (button,
                          Q_image, check_valid_glyph_or_instantiator);
   VALID_WIDGET_KEYWORDS (button);
   VALID_GUI_KEYWORDS (button);
+}
 
-  /* edit fields */
+static void image_instantiator_edit_fields (void)
+{
   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);
+  IIFORMAT_HAS_SHARED_METHOD (edit_field, post_instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (edit_field, governing_domain, subwindow);
   VALID_WIDGET_KEYWORDS (edit_field);
   VALID_GUI_KEYWORDS (edit_field);
+}
 
-  /* combo box */
+static void image_instantiator_combo_box (void)
+{
   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (combo_box, "combo-box");
   IIFORMAT_HAS_METHOD (combo_box, validate);
   IIFORMAT_HAS_SHARED_METHOD (combo_box, possible_dest_types, widget);
+  IIFORMAT_HAS_SHARED_METHOD (combo_box, governing_domain, subwindow);
+
   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_pixel_width, check_valid_int_or_function);
   IIFORMAT_VALID_KEYWORD (combo_box, Q_face, check_valid_face);
   IIFORMAT_VALID_KEYWORD (combo_box, Q_properties, check_valid_item_list);
+}
 
-  /* scrollbar */
+static void image_instantiator_scrollbar (void)
+{
   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);
+  IIFORMAT_HAS_SHARED_METHOD (scrollbar, post_instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (scrollbar, governing_domain, subwindow);
   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_pixel_width, check_valid_int_or_function);
+  IIFORMAT_VALID_KEYWORD (scrollbar, Q_pixel_height, check_valid_int_or_function);
   IIFORMAT_VALID_KEYWORD (scrollbar, Q_face, check_valid_face);
+}
 
-  /* progress guage */
+static void image_instantiator_progress_guage (void)
+{
   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);
+  IIFORMAT_HAS_SHARED_METHOD (progress_gauge, post_instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (progress_gauge, governing_domain, subwindow);
+  IIFORMAT_HAS_METHOD (progress_gauge, set_property);
   VALID_WIDGET_KEYWORDS (progress_gauge);
   VALID_GUI_KEYWORDS (progress_gauge);
+}
 
-  /* tree view */
+static void image_instantiator_tree_view (void)
+{
   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);
+  IIFORMAT_HAS_SHARED_METHOD (tree_view, instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (tree_view, post_instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (tree_view, governing_domain, subwindow);
+  IIFORMAT_HAS_METHOD (tree_view, query_geometry);
   VALID_WIDGET_KEYWORDS (tree_view);
   VALID_GUI_KEYWORDS (tree_view);
   IIFORMAT_VALID_KEYWORD (tree_view, Q_properties, check_valid_item_list);
+}
 
-  /* tab control */
+static void image_instantiator_tab_control (void)
+{
   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);
+  IIFORMAT_HAS_SHARED_METHOD (tab_control, instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (tab_control, post_instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (tab_control, governing_domain, subwindow);
+  IIFORMAT_HAS_METHOD (tab_control, query_geometry);
+  IIFORMAT_HAS_METHOD (tab_control, set_property);
   VALID_WIDGET_KEYWORDS (tab_control);
   VALID_GUI_KEYWORDS (tab_control);
+  IIFORMAT_VALID_KEYWORD (tab_control, Q_orientation, check_valid_tab_orientation);
   IIFORMAT_VALID_KEYWORD (tab_control, Q_properties, check_valid_item_list);
+}
 
-  /* labels */
+static void image_instantiator_labels (void)
+{
   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (label, "label");
   IIFORMAT_HAS_SHARED_METHOD (label, possible_dest_types, widget);
-  IIFORMAT_HAS_SHARED_METHOD (label, instantiate, static);
+  IIFORMAT_HAS_SHARED_METHOD (label, instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (label, post_instantiate, widget);
+  IIFORMAT_HAS_SHARED_METHOD (label, governing_domain, subwindow);
   VALID_WIDGET_KEYWORDS (label);
   IIFORMAT_VALID_KEYWORD (label, Q_descriptor, check_valid_string);
+}
+
+#define VALID_LAYOUT_KEYWORDS(layout) \
+  VALID_WIDGET_KEYWORDS (layout);                                              \
+  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_margin_width, check_valid_int);    \
+  IIFORMAT_VALID_KEYWORD (layout, Q_items,                             \
+                         check_valid_glyph_or_instantiator_list)
 
-  /* layout */
+static void image_instantiator_layout (void)
+{
   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (layout, "layout");
-  IIFORMAT_HAS_METHOD (layout, possible_dest_types);
+  IIFORMAT_HAS_SHARED_METHOD (layout, possible_dest_types, widget);
   IIFORMAT_HAS_METHOD (layout, instantiate);
+  IIFORMAT_HAS_METHOD (layout, post_instantiate);
+  IIFORMAT_HAS_SHARED_METHOD (layout, governing_domain, subwindow);
   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);
+  IIFORMAT_HAS_METHOD (layout, query_geometry);
+  IIFORMAT_HAS_METHOD (layout, layout);
+
+  VALID_GUI_KEYWORDS (layout);
+  VALID_LAYOUT_KEYWORDS (layout);
+}
+
+static void image_instantiator_native_layout (void)
+{
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (native_layout, "native-layout");
+  IIFORMAT_HAS_SHARED_METHOD (native_layout, possible_dest_types, widget);
+  IIFORMAT_HAS_SHARED_METHOD (native_layout, instantiate, layout);
+  IIFORMAT_HAS_SHARED_METHOD (native_layout, post_instantiate, layout);
+  IIFORMAT_HAS_METHOD (native_layout, layout);
+  IIFORMAT_HAS_SHARED_METHOD (native_layout, governing_domain, subwindow);
+  IIFORMAT_HAS_SHARED_METHOD (native_layout, normalize, layout);
+  IIFORMAT_HAS_SHARED_METHOD (native_layout, query_geometry, layout);
+  IIFORMAT_HAS_SHARED_METHOD (native_layout, layout, layout);
+
+  VALID_GUI_KEYWORDS (native_layout);
+  VALID_LAYOUT_KEYWORDS (native_layout);
+}
+
+void
+image_instantiator_format_create_glyphs_widget (void)
+{
+  image_instantiator_widget();
+  image_instantiator_buttons();
+  image_instantiator_edit_fields();
+  image_instantiator_combo_box();
+  image_instantiator_scrollbar();
+  image_instantiator_progress_guage();
+  image_instantiator_tree_view();
+  image_instantiator_tab_control();
+  image_instantiator_labels();
+  image_instantiator_layout();
+  image_instantiator_native_layout();
 }
 
 void