+ 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, luh;
+
+ /* 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;
+
+ luh = widget_logical_unit_height (ii);
+
+ /* Pick up the border text if we have one. */
+ if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
+ {
+ glyph_query_geometry (XCAR (items), &gwidth, &gheight, disp,
+ image_instance);
+ ph_adjust = gheight;
+ /* Include text width in vertical layouts. */
+ if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) == LAYOUT_VERTICAL)
+ maxpw = gwidth + BORDER_FIDDLE_FACTOR;
+ 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);
+ glyph_query_geometry (glyph, &gwidth, &gheight, disp, image_instance);
+
+ 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 (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii)) {
+ 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_instance_border_width (ii) +
+ IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2;
+ }
+ else
+ {
+ *width = maxpw + 2 * (widget_instance_border_width (ii) * 2 +
+ IMAGE_INSTANCE_MARGIN_WIDTH (ii));
+ }
+ }
+
+ /* Work out vertical spacings. */
+ if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii)) {
+ 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_LOGICAL_LAYOUT (ii))
+ {
+ *height = nitems * luh + ph_adjust;
+ }
+ else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) == LAYOUT_VERTICAL)
+ {
+ *height = maxph + ((nitems + 1) * widget_instance_border_width (ii) +
+ IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
+ }
+ else
+ {
+ *height = maxph + (2 * widget_instance_border_width (ii) +
+ IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
+ }
+ }
+#ifdef DEBUG_WIDGET_OUTPUT
+ stderr_out ("layout wants %dx%d\n", *width, *height);
+#endif
+}
+
+int
+layout_layout (Lisp_Object image_instance,
+ int width, int height, int xoffset, int yoffset,
+ 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;
+ /* See comments in widget_logical_unit_height(). */
+ int luh = widget_logical_unit_height (ii);
+
+ /* If we are not initialized then we won't have any children. */
+ if (!IMAGE_INSTANCE_INITIALIZED (ii))
+ return 0;
+
+#ifdef DEBUG_WIDGET_OUTPUT
+ stderr_out ("layout output %dx%d\n", width, height);
+#endif
+
+ /* Pick up the border text if we have one. A border can have the
+ values Qetched_in, Qetched_out, Qbevel_in, Qbevel_out or an
+ integer. The first four just affect the display properties of the
+ border that is drawn. The last is an offset and implies that the
+ first item in the list of subcontrols is a text control that
+ should be displayed on the border. */
+ if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
+ {
+ Lisp_Object border = XCAR (items);
+ items = XCDR (items);
+ glyph_query_geometry (border, &gwidth, &gheight,
+ IMAGE_DESIRED_GEOMETRY, image_instance);
+ /* The vertical offset for subsequent items is the full height
+ of the border glyph. */
+ ph_adjust = gheight;
+ /* The offset for the border is half the glyph height. */
+ IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (gheight / 2);
+
+ /* #### Really, what should this be? */
+ glyph_do_layout (border, gwidth, gheight, BORDER_FIDDLE_FACTOR, 0,
+ image_instance);
+ }
+
+ /* Flip through the items to work out how much stuff we have to display. */
+ LIST_LOOP (rest, items)
+ {
+ Lisp_Object glyph = XCAR (rest);
+
+ glyph_query_geometry (glyph, &gwidth, &gheight,
+ IMAGE_DESIRED_GEOMETRY, image_instance);
+ 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_spacing (IMAGE_INSTANCE_DOMAIN (ii));
+ 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);
+
+ /* We are trying here to get widgets to line up when they are left
+ or right justified vertically. This means that we must position
+ widgets on logical unit boundaries, even though their height may
+ be greater or less than a logical unit. In order to avoid
+ clipping we need to determine how big the widget wants to be and
+ then allocate as many logical units as necessary in order to
+ accommodate it. */
+ if (height < maxph)
+ vert_spacing = widget_spacing (IMAGE_INSTANCE_DOMAIN (ii)) * 2;
+ else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
+ == LAYOUT_VERTICAL)
+ {
+ if (!IMAGE_INSTANCE_SUBWINDOW_V_CENTERED (ii))
+ vert_spacing = widget_spacing (IMAGE_INSTANCE_DOMAIN (ii)) * 2;
+ else
+ vert_spacing = (height - (maxph + ph_adjust +
+ IMAGE_INSTANCE_MARGIN_WIDTH (ii) * 2))
+ / (nitems + 1);
+ }
+ else
+ vert_spacing = (height - (maxph + ph_adjust)) / 2
+ - IMAGE_INSTANCE_MARGIN_WIDTH (ii);
+
+ y = yoffset = 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. Make sure we don't mess with the
+ border glyph. */
+ LIST_LOOP (rest, items)
+ {
+ Lisp_Object glyph = XCAR (rest);
+
+ glyph_query_geometry (glyph, &gwidth, &gheight,
+ IMAGE_DESIRED_GEOMETRY, image_instance);
+
+ if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) == LAYOUT_HORIZONTAL)
+ {
+ if (IMAGE_INSTANCE_SUBWINDOW_BOTTOM_JUSTIFIED (ii))
+ y = height - (gheight + vert_spacing);
+ else if (IMAGE_INSTANCE_SUBWINDOW_V_CENTERED (ii))
+ y = (height - gheight) / 2;
+ }
+ else
+ {
+ if (IMAGE_INSTANCE_SUBWINDOW_RIGHT_JUSTIFIED (ii))
+ x = width - (gwidth + horiz_spacing);
+ else if (IMAGE_INSTANCE_SUBWINDOW_H_CENTERED (ii))
+ x = (width - gwidth) / 2;
+ }
+
+ /* Now layout subwidgets if they require it. */
+ glyph_do_layout (glyph, gwidth, gheight, x, y, image_instance);
+
+ if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) == LAYOUT_HORIZONTAL)
+ {
+ x += (gwidth + horiz_spacing);
+ }
+ else
+ {
+ y += (gheight + vert_spacing);
+ if (!IMAGE_INSTANCE_SUBWINDOW_V_CENTERED (ii))
+ {
+ /* justified, vertical layout, try and align on logical unit
+ boundaries. */
+ y = ROUND_UP (y - yoffset, luh) + yoffset;
+ }
+ }
+
+ }
+ return 1;
+}
+
+/* Get the glyphs that comprise a layout. These are created internally
+ and so are otherwise inaccessible to lisp. We need some way of getting
+ properties from the widgets that comprise a layout and this is the
+ simplest way of doing it.
+
+ #### Eventually we should allow some more intelligent access to
+ sub-widgets. */
+static Lisp_Object
+layout_property (Lisp_Object image_instance, Lisp_Object prop)
+{
+ /* This function can GC. */
+ Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+ if (EQ (prop, Q_items))
+ {
+ if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)) &&
+ CONSP (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii)))
+ return Fcopy_sequence (XCDR
+ (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii)));
+ else
+ return Fcopy_sequence (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii));
+ }
+ return Qunbound;
+}
+
+/* Layout subwindows if they are real subwindows. */
+static int
+native_layout_layout (Lisp_Object image_instance,
+ int width, int height, int xoffset, int yoffset,
+ 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, xoffset, yoffset, 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;
+}
+
+DEFUN ("widget-logical-to-character-width", Fwidget_logical_to_character_width, 1, 3, 0, /*
+Convert the width in logical widget units to characters.
+Logical widget units do not take into account adjusments made for
+layout borders, so this adjusment is approximated.
+*/
+ (width, face, domain))
+{
+ int w, neww, charwidth;
+ int border_width = DEFAULT_WIDGET_BORDER_WIDTH;
+
+ if (NILP (domain))
+ domain = Fselected_frame (Qnil);
+
+ CHECK_INT (width);
+ w = XINT (width);
+
+ if (HAS_DEVMETH_P (DOMAIN_XDEVICE (domain), widget_border_width))
+ border_width = DEVMETH (DOMAIN_XDEVICE (domain), widget_border_width, ());
+
+ default_face_font_info (domain, 0, 0, 0, &charwidth, 0);
+ neww = ROUND_UP (charwidth * w + 4 * border_width + 2 * widget_spacing (domain),
+ charwidth) / charwidth;
+
+ return make_int (neww);
+}
+
+DEFUN ("widget-logical-to-character-height", Fwidget_logical_to_character_height, 1, 3, 0, /*
+Convert the height in logical widget units to characters.
+Logical widget units do not take into account adjusments made for
+layout borders, so this adjustment is approximated.
+
+If the components of a widget layout are justified to the top or the
+bottom then they are aligned in terms of `logical units'. This is a
+size quantity that is designed to be big enough to accomodate the
+largest `single height' widget. It is dependent on the widget face and
+some combination of spacing and border-width. Thus if you specify top
+or bottom justification in a vertical layout the subcontrols are laid
+out one per logical unit. This allows adjoining layouts to have
+identical alignment for their subcontrols.
+
+Since frame sizes are measured in characters, this function allows you
+to do appropriate conversion between logical units and characters.
+*/
+ (height, face, domain))
+{
+ int h, newh, charheight;
+
+ CHECK_INT (height);
+ if (NILP (domain))
+ domain = Fselected_frame (Qnil);
+
+ h = XINT (height);
+
+ default_face_font_info (domain, 0, 0, &charheight, 0, 0);
+ newh = ROUND_UP (logical_unit_height (Fsymbol_name (Qwidget),
+ Vwidget_face, domain) * h, charheight)
+ / charheight;
+
+ return make_int (newh);