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