Build Fix -- compatibility issue with newer autoconf
[sxemacs] / src / ui / glyphs-widget.c
1 /* Widget-specific glyph objects.
2    Copyright (C) 1998, 1999, 2000, 2002 Andy Piper.
3
4 This file is part of SXEmacs
5
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19
20 /* Synched up with: Not in FSF. */
21
22 /* written by Andy Piper <andy@xemacs.org> */
23
24 #include <config.h>
25 #include "lisp.h"
26 #include "lstream.h"
27 #include "console.h"
28 #include "device.h"
29 #include "faces.h"
30 #include "glyphs.h"
31 #include "objects.h"
32 #include "bytecode.h"
33 #include "window.h"
34 #include "buffer.h"
35 #include "frame.h"
36 #include "insdel.h"
37 #include "opaque.h"
38
39 DEFINE_IMAGE_INSTANTIATOR_FORMAT(button);
40 DEFINE_IMAGE_INSTANTIATOR_FORMAT(combo_box);
41 Lisp_Object Qcombo_box;
42 DEFINE_IMAGE_INSTANTIATOR_FORMAT(edit_field);
43 Lisp_Object Qedit_field;
44 DEFINE_IMAGE_INSTANTIATOR_FORMAT(scrollbar);
45 Lisp_Object Qscrollbar;
46 DEFINE_IMAGE_INSTANTIATOR_FORMAT(widget);
47 DEFINE_IMAGE_INSTANTIATOR_FORMAT(label);
48 Lisp_Object Qlabel;
49 DEFINE_IMAGE_INSTANTIATOR_FORMAT(progress_gauge);
50 Lisp_Object Qprogress_gauge;
51 DEFINE_IMAGE_INSTANTIATOR_FORMAT(tree_view);
52 Lisp_Object Qtree_view;
53 DEFINE_IMAGE_INSTANTIATOR_FORMAT(tab_control);
54 Lisp_Object Qtab_control;
55 DEFINE_IMAGE_INSTANTIATOR_FORMAT(layout);
56 Lisp_Object Qlayout;
57 DEFINE_IMAGE_INSTANTIATOR_FORMAT(native_layout);
58 Lisp_Object Qnative_layout;
59
60 Lisp_Object Qetched_in, Qetched_out, Qbevel_in, Qbevel_out;
61 Lisp_Object Qmake_glyph;
62 Lisp_Object Vwidget_border_width;
63
64 static int widget_border_width(Lisp_Object domain);
65 static int widget_spacing(Lisp_Object domain);
66 #define BORDER_FIDDLE_FACTOR 10
67 #ifdef DEBUG_WIDGETS
68 int debug_widget_instances;
69 #endif
70
71 /* TODO:
72    - tooltips for controls, especially buttons.
73    - keyboard traversal.
74    - lisp configurable layout.
75  */
76
77 /* In MS-Windows normal windows work in pixels, dialog boxes work in
78    dialog box units. Why? sigh. We could reuse the metrics for dialogs
79    if this were not the case. As it is we have to position things
80    pixel wise. I'm not even sure that X has this problem at least for
81    buttons in groups. */
82 static int widget_possible_dest_types(void)
83 {
84         return IMAGE_WIDGET_MASK;
85 }
86
87 static void check_valid_instantiator(Lisp_Object data)
88 {
89         Lisp_Object glyph = data;
90         if (SYMBOLP(data))
91                 glyph = XSYMBOL(data)->value;
92
93         if (!CONSP(glyph) && !VECTORP(glyph))
94                 invalid_argument("instantiator item must be a vector", data);
95 }
96
97 static void check_valid_orientation(Lisp_Object data)
98 {
99         if (!EQ(data, Qhorizontal)
100             && !EQ(data, Qvertical))
101                 invalid_argument("unknown orientation for layout", data);
102 }
103
104 static void check_valid_tab_orientation(Lisp_Object data)
105 {
106         if (!EQ(data, Qtop)
107             && !EQ(data, Qbottom)
108             && !EQ(data, Qleft)
109             && !EQ(data, Qright))
110                 invalid_argument("unknown orientation for tab control", data);
111 }
112
113 static void check_valid_justification(Lisp_Object data)
114 {
115         if (!EQ(data, Qleft)
116             && !EQ(data, Qright)
117             && !EQ(data, Qtop)
118             && !EQ(data, Qbottom)
119             && !EQ(data, Qcenter))
120                 invalid_argument("unknown justification for layout", data);
121 }
122
123 static void check_valid_border(Lisp_Object data)
124 {
125         if (!EQ(data, Qt) && !EQ(data, Qetched_in) && !EQ(data, Qetched_out)
126             && !EQ(data, Qbevel_in) && !EQ(data, Qbevel_out)
127             && !GLYPHP(data) && !VECTORP(data))
128                 invalid_argument("unknown border style for layout", data);
129 }
130
131 static void check_valid_anything(Lisp_Object data)
132 {
133 }
134
135 static void check_valid_callback(Lisp_Object data)
136 {
137         if (!SYMBOLP(data)
138             && !COMPILED_FUNCTIONP(data)
139             && !CONSP(data)) {
140                 invalid_argument(":callback must be a function or expression",
141                                  data);
142         }
143 }
144
145 static void check_valid_int_or_function(Lisp_Object data)
146 {
147         if (!INTP(data) && !CONSP(data) && !SYMBOLP(data))
148                 invalid_argument("must be an integer or expresssion", data);
149 }
150
151 static void check_valid_symbol(Lisp_Object data)
152 {
153         CHECK_SYMBOL(data);
154 }
155
156 static void check_valid_string_or_vector(Lisp_Object data)
157 {
158         if (!STRINGP(data) && !VECTORP(data))
159                 invalid_argument(":descriptor must be a string or a vector",
160                                  data);
161 }
162
163 void check_valid_item_list(Lisp_Object items)
164 {
165         Lisp_Object rest;
166
167         CHECK_LIST(items);
168         EXTERNAL_LIST_LOOP(rest, items) {
169                 if (STRINGP(XCAR(rest)))
170                         CHECK_STRING(XCAR(rest));
171                 else if (VECTORP(XCAR(rest)))
172                         gui_parse_item_keywords(XCAR(rest));
173                 else if (LISTP(XCAR(rest)))
174                         check_valid_item_list(XCAR(rest));
175                 else
176                         invalid_argument
177                             ("Items must be vectors, lists or strings", items);
178         }
179 }
180
181 static void check_valid_instantiator_list(Lisp_Object data)
182 {
183         Lisp_Object rest;
184
185         CHECK_LIST(data);
186         EXTERNAL_LIST_LOOP(rest, data) {
187                 check_valid_instantiator(XCAR(rest));
188         }
189 }
190
191 static Lisp_Object glyph_instantiator_to_glyph(Lisp_Object sym)
192 {
193         /* This function calls lisp. */
194         Lisp_Object glyph = sym;
195         struct gcpro gcpro1;
196
197         GCPRO1(glyph);
198         /* if we have a symbol get at the actual data */
199         if (SYMBOLP(glyph))
200                 glyph = XSYMBOL(glyph)->value;
201
202         if (CONSP(glyph))
203                 glyph = Feval(glyph);
204
205         /* Be really helpful to the user. */
206         if (VECTORP(glyph)) {
207                 glyph = call1(Qmake_glyph, glyph);
208         }
209
210         /* substitute the new glyph */
211         RETURN_UNGCPRO(glyph);
212 }
213
214 static void
215 substitute_keyword_value(Lisp_Object inst, Lisp_Object key, Lisp_Object val)
216 {
217         int i;
218         /* substitute the new glyph */
219         for (i = 0; i < XVECTOR_LENGTH(inst); i++) {
220                 if (EQ(key, XVECTOR_DATA(inst)[i])) {
221                         XVECTOR_DATA(inst)[i + 1] = val;
222                         break;
223                 }
224         }
225 }
226
227 /* Determine the border with of the widget. */
228 static int widget_border_width(Lisp_Object domain)
229 {
230         /* #### FIXME -- need to use specifiers (Vwidget_border_width) for
231            some portion of this. */
232         if (HAS_DEVMETH_P(DOMAIN_XDEVICE(domain), widget_border_width))
233                 return DEVMETH(DOMAIN_XDEVICE(domain), widget_border_width, ());
234         else
235                 return DEFAULT_WIDGET_BORDER_WIDTH;
236 }
237
238 static int widget_instance_border_width(Lisp_Image_Instance * ii)
239 {
240         return widget_border_width(IMAGE_INSTANCE_DOMAIN(ii));
241 }
242
243 /* #### Its not clear to me what the value of logical_unit_height should
244    be, or whether it should even depend on the current
245    image_instance. It really should probably only depend on the
246    default widget face and the domain, however you can envisage users
247    wanting different logical units for nested layouts - so using the
248    properties of the current lahyout is probably not so dumb. */
249 static int
250 logical_unit_height(Lisp_Object text, Lisp_Object face, Lisp_Object domain)
251 {
252         int charheight = 0;
253         query_string_geometry(text, face, 0, &charheight, 0, domain);
254         /* For the returned value to be useful it needs to be big enough to
255            accomodate the largest single-height widget. This is currently
256            the edit-field. */
257         return charheight + 2 * widget_spacing(domain)
258             + 4 * widget_border_width(domain);
259 }
260
261 static int widget_logical_unit_height(Lisp_Image_Instance * ii)
262 {
263         return logical_unit_height(NILP(IMAGE_INSTANCE_WIDGET_TEXT(ii)) ?
264                                    NILP(IMAGE_INSTANCE_NAME(ii)) ?
265                                    Fsymbol_name(Qwidget)
266                                    : IMAGE_INSTANCE_NAME(ii)
267                                    : IMAGE_INSTANCE_WIDGET_TEXT(ii),
268                                    IMAGE_INSTANCE_WIDGET_FACE(ii),
269                                    IMAGE_INSTANCE_DOMAIN(ii));
270 }
271
272 /* Wire widget property invocations to specific widgets. The problem
273    we are solving here is that when instantiators get converted to
274    instances they lose some type information (they just become
275    subwindows or widgets for example). For widgets we need to preserve
276    this type information so that we can do widget specific operations
277    on the instances. This is encoded in the widget type
278    field. widget_property gets invoked by decoding the primary type
279    (Qwidget), <widget>_property then invokes based on the secondary
280    type (Qedit_field for example). It is debatable whether we should
281    wire things in this generalised way rather than treating widgets
282    specially in image_instance_property. */
283 static Lisp_Object widget_property(Lisp_Object image_instance, Lisp_Object prop)
284 {
285         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
286         struct image_instantiator_methods *meths;
287 #if 0                           /* The usefulness of this is dubious. */
288         /* first see if its a general property ... */
289         if (!NILP(Fplist_member(IMAGE_INSTANCE_WIDGET_PROPS(ii), prop)))
290                 return Fplist_get(IMAGE_INSTANCE_WIDGET_PROPS(ii), prop, Qnil);
291 #endif
292         /* .. then try device specific methods ... */
293         meths = decode_device_ii_format(image_instance_device(image_instance),
294                                         IMAGE_INSTANCE_WIDGET_TYPE(ii),
295                                         ERROR_ME_NOT);
296         if (meths && HAS_IIFORMAT_METH_P(meths, property))
297                 return IIFORMAT_METH(meths, property, (image_instance, prop));
298         /* ... then format specific methods ... */
299         meths = decode_device_ii_format(Qnil, IMAGE_INSTANCE_WIDGET_TYPE(ii),
300                                         ERROR_ME_NOT);
301         if (meths && HAS_IIFORMAT_METH_P(meths, property))
302                 return IIFORMAT_METH(meths, property, (image_instance, prop));
303         /* ... then fail */
304         return Qunbound;
305 }
306
307 /* Update the displayed properties of a widget.
308
309    #### This has been adapted from the original set_property functions
310    and thus reuses the state management of that. A better solution is
311    to simply re-parse the instantiator when items need updating. This
312    make comparing differences much simpler and obviates the need for a
313    lot of the state variables.
314
315    #### property is still a valid function since we have to be able to
316    extract information from the actual widget.
317
318    #### update_widget should probably be re-written to use the
319    instantiator. We probably want to keep a record of the differences
320    also to make this easy. We would also need a pending_instantiator
321    so that changes could be delayed. */
322 static void widget_update(Lisp_Object image_instance, Lisp_Object instantiator)
323 {
324         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
325         struct image_instantiator_methods *meths;
326         struct gcpro gcpro1;
327
328         Lisp_Object text = find_keyword_in_vector(instantiator, Q_text);
329         Lisp_Object desc = find_keyword_in_vector(instantiator, Q_descriptor);
330         Lisp_Object items = find_keyword_in_vector(instantiator, Q_items);
331         Lisp_Object descriptor_item = Qnil;
332
333         GCPRO1(descriptor_item);
334
335         /* Pick up any generic properties that we might need to keep hold
336            of.
337            #### This is potentially bogus because it is changing the items
338            in place rather than in the pending items. */
339         if (!NILP(text)) {
340                 IMAGE_INSTANCE_WIDGET_TEXT(ii) = text;
341                 IMAGE_INSTANCE_TEXT_CHANGED(ii) = 1;
342         }
343
344         /* Retrieve the gui item information. This is easy if we have been
345            provided with a vector, more difficult if we have just been given
346            keywords.
347
348            #### This is inconsistent with instantiation in that you have to
349            have the :descriptor keyword for updates in order to recognise
350            changes. */
351         if (VECTORP(desc)) {
352                 descriptor_item = gui_parse_item_keywords_no_errors(desc);
353         } else {
354                 /* Since we are updating the instantiator could be incomplete
355                    and hence the gui item descriptor not well formed. We
356                    therefore try updating and discard the results if nothing
357                    changed. */
358                 descriptor_item = copy_gui_item(IMAGE_INSTANCE_WIDGET_ITEM(ii));
359                 if (!update_gui_item_keywords(descriptor_item, instantiator))
360                         descriptor_item = Qnil;
361         }
362
363         /* Record new items for update. *_redisplay will do the
364            rest. */
365         if (!EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qlayout)
366             && !EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qnative_layout)) {
367                 if (!NILP(items)) {
368                         if (NILP(descriptor_item))
369                                 descriptor_item =
370                                     IMAGE_INSTANCE_WIDGET_ITEM(ii);
371
372                         check_valid_item_list(items);
373 #ifdef DEBUG_WIDGET_OUTPUT
374                         stderr_out("items for widget %p updated\n",
375                                    IMAGE_INSTANCE_SUBWINDOW_ID(ii));
376 #endif
377                         /* Don't set the actual items since we might decide not to use
378                            the new ones (because nothing has really changed). If we did
379                            set them and didn't use them then we would get into whole
380                            heaps of trouble when the old items get GC'd. */
381                         descriptor_item =
382                             Fcons(descriptor_item,
383                                   parse_gui_item_tree_children(items));
384                 }
385                 /* If the descriptor was updated but not the items we need to fill
386                    in the `new' items. */
387                 else if (!NILP(descriptor_item)
388                          && CONSP(IMAGE_INSTANCE_WIDGET_ITEMS(ii))) {
389                         descriptor_item = Fcons
390                             (descriptor_item,
391                              copy_gui_item_tree(XCDR
392                                                 (IMAGE_INSTANCE_WIDGET_ITEMS
393                                                  (ii))));
394                 }
395         }
396
397         if (!NILP(descriptor_item)) {
398                 IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii) = descriptor_item;
399                 IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii) = 1;
400         }
401
402         UNGCPRO;
403
404         /* Now try device specific methods first ... */
405         meths = decode_device_ii_format(image_instance_device(image_instance),
406                                         IMAGE_INSTANCE_WIDGET_TYPE(ii),
407                                         ERROR_ME_NOT);
408         MAYBE_IIFORMAT_METH(meths, update, (image_instance, instantiator));
409         /* ... then format specific methods ... */
410         meths = decode_device_ii_format(Qnil, IMAGE_INSTANCE_WIDGET_TYPE(ii),
411                                         ERROR_ME_NOT);
412         MAYBE_IIFORMAT_METH(meths, update, (image_instance, instantiator));
413 #if 0                           /* The usefulness of this is dubious. */
414         /* we didn't do any device specific properties, so shove the property in our plist. */
415         IMAGE_INSTANCE_WIDGET_PROPS(ii)
416             = Fplist_put(IMAGE_INSTANCE_WIDGET_PROPS(ii), prop, val);
417 #endif
418 }
419
420 /* Like the rest of redisplay, we want widget updates to occur
421    asynchronously. Thus toolkit specific methods for setting
422    properties must be called by redisplay instead of by *_update. Thus
423    *_update records the change and this function actually implements
424    it. We want to be slightly clever about this however by supplying
425    format specific functions for the updates instead of lumping them
426    all into this function. Note that there is no need for format
427    generic functions. This is not the same as widget_update! */
428 void redisplay_widget(Lisp_Object widget)
429 {
430         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(widget);
431         struct image_instantiator_methods *meths;
432
433         if (!WIDGET_IMAGE_INSTANCEP(widget)
434             || EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qlayout)
435             || EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qnative_layout))
436                 return;
437
438         /* Device-format specific methods - e.g. x_tab_control_redisplay () */
439         meths = decode_device_ii_format(image_instance_device(widget),
440                                         IMAGE_INSTANCE_WIDGET_TYPE(ii),
441                                         ERROR_ME_NOT);
442         MAYBE_IIFORMAT_METH(meths, redisplay, (widget));
443
444         /* Device generic methods - e.g. x_redisplay_widget (). We must
445            update the widget's size as it may have been changed by the the
446            layout routines. We also do this here so that explicit resizing
447            from lisp does not result in synchronous updates. Do this last so
448            that format-specific methods have an opportunity to prevent
449            wholesale changes - e.g. rebuilding tabs. */
450         MAYBE_DEVMETH(DOMAIN_XDEVICE(IMAGE_INSTANCE_DOMAIN(ii)),
451                       redisplay_widget, (ii));
452
453         /* Pick up the items we recorded earlier. */
454         if (IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii)) {
455                 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
456                     IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii);
457                 IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii) = Qnil;
458         }
459 }
460
461 /* Determine the spacing of the widget. */
462 static int widget_spacing(Lisp_Object domain)
463 {
464         if (HAS_DEVMETH_P(DOMAIN_XDEVICE(domain), widget_spacing))
465                 return DEVMETH(DOMAIN_XDEVICE(domain), widget_spacing, (0));
466         else
467                 return DEFAULT_WIDGET_SPACING;
468 }
469
470 /* Query for a widgets desired geometry. If no type specific method is
471    provided then use the widget text to calculate sizes. */
472 static void
473 widget_query_geometry(Lisp_Object image_instance,
474                       int *width, int *height,
475                       enum image_instance_geometry disp, Lisp_Object domain)
476 {
477         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
478         struct image_instantiator_methods *meths;
479         Lisp_Object dynamic_width = Qnil;
480         Lisp_Object dynamic_height = Qnil;
481
482         /* First just set up what we already have. */
483         if (width)
484                 *width = IMAGE_INSTANCE_WIDTH(ii);
485         if (height)
486                 *height = IMAGE_INSTANCE_HEIGHT(ii);
487
488         if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii)
489             || IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii)) {
490                 /* .. then try device specific methods ... */
491                 meths =
492                     decode_device_ii_format(image_instance_device
493                                             (image_instance),
494                                             IMAGE_INSTANCE_WIDGET_TYPE(ii),
495                                             ERROR_ME_NOT);
496                 if (meths && HAS_IIFORMAT_METH_P(meths, query_geometry))
497                         IIFORMAT_METH(meths, query_geometry, (image_instance,
498                                                               width, height,
499                                                               disp, domain));
500                 else {
501                         /* ... then format specific methods ... */
502                         meths =
503                             decode_device_ii_format(Qnil,
504                                                     IMAGE_INSTANCE_WIDGET_TYPE
505                                                     (ii), ERROR_ME_NOT);
506                         if (meths && HAS_IIFORMAT_METH_P(meths, query_geometry))
507                                 IIFORMAT_METH(meths, query_geometry,
508                                               (image_instance, width, height,
509                                                disp, domain));
510                         else {
511                                 int w, h;
512
513                                 /* Then if we are allowed to resize the widget, make the
514                                    size the same as the text dimensions. */
515                                 query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT
516                                                       (ii),
517                                                       IMAGE_INSTANCE_WIDGET_FACE
518                                                       (ii), &w, &h, 0, domain);
519                                 /* Adjust the size for borders. */
520                                 if (width && IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii))
521                                         *width =
522                                             w +
523                                             2 *
524                                             widget_instance_border_width(ii);
525                                 if (height && IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii))
526                                         *height =
527                                             h +
528                                             2 *
529                                             widget_instance_border_width(ii);
530                         }
531                 }
532                 /* Finish off with dynamic sizing. */
533                 if (!NILP(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii))) {
534                         dynamic_width =
535                             Feval(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii));
536                         if (INTP(dynamic_width) && width )
537                                 *width = XINT(dynamic_width);
538                 }
539                 if (!NILP(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii))) {
540                         dynamic_height =
541                             Feval(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii));
542                         if (INTP(dynamic_height) && height)
543                                 *height = XINT(dynamic_height);
544                 }
545         }
546 }
547
548 static int
549 widget_layout(Lisp_Object image_instance,
550               int width, int height, int xoffset, int yoffset,
551               Lisp_Object domain)
552 {
553         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
554         struct image_instantiator_methods *meths;
555
556         /* .. then try device specific methods ... */
557         meths = decode_device_ii_format(image_instance_device(image_instance),
558                                         IMAGE_INSTANCE_WIDGET_TYPE(ii),
559                                         ERROR_ME_NOT);
560         if (meths && HAS_IIFORMAT_METH_P(meths, layout))
561                 return IIFORMAT_METH(meths, layout, (image_instance,
562                                                      width, height, xoffset,
563                                                      yoffset, domain));
564         else {
565                 /* ... then format specific methods ... */
566                 meths =
567                     decode_device_ii_format(Qnil,
568                                             IMAGE_INSTANCE_WIDGET_TYPE(ii),
569                                             ERROR_ME_NOT);
570                 if (meths && HAS_IIFORMAT_METH_P(meths, layout))
571                         return IIFORMAT_METH(meths, layout, (image_instance,
572                                                              width, height,
573                                                              xoffset, yoffset,
574                                                              domain));
575         }
576         return 1;
577 }
578
579 static void widget_validate(Lisp_Object instantiator)
580 {
581         Lisp_Object desc = find_keyword_in_vector(instantiator, Q_descriptor);
582
583         if (NILP(desc))
584                 syntax_error("Must supply :descriptor", instantiator);
585
586         if (VECTORP(desc))
587                 gui_parse_item_keywords(desc);
588
589         if (!NILP(find_keyword_in_vector(instantiator, Q_width))
590             && !NILP(find_keyword_in_vector(instantiator, Q_pixel_width)))
591                 syntax_error("Must supply only one of :width and :pixel-width",
592                              instantiator);
593
594         if (!NILP(find_keyword_in_vector(instantiator, Q_height))
595             && !NILP(find_keyword_in_vector(instantiator, Q_pixel_height)))
596                 syntax_error
597                     ("Must supply only one of :height and :pixel-height",
598                      instantiator);
599 }
600
601 static void combo_box_validate(Lisp_Object instantiator)
602 {
603         widget_validate(instantiator);
604         if (NILP(find_keyword_in_vector(instantiator, Q_items)))
605                 syntax_error("Must supply item list", instantiator);
606 }
607
608 /* we need to convert things like glyphs to images, eval expressions
609    etc.*/
610 static Lisp_Object
611 widget_normalize(Lisp_Object inst, Lisp_Object console_type,
612                  Lisp_Object dest_mask)
613 {
614         /* This function can call lisp */
615         Lisp_Object glyph = find_keyword_in_vector(inst, Q_image);
616
617         /* we need to eval glyph if its an expression, we do this for the
618            same reasons we normalize file to data.
619
620            #### should just normalize the data. */
621         if (!NILP(glyph)) {
622                 substitute_keyword_value(inst, Q_image,
623                                          glyph_instantiator_to_glyph(glyph));
624         }
625
626         return inst;
627 }
628
629 static void
630 initialize_widget_image_instance(Lisp_Image_Instance * ii, Lisp_Object type)
631 {
632         /*  initialize_subwindow_image_instance (ii); */
633         IMAGE_INSTANCE_WIDGET_TYPE(ii) = type;
634         IMAGE_INSTANCE_WIDGET_PROPS(ii) = Qnil;
635         SET_IMAGE_INSTANCE_WIDGET_FACE(ii, Qnil);
636         IMAGE_INSTANCE_WIDGET_ITEMS(ii) = allocate_gui_item();
637         IMAGE_INSTANCE_LAYOUT_CHILDREN(ii) = Qnil;
638         IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii) = Qnil;
639         IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii) = Qnil;
640         IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii) = Qnil;
641         IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 1;
642         IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 1;
643         IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) = LAYOUT_HORIZONTAL;
644         IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) = 0;
645         IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) = 0;
646 }
647
648 /* Instantiate a button widget. Unfortunately instantiated widgets are
649    particular to a frame since they need to have a parent. It's not
650    like images where you just select the image into the context you
651    want to display it in and BitBlt it. So image instances can have a
652    many-to-one relationship with things you see, whereas widgets can
653    only be one-to-one (i.e. per frame) */
654 void
655 widget_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
656                    Lisp_Object pointer_fg, Lisp_Object pointer_bg,
657                    int dest_mask, Lisp_Object domain)
658 {
659         /* #### practically all of this should be moved to widget_update()
660            so that users can dynamically change all possible widget
661            properties. */
662         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
663         Lisp_Object face = find_keyword_in_vector(instantiator, Q_face);
664         Lisp_Object height = find_keyword_in_vector(instantiator, Q_height);
665         Lisp_Object width = find_keyword_in_vector(instantiator, Q_width);
666         Lisp_Object pixwidth =
667             find_keyword_in_vector(instantiator, Q_pixel_width);
668         Lisp_Object pixheight =
669             find_keyword_in_vector(instantiator, Q_pixel_height);
670         Lisp_Object desc = find_keyword_in_vector(instantiator, Q_descriptor);
671         Lisp_Object glyph = find_keyword_in_vector(instantiator, Q_image);
672         Lisp_Object items = find_keyword_in_vector(instantiator, Q_items);
673         Lisp_Object orient =
674             find_keyword_in_vector(instantiator, Q_orientation);
675         Lisp_Object mwidth =
676             find_keyword_in_vector(instantiator, Q_margin_width);
677         Lisp_Object ifocus =
678             find_keyword_in_vector(instantiator, Q_initial_focus);
679         int pw = 0, ph = 0, tw = 0, th = 0;
680
681         /* this just does pixel type sizing */
682         subwindow_instantiate(image_instance, instantiator, pointer_fg,
683                               pointer_bg, dest_mask, domain);
684
685         if (!(dest_mask & IMAGE_WIDGET_MASK))
686                 incompatible_image_types(instantiator, dest_mask,
687                                          IMAGE_WIDGET_MASK);
688
689         initialize_widget_image_instance(ii, XVECTOR_DATA(instantiator)[0]);
690
691         IMAGE_INSTANCE_TYPE(ii) = IMAGE_WIDGET;
692
693         /* retrieve the fg and bg colors */
694         if (!NILP(face))
695                 SET_IMAGE_INSTANCE_WIDGET_FACE(ii, Fget_face(face));
696
697         /* Retrieve the gui item information. This is easy if we have been
698            provided with a vector, more difficult if we have just been given
699            keywords. Note that standard gui descriptor shortcuts will not work
700            because of keyword parsing.
701
702            #### This is bogus in that descriptor and items share the same slot,
703            we should rationalize. */
704         if (VECTORP(desc)) {
705                 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
706                     gui_parse_item_keywords_no_errors(desc);
707         } else {
708                 /* big cheat - we rely on the fact that a gui item looks like an instantiator */
709                 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
710                     widget_gui_parse_item_keywords(instantiator);
711         }
712
713         /* Pick up the orientation before we do our first layout. */
714         if (EQ(orient, Qleft) || EQ(orient, Qright) || EQ(orient, Qvertical))
715                 IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) = LAYOUT_VERTICAL;
716
717         /* parse more gui items out of the properties */
718         if (!NILP(items) && !EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qlayout)
719             && !EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qnative_layout)) {
720                 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
721                     Fcons(IMAGE_INSTANCE_WIDGET_ITEMS(ii),
722                           parse_gui_item_tree_children(items));
723         }
724
725         /* Normalize size information. We now only assign sizes if the user
726            gives us some explicitly, or there are some constraints that we
727            can't change later on. Otherwise we postpone sizing until query
728            geometry gets called. */
729         if (!NILP(pixwidth)) {  /* pixwidth takes precendent */
730                 if (!INTP(pixwidth))
731                         IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii) = pixwidth;
732                 else {
733                         pw = XINT(pixwidth);
734                         IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 0;
735                 }
736         } else if (!NILP(width)) {
737                 tw = XINT(width);
738                 IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 0;
739         }
740
741         if (!NILP(pixheight)) {
742                 if (!INTP(pixheight))
743                         IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii) = pixheight;
744                 else {
745                         ph = XINT(pixheight);
746                         IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 0;
747                 }
748         } else if (!NILP(height) && XINT(height) > 1) {
749                 th = XINT(height);
750                 IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 0;
751         }
752
753         /* Taking the default face information when the user has specified
754            size in characters is probably as good as any since the widget
755            face is more likely to be proportional and thus give inadequate
756            results. Using character sizes can only ever be approximate
757            anyway. :height is measured in logical characters which take into
758            account the borders and spacing on widgets. */
759         if (tw) {
760                 int charwidth;
761                 default_face_font_info(domain, 0, 0, 0, &charwidth, 0);
762                 pw = ROUND_UP(charwidth * tw +
763                               4 * widget_instance_border_width(ii), charwidth);
764         }
765
766         /* For heights the widget face is more appropriate. */
767         if (th == 1) {
768                 int charheight;
769                 if (!NILP(IMAGE_INSTANCE_WIDGET_TEXT(ii))) {
770                         query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
771                                               IMAGE_INSTANCE_WIDGET_FACE(ii),
772                                               0, &charheight, 0, domain);
773                 } else {
774                         default_face_font_info(domain, 0, 0, &charheight, 0, 0);
775                 }
776                 ph = (charheight + 2 * widget_instance_border_width(ii)) * th;
777         }
778         /* For heights > 1 use logical units. */
779         else if (th > 1) {
780                 ph = widget_logical_unit_height(ii) * th;
781         }
782
783         /* for a widget with an image pick up the dimensions from that */
784         if (!NILP(glyph)) {
785                 if (!pw)
786                         pw = glyph_width(glyph,
787                                          image_instance) +
788                             2 * widget_instance_border_width(ii);
789                 if (!ph)
790                         ph = glyph_height(glyph,
791                                           image_instance) +
792                             2 * widget_instance_border_width(ii);
793                 IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 0;
794                 IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 0;
795         }
796
797         /* Pick up the margin width. */
798         if (!NILP(mwidth))
799                 IMAGE_INSTANCE_MARGIN_WIDTH(ii) = XINT(mwidth);
800
801         IMAGE_INSTANCE_WANTS_INITIAL_FOCUS(ii) = !NILP(ifocus);
802
803         /* Layout for the layout widget is premature at this point since the
804            children will not have been instantiated. We can't instantiate
805            them until the device instantiation method for the layout has
806            been executed. We do however want to record any specified
807            dimensions. */
808         if (pw)
809                 IMAGE_INSTANCE_WIDTH(ii) = pw;
810         if (ph)
811                 IMAGE_INSTANCE_HEIGHT(ii) = ph;
812 }
813
814 static void
815 widget_post_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
816                         Lisp_Object domain)
817 {
818 #ifdef DEBUG_WIDGETS
819         debug_widget_instances++;
820         stderr_out("instantiated ");
821         debug_print(instantiator);
822         stderr_out("%d widgets instantiated\n", debug_widget_instances);
823 #endif
824 }
825
826 /* Get the geometry of a button control. We need to adjust the size
827    depending on the type of button. */
828 static void
829 button_query_geometry(Lisp_Object image_instance,
830                       int *width, int *height,
831                       enum image_instance_geometry disp, Lisp_Object domain)
832 {
833         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
834         int w, h;
835         query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
836                               IMAGE_INSTANCE_WIDGET_FACE(ii),
837                               &w, &h, 0, domain);
838         /* Adjust the size for borders. */
839         if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii)) {
840                 *width = w + 3 * widget_instance_border_width(ii);
841
842                 if (EQ(XGUI_ITEM(IMAGE_INSTANCE_WIDGET_ITEM(ii))->style, Qradio)
843                     ||
844                     EQ(XGUI_ITEM(IMAGE_INSTANCE_WIDGET_ITEM(ii))->style,
845                        Qtoggle))
846                         /* This is an approximation to the size of the actual button bit. */
847                         *width += 12;
848         }
849         if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii))
850                 *height = h + 3 * widget_instance_border_width(ii);
851 }
852
853 /* Get the geometry of an edit field. */
854 static void
855 edit_field_query_geometry(Lisp_Object image_instance,
856                           int *width, int *height,
857                           enum image_instance_geometry disp, Lisp_Object domain)
858 {
859         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
860         int w, h;
861         query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
862                               IMAGE_INSTANCE_WIDGET_FACE(ii),
863                               &w, &h, 0, domain);
864         /* Adjust the size for borders. */
865         if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii))
866                 *width = w + 4 * widget_instance_border_width(ii);
867         if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii))
868                 *height = h + 4 * widget_instance_border_width(ii);
869 }
870
871 /* tree-view geometry - get the height right */
872 static void
873 tree_view_query_geometry(Lisp_Object image_instance,
874                          int *width, int *height,
875                          enum image_instance_geometry disp, Lisp_Object domain)
876 {
877         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
878         Lisp_Object items = IMAGE_INSTANCE_WIDGET_ITEMS(ii);
879
880         if (*width) {
881                 /* #### what should this be. reconsider when X has tree views. */
882                 query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
883                                       IMAGE_INSTANCE_WIDGET_FACE(ii),
884                                       width, 0, 0, domain);
885         }
886         if (*height) {
887                 int len, h;
888                 /* #### widget face would be better here. */
889                 default_face_font_info(domain, 0, 0, &h, 0, 0);
890                 GET_LIST_LENGTH(items, len);
891                 *height = len * h;
892         }
893 }
894
895 /* Get the geometry of a tab control. This is based on the number of
896    items and text therin in the tab control. */
897 static void
898 tab_control_query_geometry(Lisp_Object image_instance,
899                            int *width, int *height,
900                            enum image_instance_geometry disp,
901                            Lisp_Object domain)
902 {
903         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
904         Lisp_Object items = XCDR(IMAGE_INSTANCE_WIDGET_ITEMS(ii));
905         Lisp_Object rest;
906         int tw = 0, th = 0;
907
908         LIST_LOOP(rest, items) {
909                 int h, w;
910
911                 query_string_geometry(XGUI_ITEM(XCAR(rest))->name,
912                                       IMAGE_INSTANCE_WIDGET_FACE(ii),
913                                       &w, &h, 0, domain);
914                 tw += 5 * widget_instance_border_width(ii);     /* some bias */
915                 tw += w;
916                 th = max(th, h + 2 * widget_instance_border_width(ii));
917         }
918
919         /* Fixup returned values depending on orientation. */
920         if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)) {
921                 if (height)
922                         *height = tw;
923                 if (width)
924                         *width = th;
925         } else {
926                 if (height)
927                         *height = th;
928                 if (width)
929                         *width = tw;
930         }
931 }
932
933 /* Determine whether only the order has changed for a tab. */
934 int tab_control_order_only_changed(Lisp_Object image_instance)
935 {
936         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
937         int found = 0, len, pending_len;
938         Lisp_Object rest;
939
940         /* Degenerate case. */
941         if (NILP(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)))
942                 return 1;
943
944         /* See whether we just need a change in order. */
945         GET_LIST_LENGTH(IMAGE_INSTANCE_WIDGET_ITEMS(ii), len);
946         GET_LIST_LENGTH(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii), pending_len);
947         if (len == pending_len) {
948                 LIST_LOOP(rest, XCDR(IMAGE_INSTANCE_WIDGET_ITEMS(ii))) {
949                         Lisp_Object pending_rest;
950                         found = 0;
951                         LIST_LOOP(pending_rest,
952                                   XCDR(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)))
953                         {
954                                 if (gui_item_equal_sans_selected(XCAR(rest),
955                                                                  XCAR
956                                                                  (pending_rest),
957                                                                  0)) {
958                                         found = 1;
959                                         break;
960                                 }
961                         }
962                         if (!found)
963                                 break;
964                 }
965         }
966         return found;
967 }
968 \f
969 /*****************************************************************************
970  *                              widget layout                               *
971  *****************************************************************************/
972 /* We need to cascade normalization.*/
973 static Lisp_Object
974 layout_normalize(Lisp_Object inst, Lisp_Object console_type,
975                  Lisp_Object dest_mask)
976 {
977         /* This function can call lisp */
978         struct gcpro gcpro1, gcpro2;
979         Lisp_Object alist = Qnil, new_items = Qnil, border;
980         /* This function can call lisp */
981         Lisp_Object items;
982
983         GCPRO2(alist, new_items);
984         alist = tagged_vector_to_alist(inst);
985         items = assq_no_quit(Q_items, alist);
986
987         /* We need to normalize sub-objects. */
988         if (!NILP(items)) {
989                 Lisp_Object rest;
990                 LIST_LOOP(rest, XCDR(items)) {
991                         /* Substitute the new instantiator */
992                         new_items =
993                             Fcons(normalize_image_instantiator
994                                   (XCAR(rest), console_type, dest_mask),
995                                   new_items);
996                 }
997                 new_items = Fnreverse(new_items);
998                 Fsetcdr(items, new_items);
999         }
1000         /* Normalize the border spec. */
1001         border = assq_no_quit(Q_border, alist);
1002         if (!NILP(border) && VECTORP(XCDR(border))) {
1003                 Fsetcdr(border, normalize_image_instantiator(XCDR(border),
1004                                                              console_type,
1005                                                              dest_mask));
1006         }
1007
1008         {
1009                 Lisp_Object result =
1010                     alist_to_tagged_vector(XVECTOR_DATA(inst)[0],
1011                                            alist);
1012                 free_alist(alist);
1013                 RETURN_UNGCPRO(result);
1014         }
1015 }
1016
1017 /* Update the instances in the layout. */
1018 static void layout_update(Lisp_Object image_instance, Lisp_Object instantiator)
1019 {
1020         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1021         Lisp_Object items = find_keyword_in_vector(instantiator, Q_items);
1022         Lisp_Object border_inst =
1023             find_keyword_in_vector(instantiator, Q_border);
1024         Lisp_Object justify = find_keyword_in_vector(instantiator, Q_justify);
1025         Lisp_Object hjustify =
1026             find_keyword_in_vector(instantiator, Q_horizontally_justify);
1027         Lisp_Object vjustify =
1028             find_keyword_in_vector(instantiator, Q_vertically_justify);
1029         Lisp_Object border = Qnil;
1030         Lisp_Object children = IMAGE_INSTANCE_LAYOUT_CHILDREN(ii);
1031         int structure_changed = 0;
1032         struct gcpro gcpro1;
1033
1034         /* Pick up horizontal justification, left is the default. */
1035         if (!NILP(hjustify)) {
1036                 if (EQ(hjustify, Qright) || EQ(hjustify, Qbottom))
1037                         IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1038                             LAYOUT_JUSTIFY_RIGHT;
1039                 else if (EQ(hjustify, Qcenter))
1040                         IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1041                             LAYOUT_JUSTIFY_CENTER;
1042         }
1043         /* If not set use general justification. */
1044         else if (!NILP(justify)) {
1045                 if (EQ(justify, Qright) || EQ(justify, Qbottom))
1046                         IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1047                             LAYOUT_JUSTIFY_RIGHT;
1048                 else if (EQ(justify, Qcenter))
1049                         IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1050                             LAYOUT_JUSTIFY_CENTER;
1051         }
1052
1053         /* Pick up vertical justification, top is the default. */
1054         if (!NILP(vjustify)) {
1055                 if (EQ(vjustify, Qright) || EQ(vjustify, Qbottom))
1056                         IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1057                             LAYOUT_JUSTIFY_BOTTOM;
1058                 else if (EQ(vjustify, Qcenter))
1059                         IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1060                             LAYOUT_JUSTIFY_CENTER;
1061         }
1062         /* If not set use general justification. */
1063         else if (!NILP(justify)) {
1064                 if (EQ(justify, Qright) || EQ(justify, Qbottom))
1065                         IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1066                             LAYOUT_JUSTIFY_BOTTOM;
1067                 else if (EQ(justify, Qcenter))
1068                         IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1069                             LAYOUT_JUSTIFY_CENTER;
1070         }
1071
1072         /* We want to avoid consing if we can. This is quite awkward because
1073            we have to deal with the border as well as the items. */
1074         GCPRO1(border);
1075
1076         if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii))) {
1077                 border = XCAR(children);
1078                 children = XCDR(children);
1079         }
1080 #ifdef DEBUG_WIDGET_OUTPUT
1081         stderr_out("layout updated\n");
1082 #endif
1083         /* Update the border. */
1084         if (!NILP(border_inst)) {
1085                 if (VECTORP(border_inst)) {
1086                         /* We are going to be sneaky here and add the border text as
1087                            just another child, the layout and output routines don't know
1088                            this and will just display at the offsets we prescribe. */
1089                         if (!NILP(border))
1090                                 call3(Qset_glyph_image, border, border_inst,
1091                                       IMAGE_INSTANCE_DOMAIN(ii));
1092                         else {
1093                                 border =
1094                                     Fcons(call1(Qmake_glyph, border_inst),
1095                                           Qnil);
1096                                 structure_changed = 1;
1097                         }
1098                         IMAGE_INSTANCE_LAYOUT_BORDER(ii) = make_int(0);
1099                 } else {
1100                         if (!NILP(border)) {
1101                                 border = Qnil;
1102                                 structure_changed = 1;
1103                         }
1104                         if (EQ(border_inst, Qt))
1105                                 IMAGE_INSTANCE_LAYOUT_BORDER(ii) = Qetched_in;
1106                         else
1107                                 IMAGE_INSTANCE_LAYOUT_BORDER(ii) = border_inst;
1108                 }
1109         }
1110
1111         /* Pick up the sub-widgets. */
1112         if (!NILP(items)) {
1113                 int len1, len2;
1114                 GET_LIST_LENGTH(items, len1);
1115                 GET_LIST_LENGTH(children, len2);
1116                 /* The structure hasn't changed so just update the images. */
1117                 if (!structure_changed && len1 == len2) {
1118                         /* Pick up the sub-widgets. */
1119                         for (; !NILP(children);
1120                              children = XCDR(children), items = XCDR(items)) {
1121                                 call3(Qset_glyph_image, XCAR(children),
1122                                       XCAR(items), IMAGE_INSTANCE_DOMAIN(ii));
1123                         }
1124                 }
1125                 /* The structure has changed so start over. */
1126                 else {
1127                         /* Instantiate any new glyphs. */
1128                         for (; !NILP(items); items = XCDR(items)) {
1129                                 /* #### We really want to use call_with_suspended_errors
1130                                    here, but it won't allow us to call lisp. */
1131                                 border =
1132                                     Fcons(call1(Qmake_glyph, XCAR(items)),
1133                                           border);
1134                         }
1135                         IMAGE_INSTANCE_LAYOUT_CHILDREN(ii) = Fnreverse(border);
1136                 }
1137         }
1138         UNGCPRO;
1139 }
1140
1141 static void
1142 layout_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1143                    Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1144                    int dest_mask, Lisp_Object domain)
1145 {
1146         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1147         Lisp_Object orient =
1148             find_keyword_in_vector(instantiator, Q_orientation);
1149
1150 #ifdef DEBUG_WIDGET_OUTPUT
1151         stderr_out("layout instantiated\n");
1152 #endif
1153         /* Do widget type instantiation first. */
1154         widget_instantiate(image_instance, instantiator, pointer_fg, pointer_bg,
1155                            dest_mask, domain);
1156
1157         if (NILP(orient)) {
1158                 IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) = LAYOUT_VERTICAL;
1159         }
1160
1161         /* Get child glyphs and finish instantiation. We can't do image
1162            instance children yet as we might not have a containing
1163            window. */
1164         layout_update(image_instance, instantiator);
1165 }
1166
1167 static void
1168 layout_post_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1169                         Lisp_Object domain)
1170 {
1171 }
1172
1173 /* Layout widget. Sizing commentary: we have a number of problems that
1174    we would like to address. Some consider some of these more
1175    important than others. It used to be that size information was
1176    determined at instantiation time and was then fixed forever
1177    after. Generally this is not what we want. Users want size to be
1178    "big enough" to accommodate whatever they are trying to show and
1179    this is dependent on text length, lines, font metrics etc. Of
1180    course these attributes can change dynamically and so the size
1181    should changed dynamically also. Only in a few limited cases should
1182    the size be fixed and remain fixed. Of course this actually means
1183    that we don't really want to specify the size *at all* for most
1184    widgets - we want it to be discovered dynamically. Thus we can
1185    envisage the following scenarios:
1186
1187    1. A button is sized to accommodate its text, the text changes and the
1188    button should change size also.
1189
1190    2. A button is given an explicit size. Its size should never change.
1191
1192    3. Layout is put inside an area. The size of the area changes, the
1193    layout should change with it.
1194
1195    4. A button grows to accommodate additional text. The whitespace
1196    around it should be modified to cope with the new layout
1197    requirements.
1198
1199    5. A button grows. The area surrounding it should grow also if
1200    possible.
1201
1202    What metrics are important?
1203    1. Actual width and height.
1204
1205    2. Whether the width and height are what the widget actually wants, or
1206    whether it can grow or shrink.
1207
1208    Text glyphs are particularly troublesome since their metrics depend
1209    on the context in which they are being viewed. For instance they
1210    can appear differently depending on the window face, frame face or
1211    glyph face. In order to simplify this text glyphs can now only have
1212    a glyph-face or image-instance face. All other glyphs are
1213    essentially fixed in appearance. Perhaps the problem is that text
1214    glyphs are cached on a device basis like most other glyphs. Instead
1215    they should be cached per-window and then the instance would be
1216    fixed and we wouldn't have to mess around with font metrics and the
1217    rest.
1218
1219    Another sizing problem is alignment. We provide layout widgets that
1220    allow users to stack widgets vertically or horizontally. These
1221    layouts also allow the widgets to be centered (space evenly
1222    distributed), left or right justified (fixed spacing widgets
1223    stacked against the left, righ, top or bottom edge). Unfortunately
1224    this doesn't allow widgets in different layouts to be aligned. For
1225    instance how should the search dialog be organized for alignment?
1226    The obvious choice of two vertical columns does not work since the
1227    size of individual widgets will affect where they get placed. The
1228    same is true for several rows of widgets. To solve this problem we
1229    introduce the notion of `logical_unit_height'. This is a size
1230    quantity that is designed to be big enough to accomodate the
1231    largest `single height unit'. The function
1232    widget_logical_unit_height() determines the value of this in
1233    pixels. It is dependent on the widget face and some combination of
1234    spacing and border-width. Thus if users specify left or right
1235    justification in a vertical layout they get something in logical
1236    units. To simplify this the functions
1237    `widget-logical-to-character-height' and
1238    `widget-logical-to-character-width' allow conversion between
1239    characters and logical units so that frames can be sized
1240    appropriately. */
1241
1242 /* Query the geometry of a layout widget. */
1243 static void
1244 layout_query_geometry(Lisp_Object image_instance, int *width,
1245                       int *height, enum image_instance_geometry disp,
1246                       Lisp_Object domain)
1247 {
1248         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1249         Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN(ii), rest;
1250         int maxph = 0, maxpw = 0, nitems = 0, ph_adjust = 0;
1251         int gheight, gwidth, luh;
1252
1253         /* If we are not initialized then we won't have any children. */
1254         if (!IMAGE_INSTANCE_INITIALIZED(ii))
1255                 return;
1256
1257         /* First just set up what we already have. */
1258         if (width)
1259                 *width = IMAGE_INSTANCE_WIDTH(ii);
1260         if (height)
1261                 *height = IMAGE_INSTANCE_HEIGHT(ii);
1262
1263         /* If we are not allowed to dynamically size then return. */
1264         if (!IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii)
1265             && !IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii))
1266                 return;
1267
1268         luh = widget_logical_unit_height(ii);
1269
1270         /* Pick up the border text if we have one. */
1271         if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii))) {
1272                 glyph_query_geometry(XCAR(items), &gwidth, &gheight, disp,
1273                                      image_instance);
1274                 ph_adjust = gheight;
1275                 /* Include text width in vertical layouts. */
1276                 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_VERTICAL)
1277                         maxpw = gwidth + BORDER_FIDDLE_FACTOR;
1278                 items = XCDR(items);
1279         }
1280
1281         /* Flip through the items to work out how much stuff we have to display */
1282         LIST_LOOP(rest, items) {
1283                 Lisp_Object glyph = XCAR(rest);
1284                 glyph_query_geometry(glyph, &gwidth, &gheight, disp,
1285                                      image_instance);
1286
1287                 nitems++;
1288                 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_HORIZONTAL) {
1289                         maxph = max(maxph, gheight);
1290                         maxpw += gwidth;
1291                 } else {
1292                         maxpw = max(maxpw, gwidth);
1293                         maxph += gheight;
1294                 }
1295         }
1296
1297         /* Work out minimum space we need to fit all the items. This could
1298            have been fixed by the user. */
1299         if (width && IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii)) {
1300                 if (!NILP(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii))) {
1301                         Lisp_Object dynamic_width =
1302                             Feval(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii));
1303                         if (INTP(dynamic_width))
1304                                 *width = XINT(dynamic_width);
1305                 } else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) ==
1306                            LAYOUT_HORIZONTAL) {
1307                         *width =
1308                             maxpw +
1309                             ((nitems + 1) * widget_instance_border_width(ii) +
1310                              IMAGE_INSTANCE_MARGIN_WIDTH(ii)) * 2;
1311                 } else {
1312                         *width =
1313                             maxpw + 2 * (widget_instance_border_width(ii) * 2 +
1314                                          IMAGE_INSTANCE_MARGIN_WIDTH(ii));
1315                 }
1316         }
1317
1318         /* Work out vertical spacings. */
1319         if (height && IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii)) {
1320                 if (!NILP(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii))) {
1321                         Lisp_Object dynamic_height =
1322                             Feval(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii));
1323                         if (INTP(dynamic_height))
1324                                 *height = XINT(dynamic_height);
1325                 } else if (IMAGE_INSTANCE_SUBWINDOW_LOGICAL_LAYOUT(ii)) {
1326                         *height = nitems * luh + ph_adjust;
1327                 } else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) ==
1328                            LAYOUT_VERTICAL) {
1329                         *height =
1330                             maxph +
1331                             ((nitems + 1) * widget_instance_border_width(ii) +
1332                              IMAGE_INSTANCE_MARGIN_WIDTH(ii)) * 2 + ph_adjust;
1333                 } else {
1334                         *height =
1335                             maxph + (2 * widget_instance_border_width(ii) +
1336                                      IMAGE_INSTANCE_MARGIN_WIDTH(ii)) * 2 +
1337                             ph_adjust;
1338                 }
1339         }
1340 #ifdef DEBUG_WIDGET_OUTPUT
1341         stderr_out("layout wants %dx%d\n", *width, *height);
1342 #endif
1343 }
1344
1345 int
1346 layout_layout(Lisp_Object image_instance,
1347               int width, int height, int xoffset, int yoffset,
1348               Lisp_Object domain)
1349 {
1350         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1351         Lisp_Object rest;
1352         Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN(ii);
1353         int x, y, maxph = 0, maxpw = 0, nitems = 0,
1354             horiz_spacing, vert_spacing, ph_adjust = 0;
1355         int gheight, gwidth;
1356         /* See comments in widget_logical_unit_height(). */
1357         int luh = widget_logical_unit_height(ii);
1358
1359         /* If we are not initialized then we won't have any children. */
1360         if (!IMAGE_INSTANCE_INITIALIZED(ii))
1361                 return 0;
1362
1363 #ifdef DEBUG_WIDGET_OUTPUT
1364         stderr_out("layout output %dx%d\n", width, height);
1365 #endif
1366
1367         /* Pick up the border text if we have one. A border can have the
1368            values Qetched_in, Qetched_out, Qbevel_in, Qbevel_out or an
1369            integer. The first four just affect the display properties of the
1370            border that is drawn. The last is an offset and implies that the
1371            first item in the list of subcontrols is a text control that
1372            should be displayed on the border. */
1373         if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii))) {
1374                 Lisp_Object border = XCAR(items);
1375                 items = XCDR(items);
1376                 glyph_query_geometry(border, &gwidth, &gheight,
1377                                      IMAGE_DESIRED_GEOMETRY, image_instance);
1378                 /* The vertical offset for subsequent items is the full height
1379                    of the border glyph. */
1380                 ph_adjust = gheight;
1381                 /* The offset for the border is half the glyph height. */
1382                 IMAGE_INSTANCE_LAYOUT_BORDER(ii) = make_int(gheight / 2);
1383
1384                 /* #### Really, what should this be? */
1385                 glyph_do_layout(border, gwidth, gheight, BORDER_FIDDLE_FACTOR,
1386                                 0, image_instance);
1387         }
1388
1389         /* Flip through the items to work out how much stuff we have to display. */
1390         LIST_LOOP(rest, items) {
1391                 Lisp_Object glyph = XCAR(rest);
1392
1393                 glyph_query_geometry(glyph, &gwidth, &gheight,
1394                                      IMAGE_DESIRED_GEOMETRY, image_instance);
1395                 nitems++;
1396                 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)
1397                     == LAYOUT_HORIZONTAL) {
1398                         maxph = max(maxph, gheight);
1399                         maxpw += gwidth;
1400                 } else {
1401                         maxpw = max(maxpw, gwidth);
1402                         maxph += gheight;
1403                 }
1404         }
1405
1406         /* work out spacing between items and bounds of the layout */
1407         if (width < maxpw)
1408                 /* The user wants a smaller space than the largest item, so we
1409                    just provide default spacing and will let the output routines
1410                    clip. */
1411                 horiz_spacing = widget_spacing(IMAGE_INSTANCE_DOMAIN(ii));
1412         else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)
1413                  == LAYOUT_HORIZONTAL)
1414                 /* We have a larger area to display in so distribute the space
1415                    evenly. */
1416                 horiz_spacing = (width - (maxpw +
1417                                           IMAGE_INSTANCE_MARGIN_WIDTH(ii) * 2))
1418                     / (nitems + 1);
1419         else
1420                 horiz_spacing = (width - maxpw) / 2
1421                     - IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1422
1423         /* We are trying here to get widgets to line up when they are left
1424            or right justified vertically. This means that we must position
1425            widgets on logical unit boundaries, even though their height may
1426            be greater or less than a logical unit. In order to avoid
1427            clipping we need to determine how big the widget wants to be and
1428            then allocate as many logical units as necessary in order to
1429            accommodate it. */
1430         if (height < maxph)
1431                 vert_spacing = widget_spacing(IMAGE_INSTANCE_DOMAIN(ii)) * 2;
1432         else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)
1433                  == LAYOUT_VERTICAL) {
1434                 if (!IMAGE_INSTANCE_SUBWINDOW_V_CENTERED(ii))
1435                         vert_spacing =
1436                             widget_spacing(IMAGE_INSTANCE_DOMAIN(ii)) * 2;
1437                 else
1438                         vert_spacing = (height - (maxph + ph_adjust +
1439                                                   IMAGE_INSTANCE_MARGIN_WIDTH
1440                                                   (ii) * 2))
1441                             / (nitems + 1);
1442         } else
1443                 vert_spacing = (height - (maxph + ph_adjust)) / 2
1444                     - IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1445
1446         y = yoffset =
1447             vert_spacing + ph_adjust + IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1448         x = horiz_spacing + IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1449
1450         /* Now flip through putting items where we want them, paying
1451            attention to justification. Make sure we don't mess with the
1452            border glyph. */
1453         LIST_LOOP(rest, items) {
1454                 Lisp_Object glyph = XCAR(rest);
1455
1456                 glyph_query_geometry(glyph, &gwidth, &gheight,
1457                                      IMAGE_DESIRED_GEOMETRY, image_instance);
1458
1459                 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_HORIZONTAL) {
1460                         if (IMAGE_INSTANCE_SUBWINDOW_BOTTOM_JUSTIFIED(ii))
1461                                 y = height - (gheight + vert_spacing);
1462                         else if (IMAGE_INSTANCE_SUBWINDOW_V_CENTERED(ii))
1463                                 y = (height - gheight) / 2;
1464                 } else {
1465                         if (IMAGE_INSTANCE_SUBWINDOW_RIGHT_JUSTIFIED(ii))
1466                                 x = width - (gwidth + horiz_spacing);
1467                         else if (IMAGE_INSTANCE_SUBWINDOW_H_CENTERED(ii))
1468                                 x = (width - gwidth) / 2;
1469                 }
1470
1471                 /* Now layout subwidgets if they require it. */
1472                 glyph_do_layout(glyph, gwidth, gheight, x, y, image_instance);
1473
1474                 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_HORIZONTAL) {
1475                         x += (gwidth + horiz_spacing);
1476                 } else {
1477                         y += (gheight + vert_spacing);
1478                         if (!IMAGE_INSTANCE_SUBWINDOW_V_CENTERED(ii)) {
1479                                 /* justified, vertical layout, try and align on logical unit
1480                                    boundaries. */
1481                                 y = ROUND_UP(y - yoffset, luh) + yoffset;
1482                         }
1483                 }
1484
1485         }
1486         return 1;
1487 }
1488
1489 /* Get the glyphs that comprise a layout. These are created internally
1490    and so are otherwise inaccessible to lisp. We need some way of getting
1491    properties from the widgets that comprise a layout and this is the
1492    simplest way of doing it.
1493
1494    #### Eventually we should allow some more intelligent access to
1495    sub-widgets. */
1496 static Lisp_Object layout_property(Lisp_Object image_instance, Lisp_Object prop)
1497 {
1498         /* This function can GC. */
1499         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1500         if (EQ(prop, Q_items)) {
1501                 if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii)) &&
1502                     CONSP(IMAGE_INSTANCE_LAYOUT_CHILDREN(ii)))
1503                         return Fcopy_sequence(XCDR
1504                                               (IMAGE_INSTANCE_LAYOUT_CHILDREN
1505                                                (ii)));
1506                 else
1507                         return
1508                             Fcopy_sequence(IMAGE_INSTANCE_LAYOUT_CHILDREN(ii));
1509         }
1510         return Qunbound;
1511 }
1512
1513 /* Layout subwindows if they are real subwindows. */
1514 static int
1515 native_layout_layout(Lisp_Object image_instance,
1516                      int width, int height, int xoffset, int yoffset,
1517                      Lisp_Object domain)
1518 {
1519         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1520         Lisp_Object rest;
1521
1522         /* The first time this gets called, the layout will be only
1523            partially instantiated. The children get done in
1524            post_instantiate. */
1525         if (!IMAGE_INSTANCE_INITIALIZED(ii))
1526                 return 0;
1527
1528         /* Defining this overrides the default layout_layout so we first have to call that to get
1529            suitable instances and values set up. */
1530         layout_layout(image_instance, width, height, xoffset, yoffset, domain);
1531
1532         LIST_LOOP(rest, IMAGE_INSTANCE_LAYOUT_CHILDREN(ii)) {
1533                 struct display_glyph_area dga;
1534                 dga.xoffset = 0;
1535                 dga.yoffset = 0;
1536                 dga.width = IMAGE_INSTANCE_WIDTH(ii);
1537                 dga.height = IMAGE_INSTANCE_HEIGHT(ii);
1538
1539                 map_subwindow(XCAR(rest),
1540                               IMAGE_INSTANCE_XOFFSET(ii),
1541                               IMAGE_INSTANCE_YOFFSET(ii), &dga);
1542         }
1543         return 1;
1544 }
1545
1546 DEFUN("widget-logical-to-character-width", Fwidget_logical_to_character_width, 1, 3, 0, /*
1547 Convert the width in logical widget units to characters.
1548 Logical widget units do not take into account adjusments made for
1549 layout borders, so this adjusment is approximated.
1550 */
1551       (width, face, domain))
1552 {
1553         int w, neww, charwidth;
1554         int border_width = DEFAULT_WIDGET_BORDER_WIDTH;
1555
1556         if (NILP(domain))
1557                 domain = Fselected_frame(Qnil);
1558
1559         CHECK_INT(width);
1560         w = XINT(width);
1561
1562         if (HAS_DEVMETH_P(DOMAIN_XDEVICE(domain), widget_border_width))
1563                 border_width =
1564                     DEVMETH(DOMAIN_XDEVICE(domain), widget_border_width, ());
1565
1566         default_face_font_info(domain, 0, 0, 0, &charwidth, 0);
1567         neww =
1568             ROUND_UP(charwidth * w + 4 * border_width +
1569                      2 * widget_spacing(domain), charwidth) / charwidth;
1570
1571         return make_int(neww);
1572 }
1573
1574 DEFUN("widget-logical-to-character-height", Fwidget_logical_to_character_height, 1, 3, 0,       /*
1575 Convert the height in logical widget units to characters.
1576 Logical widget units do not take into account adjusments made for
1577 layout borders, so this adjustment is approximated.
1578
1579 If the components of a widget layout are justified to the top or the
1580 bottom then they are aligned in terms of `logical units'. This is a
1581 size quantity that is designed to be big enough to accomodate the
1582 largest `single height' widget. It is dependent on the widget face and
1583 some combination of spacing and border-width. Thus if you specify top
1584 or bottom justification in a vertical layout the subcontrols are laid
1585 out one per logical unit. This allows adjoining layouts to have
1586 identical alignment for their subcontrols.
1587
1588 Since frame sizes are measured in characters, this function allows you
1589 to do appropriate conversion between logical units and characters.
1590 */
1591       (height, face, domain))
1592 {
1593         int h, newh, charheight;
1594
1595         CHECK_INT(height);
1596         if (NILP(domain))
1597                 domain = Fselected_frame(Qnil);
1598
1599         h = XINT(height);
1600
1601         default_face_font_info(domain, 0, 0, &charheight, 0, 0);
1602         newh = ROUND_UP(logical_unit_height(Fsymbol_name(Qwidget),
1603                                             Vwidget_face, domain) * h,
1604                         charheight)
1605             / charheight;
1606
1607         return make_int(newh);
1608 }
1609 \f
1610 /************************************************************************/
1611 /*                            initialization                            */
1612 /************************************************************************/
1613
1614 void syms_of_glyphs_widget(void)
1615 {
1616         DEFSYMBOL(Qetched_in);
1617         DEFSYMBOL(Qetched_out);
1618         DEFSYMBOL(Qbevel_in);
1619         DEFSYMBOL(Qbevel_out);
1620         DEFSYMBOL(Qmake_glyph);
1621
1622         DEFSUBR(Fwidget_logical_to_character_height);
1623         DEFSUBR(Fwidget_logical_to_character_width);
1624 }
1625
1626 #define VALID_GUI_KEYWORDS(type) do {                                         \
1627   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_active, check_valid_anything);      \
1628   IIFORMAT_VALID_KEYWORD (type, Q_suffix, check_valid_anything);              \
1629   IIFORMAT_VALID_KEYWORD (type, Q_keys, check_valid_string);                  \
1630   IIFORMAT_VALID_KEYWORD (type, Q_style, check_valid_symbol);                 \
1631   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_selected, check_valid_anything);    \
1632   IIFORMAT_VALID_KEYWORD (type, Q_filter, check_valid_anything);              \
1633   IIFORMAT_VALID_KEYWORD (type, Q_config, check_valid_symbol);                \
1634   IIFORMAT_VALID_KEYWORD (type, Q_included, check_valid_anything);            \
1635   IIFORMAT_VALID_KEYWORD (type, Q_initial_focus, check_valid_anything);       \
1636   IIFORMAT_VALID_KEYWORD (type, Q_key_sequence, check_valid_string);          \
1637   IIFORMAT_VALID_KEYWORD (type, Q_accelerator, check_valid_string);           \
1638   IIFORMAT_VALID_KEYWORD (type, Q_label, check_valid_anything);               \
1639   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback, check_valid_callback);    \
1640   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback_ex, check_valid_callback); \
1641   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_descriptor,                         \
1642                                   check_valid_string_or_vector);              \
1643 } while (0)
1644
1645 #define VALID_WIDGET_KEYWORDS(type) do {                                      \
1646   IIFORMAT_VALID_KEYWORD (type, Q_width, check_valid_int);                    \
1647   IIFORMAT_VALID_KEYWORD (type, Q_height, check_valid_int);                   \
1648   IIFORMAT_VALID_KEYWORD (type, Q_pixel_width, check_valid_int_or_function);  \
1649   IIFORMAT_VALID_KEYWORD (type, Q_pixel_height, check_valid_int_or_function); \
1650   IIFORMAT_VALID_KEYWORD (type, Q_face, check_valid_face);                    \
1651 } while (0)
1652
1653 static void image_instantiator_widget(void)
1654 {                               /* we only do this for properties */
1655         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT_NO_SYM(widget, "widget");
1656         IIFORMAT_HAS_METHOD(widget, property);
1657         IIFORMAT_HAS_METHOD(widget, update);
1658         IIFORMAT_HAS_METHOD(widget, query_geometry);
1659         IIFORMAT_HAS_METHOD(widget, layout);
1660 }
1661
1662 static void image_instantiator_buttons(void)
1663 {
1664         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(button, "button");
1665         IIFORMAT_HAS_SHARED_METHOD(button, validate, widget);
1666         IIFORMAT_HAS_SHARED_METHOD(button, possible_dest_types, widget);
1667         IIFORMAT_HAS_SHARED_METHOD(button, instantiate, widget);
1668         IIFORMAT_HAS_SHARED_METHOD(button, post_instantiate, widget);
1669         IIFORMAT_HAS_SHARED_METHOD(button, normalize, widget);
1670         IIFORMAT_HAS_SHARED_METHOD(button, governing_domain, subwindow);
1671         IIFORMAT_HAS_METHOD(button, query_geometry);
1672         IIFORMAT_VALID_KEYWORD(button, Q_image, check_valid_instantiator);
1673         VALID_WIDGET_KEYWORDS(button);
1674         VALID_GUI_KEYWORDS(button);
1675 }
1676
1677 static void image_instantiator_edit_fields(void)
1678 {
1679         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(edit_field, "edit-field");
1680         IIFORMAT_HAS_SHARED_METHOD(edit_field, validate, widget);
1681         IIFORMAT_HAS_SHARED_METHOD(edit_field, possible_dest_types, widget);
1682         IIFORMAT_HAS_SHARED_METHOD(edit_field, instantiate, widget);
1683         IIFORMAT_HAS_SHARED_METHOD(edit_field, post_instantiate, widget);
1684         IIFORMAT_HAS_SHARED_METHOD(edit_field, governing_domain, subwindow);
1685         IIFORMAT_HAS_METHOD(edit_field, query_geometry);
1686         VALID_WIDGET_KEYWORDS(edit_field);
1687         VALID_GUI_KEYWORDS(edit_field);
1688 }
1689
1690 static void image_instantiator_combo_box(void)
1691 {
1692         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(combo_box, "combo-box");
1693         IIFORMAT_HAS_METHOD(combo_box, validate);
1694         IIFORMAT_HAS_SHARED_METHOD(combo_box, possible_dest_types, widget);
1695         IIFORMAT_HAS_SHARED_METHOD(combo_box, governing_domain, subwindow);
1696
1697         VALID_GUI_KEYWORDS(combo_box);
1698
1699         IIFORMAT_VALID_KEYWORD(combo_box, Q_width, check_valid_int);
1700         IIFORMAT_VALID_KEYWORD(combo_box, Q_height, check_valid_int);
1701         IIFORMAT_VALID_KEYWORD(combo_box, Q_pixel_width,
1702                                check_valid_int_or_function);
1703         IIFORMAT_VALID_KEYWORD(combo_box, Q_face, check_valid_face);
1704         IIFORMAT_VALID_KEYWORD(combo_box, Q_items, check_valid_item_list);
1705 }
1706
1707 static void image_instantiator_scrollbar(void)
1708 {
1709         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(scrollbar, "scrollbar");
1710         IIFORMAT_HAS_SHARED_METHOD(scrollbar, validate, widget);
1711         IIFORMAT_HAS_SHARED_METHOD(scrollbar, possible_dest_types, widget);
1712         IIFORMAT_HAS_SHARED_METHOD(scrollbar, instantiate, widget);
1713         IIFORMAT_HAS_SHARED_METHOD(scrollbar, post_instantiate, widget);
1714         IIFORMAT_HAS_SHARED_METHOD(scrollbar, governing_domain, subwindow);
1715         VALID_GUI_KEYWORDS(scrollbar);
1716
1717         IIFORMAT_VALID_KEYWORD(scrollbar, Q_pixel_width,
1718                                check_valid_int_or_function);
1719         IIFORMAT_VALID_KEYWORD(scrollbar, Q_pixel_height,
1720                                check_valid_int_or_function);
1721         IIFORMAT_VALID_KEYWORD(scrollbar, Q_face, check_valid_face);
1722 }
1723
1724 static void image_instantiator_progress_guage(void)
1725 {
1726         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(progress_gauge, "progress-gauge");
1727         IIFORMAT_HAS_SHARED_METHOD(progress_gauge, validate, widget);
1728         IIFORMAT_HAS_SHARED_METHOD(progress_gauge, possible_dest_types, widget);
1729         IIFORMAT_HAS_SHARED_METHOD(progress_gauge, instantiate, widget);
1730         IIFORMAT_HAS_SHARED_METHOD(progress_gauge, post_instantiate, widget);
1731         IIFORMAT_HAS_SHARED_METHOD(progress_gauge, governing_domain, subwindow);
1732         VALID_WIDGET_KEYWORDS(progress_gauge);
1733         VALID_GUI_KEYWORDS(progress_gauge);
1734
1735         IIFORMAT_VALID_KEYWORD(progress_gauge, Q_value, check_valid_int);
1736 }
1737
1738 static void image_instantiator_tree_view(void)
1739 {
1740         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(tree_view, "tree-view");
1741         IIFORMAT_HAS_SHARED_METHOD(tree_view, validate, combo_box);
1742         IIFORMAT_HAS_SHARED_METHOD(tree_view, possible_dest_types, widget);
1743         IIFORMAT_HAS_SHARED_METHOD(tree_view, instantiate, widget);
1744         IIFORMAT_HAS_SHARED_METHOD(tree_view, post_instantiate, widget);
1745         IIFORMAT_HAS_SHARED_METHOD(tree_view, governing_domain, subwindow);
1746         IIFORMAT_HAS_METHOD(tree_view, query_geometry);
1747         VALID_WIDGET_KEYWORDS(tree_view);
1748         VALID_GUI_KEYWORDS(tree_view);
1749         IIFORMAT_VALID_KEYWORD(tree_view, Q_items, check_valid_item_list);
1750 }
1751
1752 static void image_instantiator_tab_control(void)
1753 {
1754         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(tab_control, "tab-control");
1755         IIFORMAT_HAS_SHARED_METHOD(tab_control, validate, combo_box);
1756         IIFORMAT_HAS_SHARED_METHOD(tab_control, possible_dest_types, widget);
1757         IIFORMAT_HAS_SHARED_METHOD(tab_control, instantiate, widget);
1758         IIFORMAT_HAS_SHARED_METHOD(tab_control, post_instantiate, widget);
1759         IIFORMAT_HAS_SHARED_METHOD(tab_control, governing_domain, subwindow);
1760         IIFORMAT_HAS_METHOD(tab_control, query_geometry);
1761         VALID_WIDGET_KEYWORDS(tab_control);
1762         VALID_GUI_KEYWORDS(tab_control);
1763         IIFORMAT_VALID_KEYWORD(tab_control, Q_orientation,
1764                                check_valid_tab_orientation);
1765         IIFORMAT_VALID_KEYWORD(tab_control, Q_items, check_valid_item_list);
1766 }
1767
1768 static void image_instantiator_labels(void)
1769 {
1770         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(label, "label");
1771         IIFORMAT_HAS_SHARED_METHOD(label, possible_dest_types, widget);
1772         IIFORMAT_HAS_SHARED_METHOD(label, instantiate, widget);
1773         IIFORMAT_HAS_SHARED_METHOD(label, post_instantiate, widget);
1774         IIFORMAT_HAS_SHARED_METHOD(label, governing_domain, subwindow);
1775         VALID_WIDGET_KEYWORDS(label);
1776         IIFORMAT_VALID_KEYWORD(label, Q_descriptor, check_valid_string);
1777 }
1778
1779 #define VALID_LAYOUT_KEYWORDS(layout)                                      \
1780   VALID_WIDGET_KEYWORDS (layout);                                          \
1781   IIFORMAT_VALID_KEYWORD (layout, Q_orientation, check_valid_orientation); \
1782   IIFORMAT_VALID_KEYWORD (layout, Q_justify, check_valid_justification);   \
1783   IIFORMAT_VALID_KEYWORD (layout, Q_vertically_justify, check_valid_justification);   \
1784   IIFORMAT_VALID_KEYWORD (layout, Q_horizontally_justify, check_valid_justification);   \
1785   IIFORMAT_VALID_KEYWORD (layout, Q_border, check_valid_border);           \
1786   IIFORMAT_VALID_KEYWORD (layout, Q_margin_width, check_valid_int);        \
1787   IIFORMAT_VALID_KEYWORD (layout, Q_items,                                 \
1788                           check_valid_instantiator_list)
1789
1790 static void image_instantiator_layout(void)
1791 {
1792         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(layout, "layout");
1793         IIFORMAT_HAS_SHARED_METHOD(layout, possible_dest_types, widget);
1794         IIFORMAT_HAS_METHOD(layout, instantiate);
1795         IIFORMAT_HAS_METHOD(layout, post_instantiate);
1796         IIFORMAT_HAS_SHARED_METHOD(layout, governing_domain, subwindow);
1797         IIFORMAT_HAS_METHOD(layout, normalize);
1798         IIFORMAT_HAS_METHOD(layout, query_geometry);
1799         IIFORMAT_HAS_METHOD(layout, layout);
1800         IIFORMAT_HAS_METHOD(layout, update);
1801         IIFORMAT_HAS_METHOD(layout, property);
1802
1803         VALID_GUI_KEYWORDS(layout);
1804         VALID_LAYOUT_KEYWORDS(layout);
1805 }
1806
1807 static void image_instantiator_native_layout(void)
1808 {
1809         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(native_layout, "native-layout");
1810         IIFORMAT_HAS_SHARED_METHOD(native_layout, possible_dest_types, widget);
1811         IIFORMAT_HAS_SHARED_METHOD(native_layout, instantiate, layout);
1812         IIFORMAT_HAS_SHARED_METHOD(native_layout, post_instantiate, layout);
1813         IIFORMAT_HAS_METHOD(native_layout, layout);
1814         IIFORMAT_HAS_SHARED_METHOD(native_layout, governing_domain, subwindow);
1815         IIFORMAT_HAS_SHARED_METHOD(native_layout, normalize, layout);
1816         IIFORMAT_HAS_SHARED_METHOD(native_layout, query_geometry, layout);
1817         IIFORMAT_HAS_SHARED_METHOD(native_layout, layout, layout);
1818         IIFORMAT_HAS_SHARED_METHOD(native_layout, property, layout);
1819
1820         VALID_GUI_KEYWORDS(native_layout);
1821         VALID_LAYOUT_KEYWORDS(native_layout);
1822 }
1823
1824 void image_instantiator_format_create_glyphs_widget(void)
1825 {
1826         image_instantiator_widget();
1827         image_instantiator_buttons();
1828         image_instantiator_edit_fields();
1829         image_instantiator_combo_box();
1830         image_instantiator_scrollbar();
1831         image_instantiator_progress_guage();
1832         image_instantiator_tree_view();
1833         image_instantiator_tab_control();
1834         image_instantiator_labels();
1835         image_instantiator_layout();
1836         image_instantiator_native_layout();
1837 }
1838
1839 void reinit_vars_of_glyphs_widget(void)
1840 {
1841 #ifdef DEBUG_WIDGETS
1842         debug_widget_instances = 0;
1843 #endif
1844 }
1845
1846 void vars_of_glyphs_widget(void)
1847 {
1848         reinit_vars_of_glyphs_widget();
1849 }
1850
1851 void specifier_vars_of_glyphs_widget(void)
1852 {
1853         DEFVAR_SPECIFIER("widget-border-width", &Vwidget_border_width   /*
1854 *Border width of widgets.
1855 This is a specifier; use `set-specifier' to change it.
1856                                                                          */ );
1857         Vwidget_border_width = Fmake_specifier(Qnatnum);
1858 }