+ 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
+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;
+}
+
+/* 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, unsigned int* width,
+ unsigned 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;
+ unsigned int gheight, gwidth;
+
+ /* 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);
+ image_instance_query_geometry (glyph, &gwidth, &gheight, disp, domain);
+
+ nitems ++;
+ if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+ == LAYOUT_HORIZONTAL)
+ {
+ maxph = max (maxph, gheight);
+ maxpw += gwidth;
+ }
+ else
+ {
+ maxpw = max (maxpw, gwidth);
+ maxph += gheight;
+ }
+ }
+
+ /* 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)))
+ {
+ Lisp_Object dynamic_width =
+ Feval (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii));
+ if (INTP (dynamic_width))
+ *width = XINT (dynamic_width);
+ }
+ else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+ == LAYOUT_HORIZONTAL)
+ *width = maxpw + (nitems + 1) * WIDGET_BORDER_WIDTH * 2;
+ else
+ *width = maxpw + 2 * WIDGET_BORDER_WIDTH * 2;
+
+ /* Work out vertical spacings. */
+ if (!NILP (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii)))
+ {
+ 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 * 2 + ph_adjust;
+ else
+ *height = maxph + 2 * WIDGET_BORDER_HEIGHT * 2 + ph_adjust;
+}
+
+static void
+layout_layout (Lisp_Object image_instance,
+ unsigned int width, unsigned 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;
+ unsigned int gheight, gwidth;
+
+ /* 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);
+ }
+
+ /* Flip through the items to work out how much stuff we have to display. */
+ LIST_LOOP (rest, items)
+ {
+ 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;
+ }
+ }
+
+ /* 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) / (nitems + 1);
+ else
+ horiz_spacing = (width - maxpw) / 2;
+
+ if (height < maxph)
+ vert_spacing = WIDGET_BORDER_HEIGHT * 2;
+ else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+ == LAYOUT_VERTICAL)
+ vert_spacing = (height - (maxph + ph_adjust)) / (nitems + 1);
+ else
+ vert_spacing = (height - (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. Make sure we don't mess with the
+ border glyph. */
+ LIST_LOOP (rest, items)
+ {
+ Lisp_Object glyph = XCAR (rest);
+
+ image_instance_query_geometry (glyph, &gwidth, &gheight,
+ IMAGE_DESIRED_GEOMETRY, domain);
+
+ if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+ == LAYOUT_HORIZONTAL)
+ {
+ 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 (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 (glyph) = x;
+ XIMAGE_INSTANCE_YOFFSET (glyph) = y;
+
+ if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+ == LAYOUT_HORIZONTAL)
+ {
+ x += (gwidth + horiz_spacing);
+ }
+ else
+ {
+ y += (gheight + vert_spacing);
+ }
+
+ /* Now layout subwidgets if they require it. */
+ image_instance_layout (glyph, gwidth, gheight, domain);
+ }