+ 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_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 = XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii));
+ Lisp_Object rest;
+ int tw = 0, th = 0;
+
+ LIST_LOOP (rest, items)
+ {
+ 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);
+ }
+
+ /* 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;
+ }
+}
+
+/* Update the contents of a tab control. */
+static void
+tab_control_update (Lisp_Object image_instance,
+ Lisp_Object instantiator)
+{
+ Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
+ /* Record new items for update. *_tab_control_redisplay will do the
+ rest. */
+ if (!NILP (items))
+ {
+ Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
+ check_valid_item_list (items);
+#ifdef DEBUG_WIDGET_OUTPUT
+ stderr_out ("tab control %p updated\n", IMAGE_INSTANCE_SUBWINDOW_ID (ii));
+#endif
+ /* 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 (items));
+ IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii) = 1;
+ }
+}
+
+/* Determine whether only the order has changed for a tab. */
+int tab_control_order_only_changed (Lisp_Object image_instance)
+{
+ Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+ int found = 0, len, pending_len;
+ Lisp_Object rest;
+
+ /* Degenerate case. */
+ if (NILP (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
+ return 1;
+
+ /* See whether we just need a change in order. */
+ GET_LIST_LENGTH (IMAGE_INSTANCE_WIDGET_ITEMS (ii), len);
+ GET_LIST_LENGTH (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii),
+ pending_len);
+ if (len == pending_len)
+ {
+ LIST_LOOP (rest, XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii)))
+ {
+ Lisp_Object pending_rest;
+ found = 0;
+ LIST_LOOP (pending_rest,
+ XCDR (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
+ {
+ if (gui_item_equal_sans_selected (XCAR (rest),
+ XCAR (pending_rest), 0))
+ {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+ }
+ return found;
+}
+
+/* Set the properties of a progress gauge */
+static void
+progress_gauge_update (Lisp_Object image_instance,
+ Lisp_Object instantiator)
+{
+ Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+ Lisp_Object value = find_keyword_in_vector (instantiator, Q_value);
+
+ if (!NILP (value))
+ {
+ CHECK_INT (value);
+#ifdef DEBUG_WIDGET_OUTPUT
+ stderr_out ("progress gauge value set to %ld\n", XINT (value));
+#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 = value;
+
+ IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii) = 1;
+ }
+}
+
+\f
+/*****************************************************************************
+ * widget layout *
+ *****************************************************************************/
+/* We need to cascade normalization.*/
+static Lisp_Object
+layout_normalize (Lisp_Object inst, Lisp_Object console_type,
+ Lisp_Object dest_mask)
+{
+ /* This function can call lisp */
+ struct gcpro gcpro1, gcpro2;
+ Lisp_Object alist = Qnil, new_items = Qnil, border;
+ /* This function can call lisp */
+ Lisp_Object items;
+
+ GCPRO2 (alist, new_items);
+ alist = tagged_vector_to_alist (inst);
+ items = assq_no_quit (Q_items, alist);
+
+ /* We need to normalize sub-objects. */
+ if (!NILP (items))
+ {
+ Lisp_Object rest;
+ LIST_LOOP (rest, XCDR (items))
+ {
+ /* Substitute the new instantiator */
+ new_items = Fcons (normalize_image_instantiator (XCAR (rest),
+ console_type, dest_mask),
+ new_items);
+ }
+ new_items = Fnreverse (new_items);
+ Fsetcdr (items, new_items);
+ }
+ /* Normalize the border spec. */
+ border = assq_no_quit (Q_border, alist);
+ if (!NILP (border) && VECTORP (XCDR (border)))
+ {
+ Fsetcdr (border, normalize_image_instantiator (XCDR (border),
+ console_type, dest_mask));
+ }
+
+ {
+ Lisp_Object result = alist_to_tagged_vector (XVECTOR_DATA (inst)[0],
+ alist);
+ free_alist (alist);
+ RETURN_UNGCPRO (result);
+ }
+}
+
+/* Update the instances in the layout. */
+static void
+layout_update (Lisp_Object image_instance, Lisp_Object instantiator)
+{
+ Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+ Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
+ Lisp_Object border_inst = find_keyword_in_vector (instantiator, Q_border);
+ Lisp_Object border = Qnil;
+ Lisp_Object children = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii);
+ int structure_changed = 0;
+ struct gcpro gcpro1;
+
+ /* We want to avoid consing if we can. This is quite awkward because
+ we have to deal with the border as well as the items. */
+
+ GCPRO1 (border);
+
+ if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
+ {
+ border = XCAR (children);
+ children = XCDR (children);
+ }
+
+#ifdef DEBUG_WIDGET_OUTPUT
+ stderr_out ("layout updated\n");
+#endif
+ /* Update the border. */
+ if (!NILP (border_inst))
+ {
+ if (VECTORP (border_inst))
+ {
+ /* 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. */
+ if (!NILP (border))
+ call3 (Qset_glyph_image, border, border_inst,
+ IMAGE_INSTANCE_DOMAIN (ii));
+ else
+ {
+ border = Fcons (call1 (Qmake_glyph, border_inst), Qnil);
+ structure_changed = 1;
+ }
+ IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (0);
+ }