This commit was generated by cvs2svn to compensate for changes in r5209,
[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) && !SYMBOLP (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               /* #### We really want to use call_with_suspended_errors
1025                  here, but it won't allow us to call lisp. */
1026               border = Fcons (call1 (Qmake_glyph, XCAR (items)), border);
1027             }
1028           IMAGE_INSTANCE_LAYOUT_CHILDREN (ii) = Fnreverse (border);
1029         }
1030     }
1031   UNGCPRO;
1032 }
1033
1034 static void
1035 layout_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
1036                     Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1037                     int dest_mask, Lisp_Object domain)
1038 {
1039   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1040   Lisp_Object orient = find_keyword_in_vector (instantiator, Q_orientation);
1041
1042 #ifdef DEBUG_WIDGET_OUTPUT
1043   stderr_out ("layout instantiated\n");
1044 #endif
1045   /* Do widget type instantiation first. */
1046   widget_instantiate (image_instance, instantiator, pointer_fg, pointer_bg,
1047                       dest_mask, domain);
1048
1049   if (NILP (orient))
1050     {
1051       IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) = LAYOUT_VERTICAL;
1052     }
1053
1054   /* Get child glyphs and finish instantiation. We can't do image
1055      instance children yet as we might not have a containing
1056      window. */
1057   layout_update (image_instance, instantiator);
1058 }
1059
1060 static void
1061 layout_post_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
1062                          Lisp_Object domain)
1063 {
1064 }
1065
1066 /* Layout widget. Sizing commentary: we have a number of problems that
1067    we would like to address. Some consider some of these more
1068    important than others. It used to be that size information was
1069    determined at instantiation time and was then fixed forever
1070    after. Generally this is not what we want. Users want size to be
1071    "big enough" to accommodate whatever they are trying to show and
1072    this is dependent on text length, lines, font metrics etc. Of
1073    course these attributes can change dynamically and so the size
1074    should changed dynamically also. Only in a few limited cases should
1075    the size be fixed and remain fixed. Of course this actually means
1076    that we don't really want to specify the size *at all* for most
1077    widgets - we want it to be discovered dynamically. Thus we can
1078    envisage the following scenarios:
1079
1080    1. A button is sized to accommodate its text, the text changes and the
1081    button should change size also.
1082
1083    2. A button is given an explicit size. Its size should never change.
1084
1085    3. Layout is put inside an area. The size of the area changes, the
1086    layout should change with it.
1087
1088    4. A button grows to accommodate additional text. The whitespace
1089    around it should be modified to cope with the new layout
1090    requirements.
1091
1092    5. A button grows. The area surrounding it should grow also if
1093    possible.
1094
1095    What metrics are important?
1096    1. Actual width and height.
1097
1098    2. Whether the width and height are what the widget actually wants, or
1099    whether it can grow or shrink.
1100
1101    Text glyphs are particularly troublesome since their metrics depend
1102    on the context in which they are being viewed. For instance they
1103    can appear differently depending on the window face, frame face or
1104    glyph face. In order to simplify this text glyphs can now only have
1105    a glyph-face or image-instance face. All other glyphs are
1106    essentially fixed in appearance. Perhaps the problem is that text
1107    glyphs are cached on a device basis like most other glyphs. Instead
1108    they should be cached per-window and then the instance would be
1109    fixed and we wouldn't have to mess around with font metrics and the
1110    rest. */
1111
1112 /* Query the geometry of a layout widget. We assume that we can only
1113    get here if the size is not already fixed. */
1114 static void
1115 layout_query_geometry (Lisp_Object image_instance, int* width,
1116                        int* height, enum image_instance_geometry disp,
1117                        Lisp_Object domain)
1118 {
1119   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1120   Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii), rest;
1121   int maxph = 0, maxpw = 0, nitems = 0, ph_adjust = 0;
1122   int gheight, gwidth;
1123
1124   /* If we are not initialized then we won't have any children. */
1125   if (!IMAGE_INSTANCE_INITIALIZED (ii))
1126       return;
1127
1128   /* First just set up what we already have. */
1129   if (width)    *width = IMAGE_INSTANCE_WIDTH (ii);
1130   if (height)   *height = IMAGE_INSTANCE_HEIGHT (ii);
1131
1132   /* If we are not allowed to dynamically size then return. */
1133   if (!IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii)
1134       &&
1135       !IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
1136     return;
1137
1138   /* Pick up the border text if we have one. */
1139   if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
1140     {
1141       glyph_query_geometry (XCAR (items), &gwidth, &gheight, disp,
1142                             image_instance);
1143       ph_adjust = gheight / 2;
1144       items = XCDR (items);
1145     }
1146
1147   /* Flip through the items to work out how much stuff we have to display */
1148   LIST_LOOP (rest, items)
1149     {
1150       Lisp_Object glyph = XCAR (rest);
1151       glyph_query_geometry (glyph, &gwidth, &gheight, disp, image_instance);
1152
1153       nitems ++;
1154       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1155           == LAYOUT_HORIZONTAL)
1156         {
1157           maxph = max (maxph, gheight);
1158           maxpw += gwidth;
1159         }
1160       else
1161         {
1162           maxpw = max (maxpw, gwidth);
1163           maxph += gheight;
1164         }
1165     }
1166
1167   /* Work out minimum space we need to fit all the items. This could
1168      have been fixed by the user. */
1169   if (!NILP (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii)))
1170     {
1171       Lisp_Object dynamic_width =
1172         Feval (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii));
1173       if (INTP (dynamic_width))
1174         *width = XINT (dynamic_width);
1175     }
1176   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1177            == LAYOUT_HORIZONTAL)
1178     *width = maxpw + ((nitems + 1) * WIDGET_BORDER_WIDTH +
1179                       IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2;
1180   else
1181     *width = maxpw + 2 * (WIDGET_BORDER_WIDTH * 2 +
1182                           IMAGE_INSTANCE_MARGIN_WIDTH (ii));
1183
1184   /* Work out vertical spacings. */
1185   if (!NILP (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii)))
1186     {
1187       Lisp_Object dynamic_height =
1188         Feval (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii));
1189       if (INTP (dynamic_height))
1190         *height = XINT (dynamic_height);
1191     }
1192   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1193            == LAYOUT_VERTICAL)
1194     *height = maxph + ((nitems + 1) * WIDGET_BORDER_HEIGHT +
1195                        IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
1196   else
1197     *height = maxph + (2 * WIDGET_BORDER_HEIGHT +
1198                        IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
1199 }
1200
1201 int
1202 layout_layout (Lisp_Object image_instance,
1203                int width, int height, int xoffset, int yoffset,
1204                Lisp_Object domain)
1205 {
1206   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1207   Lisp_Object rest;
1208   Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii);
1209   int x, y, maxph = 0, maxpw = 0, nitems = 0,
1210     horiz_spacing, vert_spacing, ph_adjust = 0;
1211   int gheight, gwidth;
1212
1213   /* If we are not initialized then we won't have any children. */
1214   if (!IMAGE_INSTANCE_INITIALIZED (ii))
1215       return 0;
1216
1217   /* Pick up the border text if we have one. */
1218   if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
1219     {
1220       Lisp_Object border = XCAR (items);
1221       items = XCDR (items);
1222       glyph_query_geometry (border, &gwidth, &gheight,
1223                             IMAGE_DESIRED_GEOMETRY, image_instance);
1224       ph_adjust = gheight / 2;
1225       IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (ph_adjust);
1226
1227       /* #### Really, what should this be? */
1228       glyph_do_layout (border, gwidth, gheight, 10, 0,
1229                        image_instance);
1230     }
1231
1232   /* Flip through the items to work out how much stuff we have to display. */
1233   LIST_LOOP (rest, items)
1234     {
1235       Lisp_Object glyph = XCAR (rest);
1236
1237       glyph_query_geometry (glyph, &gwidth, &gheight,
1238                             IMAGE_DESIRED_GEOMETRY, image_instance);
1239       nitems ++;
1240       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1241           == LAYOUT_HORIZONTAL)
1242         {
1243           maxph = max (maxph, gheight);
1244           maxpw += gwidth;
1245         }
1246       else
1247         {
1248           maxpw = max (maxpw, gwidth);
1249           maxph += gheight;
1250         }
1251     }
1252
1253   /* work out spacing between items and bounds of the layout */
1254   if (width < maxpw)
1255     /* The user wants a smaller space than the largest item, so we
1256        just provide default spacing and will let the output routines
1257        clip.. */
1258     horiz_spacing = WIDGET_BORDER_WIDTH * 2;
1259   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1260            == LAYOUT_HORIZONTAL)
1261     /* We have a larger area to display in so distribute the space
1262        evenly. */
1263     horiz_spacing = (width - (maxpw +
1264                               IMAGE_INSTANCE_MARGIN_WIDTH (ii) * 2))
1265       / (nitems + 1);
1266   else
1267     horiz_spacing = (width - maxpw) / 2
1268       - IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1269
1270   if (height < maxph)
1271     vert_spacing = WIDGET_BORDER_HEIGHT * 2;
1272   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1273            == LAYOUT_VERTICAL)
1274     vert_spacing = (height - (maxph + ph_adjust +
1275                               IMAGE_INSTANCE_MARGIN_WIDTH (ii) * 2))
1276       / (nitems + 1);
1277   else
1278     vert_spacing = (height - (maxph + ph_adjust)) / 2
1279       - IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1280
1281   y = vert_spacing + ph_adjust + IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1282   x = horiz_spacing + IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1283
1284   /* Now flip through putting items where we want them, paying
1285      attention to justification. Make sure we don't mess with the
1286      border glyph. */
1287   LIST_LOOP (rest, items)
1288     {
1289       Lisp_Object glyph = XCAR (rest);
1290
1291       glyph_query_geometry (glyph, &gwidth, &gheight,
1292                             IMAGE_DESIRED_GEOMETRY, image_instance);
1293
1294       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1295           == LAYOUT_HORIZONTAL)
1296         {
1297           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1298               == LAYOUT_JUSTIFY_RIGHT)
1299             y = height - (gheight + vert_spacing);
1300           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1301               == LAYOUT_JUSTIFY_CENTER)
1302             y = (height - gheight) / 2;
1303         }
1304       else
1305         {
1306           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1307               == LAYOUT_JUSTIFY_RIGHT)
1308             x = width - (gwidth + horiz_spacing);
1309           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1310               == LAYOUT_JUSTIFY_CENTER)
1311             x = (width - gwidth) / 2;
1312         }
1313
1314       /* Now layout subwidgets if they require it. */
1315       glyph_do_layout (glyph, gwidth, gheight, x, y, image_instance);
1316
1317       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1318           == LAYOUT_HORIZONTAL)
1319         {
1320           x += (gwidth + horiz_spacing);
1321         }
1322       else
1323         {
1324           y += (gheight + vert_spacing);
1325         }
1326
1327     }
1328   return 1;
1329 }
1330
1331 /* Get the glyphs that comprise a layout. These are created internally
1332    and so are otherwise inaccessible to lisp. We need some way of getting
1333    properties from the widgets that comprise a layout and this is the
1334    simplest way of doing it.
1335
1336    #### Eventually we should allow some more intelligent access to
1337    sub-widgets. */
1338 static Lisp_Object
1339 layout_property (Lisp_Object image_instance, Lisp_Object prop)
1340 {
1341   /* This function can GC. */
1342   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1343   if (EQ (prop, Q_items))
1344     {
1345       if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)) &&
1346           CONSP (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii)))
1347         return Fcopy_sequence (XCDR
1348                                (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii)));
1349       else
1350         return Fcopy_sequence (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii));
1351     }
1352   return Qunbound;
1353 }
1354
1355 /* Layout subwindows if they are real subwindows. */
1356 static int
1357 native_layout_layout (Lisp_Object image_instance,
1358                       int width, int height, int xoffset, int yoffset,
1359                       Lisp_Object domain)
1360 {
1361   Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
1362   Lisp_Object rest;
1363
1364   /* The first time this gets called, the layout will be only
1365      partially instantiated. The children get done in
1366      post_instantiate. */
1367   if (!IMAGE_INSTANCE_INITIALIZED (ii))
1368     return 0;
1369
1370   /* Defining this overrides the default layout_layout so we first have to call that to get
1371      suitable instances and values set up. */
1372   layout_layout (image_instance, width, height, xoffset, yoffset, domain);
1373
1374   LIST_LOOP (rest, IMAGE_INSTANCE_LAYOUT_CHILDREN (ii))
1375     {
1376       struct display_glyph_area dga;
1377       dga.xoffset = 0;
1378       dga.yoffset = 0;
1379       dga.width = IMAGE_INSTANCE_WIDTH (ii);
1380       dga.height = IMAGE_INSTANCE_HEIGHT (ii);
1381
1382       map_subwindow (XCAR (rest),
1383                      IMAGE_INSTANCE_XOFFSET (ii),
1384                      IMAGE_INSTANCE_YOFFSET (ii), &dga);
1385     }
1386   return 1;
1387 }
1388
1389 \f
1390 /************************************************************************/
1391 /*                            initialization                            */
1392 /************************************************************************/
1393
1394 void
1395 syms_of_glyphs_widget (void)
1396 {
1397   DEFSYMBOL (Qetched_in);
1398   DEFSYMBOL (Qetched_out);
1399   DEFSYMBOL (Qbevel_in);
1400   DEFSYMBOL (Qbevel_out);
1401   DEFSYMBOL (Qmake_glyph);
1402 }
1403
1404 #define VALID_GUI_KEYWORDS(type) do {                                         \
1405   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_active, check_valid_anything);      \
1406   IIFORMAT_VALID_KEYWORD (type, Q_suffix, check_valid_anything);              \
1407   IIFORMAT_VALID_KEYWORD (type, Q_keys, check_valid_string);                  \
1408   IIFORMAT_VALID_KEYWORD (type, Q_style, check_valid_symbol);                 \
1409   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_selected, check_valid_anything);    \
1410   IIFORMAT_VALID_KEYWORD (type, Q_filter, check_valid_anything);              \
1411   IIFORMAT_VALID_KEYWORD (type, Q_config, check_valid_symbol);                \
1412   IIFORMAT_VALID_KEYWORD (type, Q_included, check_valid_anything);            \
1413   IIFORMAT_VALID_KEYWORD (type, Q_initial_focus, check_valid_anything);       \
1414   IIFORMAT_VALID_KEYWORD (type, Q_key_sequence, check_valid_string);          \
1415   IIFORMAT_VALID_KEYWORD (type, Q_accelerator, check_valid_string);           \
1416   IIFORMAT_VALID_KEYWORD (type, Q_label, check_valid_anything);               \
1417   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback, check_valid_callback);    \
1418   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback_ex, check_valid_callback); \
1419   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_descriptor,                         \
1420                                   check_valid_string_or_vector);              \
1421 } while (0)
1422
1423 #define VALID_WIDGET_KEYWORDS(type) do {                                      \
1424   IIFORMAT_VALID_KEYWORD (type, Q_width, check_valid_int);                    \
1425   IIFORMAT_VALID_KEYWORD (type, Q_height, check_valid_int);                   \
1426   IIFORMAT_VALID_KEYWORD (type, Q_pixel_width, check_valid_int_or_function);  \
1427   IIFORMAT_VALID_KEYWORD (type, Q_pixel_height, check_valid_int_or_function); \
1428   IIFORMAT_VALID_KEYWORD (type, Q_face, check_valid_face);                    \
1429 } while (0)
1430
1431
1432 static void image_instantiator_widget (void)
1433 { /* we only do this for properties */
1434   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT_NO_SYM (widget, "widget");
1435   IIFORMAT_HAS_METHOD (widget, property);
1436   IIFORMAT_HAS_METHOD (widget, update);
1437   IIFORMAT_HAS_METHOD (widget, query_geometry);
1438   IIFORMAT_HAS_METHOD (widget, layout);
1439 }
1440
1441 static void image_instantiator_buttons (void)
1442 {
1443   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (button, "button");
1444   IIFORMAT_HAS_SHARED_METHOD (button, validate, widget);
1445   IIFORMAT_HAS_SHARED_METHOD (button, possible_dest_types, widget);
1446   IIFORMAT_HAS_SHARED_METHOD (button, instantiate, widget);
1447   IIFORMAT_HAS_SHARED_METHOD (button, post_instantiate, widget);
1448   IIFORMAT_HAS_SHARED_METHOD (button, normalize, widget);
1449   IIFORMAT_HAS_SHARED_METHOD (button, governing_domain, subwindow);
1450   IIFORMAT_HAS_METHOD (button, query_geometry);
1451   IIFORMAT_VALID_KEYWORD (button,
1452                           Q_image, check_valid_instantiator);
1453   VALID_WIDGET_KEYWORDS (button);
1454   VALID_GUI_KEYWORDS (button);
1455 }
1456
1457 static void image_instantiator_edit_fields (void)
1458 {
1459   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (edit_field, "edit-field");
1460   IIFORMAT_HAS_SHARED_METHOD (edit_field, validate, widget);
1461   IIFORMAT_HAS_SHARED_METHOD (edit_field, possible_dest_types, widget);
1462   IIFORMAT_HAS_SHARED_METHOD (edit_field, instantiate, widget);
1463   IIFORMAT_HAS_SHARED_METHOD (edit_field, post_instantiate, widget);
1464   IIFORMAT_HAS_SHARED_METHOD (edit_field, governing_domain, subwindow);
1465   VALID_WIDGET_KEYWORDS (edit_field);
1466   VALID_GUI_KEYWORDS (edit_field);
1467 }
1468
1469 static void image_instantiator_combo_box (void)
1470 {
1471   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (combo_box, "combo-box");
1472   IIFORMAT_HAS_METHOD (combo_box, validate);
1473   IIFORMAT_HAS_SHARED_METHOD (combo_box, possible_dest_types, widget);
1474   IIFORMAT_HAS_SHARED_METHOD (combo_box, governing_domain, subwindow);
1475
1476   VALID_GUI_KEYWORDS (combo_box);
1477
1478   IIFORMAT_VALID_KEYWORD (combo_box, Q_width, check_valid_int);
1479   IIFORMAT_VALID_KEYWORD (combo_box, Q_height, check_valid_int);
1480   IIFORMAT_VALID_KEYWORD (combo_box, Q_pixel_width,
1481                           check_valid_int_or_function);
1482   IIFORMAT_VALID_KEYWORD (combo_box, Q_face, check_valid_face);
1483   IIFORMAT_VALID_KEYWORD (combo_box, Q_items, check_valid_item_list);
1484 }
1485
1486 static void image_instantiator_scrollbar (void)
1487 {
1488   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (scrollbar, "scrollbar");
1489   IIFORMAT_HAS_SHARED_METHOD (scrollbar, validate, widget);
1490   IIFORMAT_HAS_SHARED_METHOD (scrollbar, possible_dest_types, widget);
1491   IIFORMAT_HAS_SHARED_METHOD (scrollbar, instantiate, widget);
1492   IIFORMAT_HAS_SHARED_METHOD (scrollbar, post_instantiate, widget);
1493   IIFORMAT_HAS_SHARED_METHOD (scrollbar, governing_domain, subwindow);
1494   VALID_GUI_KEYWORDS (scrollbar);
1495
1496   IIFORMAT_VALID_KEYWORD (scrollbar, Q_pixel_width,
1497                           check_valid_int_or_function);
1498   IIFORMAT_VALID_KEYWORD (scrollbar, Q_pixel_height,
1499                           check_valid_int_or_function);
1500   IIFORMAT_VALID_KEYWORD (scrollbar, Q_face, check_valid_face);
1501 }
1502
1503 static void image_instantiator_progress_guage (void)
1504 {
1505   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (progress_gauge, "progress-gauge");
1506   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, validate, widget);
1507   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, possible_dest_types, widget);
1508   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, instantiate, widget);
1509   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, post_instantiate, widget);
1510   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, governing_domain, subwindow);
1511   VALID_WIDGET_KEYWORDS (progress_gauge);
1512   VALID_GUI_KEYWORDS (progress_gauge);
1513
1514   IIFORMAT_VALID_KEYWORD (progress_gauge, Q_value, check_valid_int);
1515 }
1516
1517 static void image_instantiator_tree_view (void)
1518 {
1519   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (tree_view, "tree-view");
1520   IIFORMAT_HAS_SHARED_METHOD (tree_view, validate, combo_box);
1521   IIFORMAT_HAS_SHARED_METHOD (tree_view, possible_dest_types, widget);
1522   IIFORMAT_HAS_SHARED_METHOD (tree_view, instantiate, widget);
1523   IIFORMAT_HAS_SHARED_METHOD (tree_view, post_instantiate, widget);
1524   IIFORMAT_HAS_SHARED_METHOD (tree_view, governing_domain, subwindow);
1525   IIFORMAT_HAS_METHOD (tree_view, query_geometry);
1526   VALID_WIDGET_KEYWORDS (tree_view);
1527   VALID_GUI_KEYWORDS (tree_view);
1528   IIFORMAT_VALID_KEYWORD (tree_view, Q_items, check_valid_item_list);
1529 }
1530
1531 static void image_instantiator_tab_control (void)
1532 {
1533   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (tab_control, "tab-control");
1534   IIFORMAT_HAS_SHARED_METHOD (tab_control, validate, combo_box);
1535   IIFORMAT_HAS_SHARED_METHOD (tab_control, possible_dest_types, widget);
1536   IIFORMAT_HAS_SHARED_METHOD (tab_control, instantiate, widget);
1537   IIFORMAT_HAS_SHARED_METHOD (tab_control, post_instantiate, widget);
1538   IIFORMAT_HAS_SHARED_METHOD (tab_control, governing_domain, subwindow);
1539   IIFORMAT_HAS_METHOD (tab_control, query_geometry);
1540   VALID_WIDGET_KEYWORDS (tab_control);
1541   VALID_GUI_KEYWORDS (tab_control);
1542   IIFORMAT_VALID_KEYWORD (tab_control, Q_orientation,
1543                           check_valid_tab_orientation);
1544   IIFORMAT_VALID_KEYWORD (tab_control, Q_items, check_valid_item_list);
1545 }
1546
1547 static void image_instantiator_labels (void)
1548 {
1549   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (label, "label");
1550   IIFORMAT_HAS_SHARED_METHOD (label, possible_dest_types, widget);
1551   IIFORMAT_HAS_SHARED_METHOD (label, instantiate, widget);
1552   IIFORMAT_HAS_SHARED_METHOD (label, post_instantiate, widget);
1553   IIFORMAT_HAS_SHARED_METHOD (label, governing_domain, subwindow);
1554   VALID_WIDGET_KEYWORDS (label);
1555   IIFORMAT_VALID_KEYWORD (label, Q_descriptor, check_valid_string);
1556 }
1557
1558 #define VALID_LAYOUT_KEYWORDS(layout)                                      \
1559   VALID_WIDGET_KEYWORDS (layout);                                          \
1560   IIFORMAT_VALID_KEYWORD (layout, Q_orientation, check_valid_orientation); \
1561   IIFORMAT_VALID_KEYWORD (layout, Q_justify, check_valid_justification);   \
1562   IIFORMAT_VALID_KEYWORD (layout, Q_border, check_valid_border);           \
1563   IIFORMAT_VALID_KEYWORD (layout, Q_margin_width, check_valid_int);        \
1564   IIFORMAT_VALID_KEYWORD (layout, Q_items,                                 \
1565                           check_valid_instantiator_list)
1566
1567 static void image_instantiator_layout (void)
1568 {
1569   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (layout, "layout");
1570   IIFORMAT_HAS_SHARED_METHOD (layout, possible_dest_types, widget);
1571   IIFORMAT_HAS_METHOD (layout, instantiate);
1572   IIFORMAT_HAS_METHOD (layout, post_instantiate);
1573   IIFORMAT_HAS_SHARED_METHOD (layout, governing_domain, subwindow);
1574   IIFORMAT_HAS_METHOD (layout, normalize);
1575   IIFORMAT_HAS_METHOD (layout, query_geometry);
1576   IIFORMAT_HAS_METHOD (layout, layout);
1577   IIFORMAT_HAS_METHOD (layout, update);
1578   IIFORMAT_HAS_METHOD (layout, property);
1579
1580   VALID_GUI_KEYWORDS (layout);
1581   VALID_LAYOUT_KEYWORDS (layout);
1582 }
1583
1584 static void image_instantiator_native_layout (void)
1585 {
1586   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (native_layout, "native-layout");
1587   IIFORMAT_HAS_SHARED_METHOD (native_layout, possible_dest_types, widget);
1588   IIFORMAT_HAS_SHARED_METHOD (native_layout, instantiate, layout);
1589   IIFORMAT_HAS_SHARED_METHOD (native_layout, post_instantiate, layout);
1590   IIFORMAT_HAS_METHOD (native_layout, layout);
1591   IIFORMAT_HAS_SHARED_METHOD (native_layout, governing_domain, subwindow);
1592   IIFORMAT_HAS_SHARED_METHOD (native_layout, normalize, layout);
1593   IIFORMAT_HAS_SHARED_METHOD (native_layout, query_geometry, layout);
1594   IIFORMAT_HAS_SHARED_METHOD (native_layout, layout, layout);
1595   IIFORMAT_HAS_SHARED_METHOD (native_layout, property, layout);
1596
1597   VALID_GUI_KEYWORDS (native_layout);
1598   VALID_LAYOUT_KEYWORDS (native_layout);
1599 }
1600
1601 void
1602 image_instantiator_format_create_glyphs_widget (void)
1603 {
1604   image_instantiator_widget();
1605   image_instantiator_buttons();
1606   image_instantiator_edit_fields();
1607   image_instantiator_combo_box();
1608   image_instantiator_scrollbar();
1609   image_instantiator_progress_guage();
1610   image_instantiator_tree_view();
1611   image_instantiator_tab_control();
1612   image_instantiator_labels();
1613   image_instantiator_layout();
1614   image_instantiator_native_layout();
1615 }
1616
1617 void
1618 reinit_vars_of_glyphs_widget (void)
1619 {
1620 #ifdef DEBUG_WIDGETS
1621   debug_widget_instances = 0;
1622 #endif
1623 }
1624
1625 void
1626 vars_of_glyphs_widget (void)
1627 {
1628   reinit_vars_of_glyphs_widget ();
1629 }