This commit was generated by cvs2svn to compensate for changes in r1705,
[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
298   Lisp_Object text = find_keyword_in_vector (instantiator, Q_text);
299   /* Pick up any generic properties that we might need to keep hold
300      of. */
301   if (!NILP (text))
302     {
303       IMAGE_INSTANCE_WIDGET_TEXT (ii) = text;
304       IMAGE_INSTANCE_TEXT_CHANGED (ii) = 1;
305     }
306
307   /* Now try device specific methods first ... */
308   meths = decode_device_ii_format (image_instance_device (image_instance),
309                                    IMAGE_INSTANCE_WIDGET_TYPE (ii),
310                                    ERROR_ME_NOT);
311   MAYBE_IIFORMAT_METH (meths, update, (image_instance, instantiator));
312   /* ... then format specific methods ... */
313   meths = decode_device_ii_format (Qnil, IMAGE_INSTANCE_WIDGET_TYPE (ii),
314                                    ERROR_ME_NOT);
315   MAYBE_IIFORMAT_METH (meths, update, (image_instance, instantiator));
316 #if 0 /* The usefulness of this is dubious. */
317   /* we didn't do any device specific properties, so shove the property in our plist. */
318   IMAGE_INSTANCE_WIDGET_PROPS (ii)
319     = Fplist_put (IMAGE_INSTANCE_WIDGET_PROPS (ii), prop, val);
320 #endif
321 }
322
323 /* Like the rest of redisplay, we want widget updates to occur
324    asynchronously. Thus toolkit specific methods for setting
325    properties must be called by redisplay instead of by *_update. Thus
326    *_update records the change and this function actually implements
327    it. We want to be slightly clever about this however by supplying
328    format specific functions for the updates instead of lumping them
329    all into this function. Note that there is no need for format
330    generic functions. This is not the same as widget_update! */
331 void
332 redisplay_widget (Lisp_Object widget)
333 {
334   Lisp_Image_Instance* ii = XIMAGE_INSTANCE (widget);
335   struct image_instantiator_methods* meths;
336
337   if (!WIDGET_IMAGE_INSTANCEP (widget)
338       || EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qlayout)
339       || EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qnative_layout))
340     return;
341
342   /* Device-format specific methods - e.g. x_tab_control_redisplay () */
343   meths = decode_device_ii_format (image_instance_device (widget), 
344                                    IMAGE_INSTANCE_WIDGET_TYPE (ii), 
345                                    ERROR_ME_NOT);
346   MAYBE_IIFORMAT_METH (meths, redisplay, (widget));
347
348   /* Device generic methods - e.g. x_redisplay_widget (). We must
349      update the widget's size as it may have been changed by the the
350      layout routines. We also do this here so that explicit resizing
351      from lisp does not result in synchronous updates. Do this last so
352      that format-specific methods have an opportunity to prevent
353      wholesale changes - e.g. rebuilding tabs. */
354   MAYBE_DEVMETH (DOMAIN_XDEVICE (ii->domain), redisplay_widget, (ii));
355
356   /* Pick up the items we recorded earlier. */
357   if (IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii))
358     {
359       IMAGE_INSTANCE_WIDGET_ITEMS (ii) =
360         IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii);
361       IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) = Qnil;
362     }
363 }
364
365 /* Query for a widgets desired geometry. If no type specific method is
366    provided then use the widget text to calculate sizes. */
367 static void
368 widget_query_geometry (Lisp_Object image_instance,
369                        int* width, int* height,
370                        enum image_instance_geometry disp, Lisp_Object domain)
371 {
372   Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
373   struct image_instantiator_methods* meths;
374   Lisp_Object dynamic_width = Qnil;
375   Lisp_Object dynamic_height = Qnil;
376
377   /* First just set up what we already have. */
378   if (width)    *width = IMAGE_INSTANCE_WIDTH (ii);
379   if (height)   *height = IMAGE_INSTANCE_HEIGHT (ii);
380
381   if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii)
382       ||
383       IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
384     {
385       /* .. then try device specific methods ... */
386       meths = decode_device_ii_format (image_instance_device (image_instance),
387                                        IMAGE_INSTANCE_WIDGET_TYPE (ii),
388                                        ERROR_ME_NOT);
389       if (meths && HAS_IIFORMAT_METH_P (meths, query_geometry))
390         IIFORMAT_METH (meths, query_geometry, (image_instance,
391                                                width, height, disp,
392                                                domain));
393       else
394         {
395           /* ... then format specific methods ... */
396           meths = decode_device_ii_format (Qnil, IMAGE_INSTANCE_WIDGET_TYPE (ii),
397                                            ERROR_ME_NOT);
398           if (meths && HAS_IIFORMAT_METH_P (meths, query_geometry))
399             IIFORMAT_METH (meths, query_geometry, (image_instance,
400                                                    width, height, disp,
401                                                    domain));
402           else
403             {
404               int w, h;
405
406               /* Then if we are allowed to resize the widget, make the
407                  size the same as the text dimensions. */
408               query_string_geometry (IMAGE_INSTANCE_WIDGET_TEXT (ii),
409                                      IMAGE_INSTANCE_WIDGET_FACE (ii),
410                                      &w, &h, 0, domain);
411               /* Adjust the size for borders. */
412               if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
413                 *width = w + 2 * WIDGET_BORDER_WIDTH;
414               if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii))
415                 *height = h +  2 * WIDGET_BORDER_HEIGHT;
416             }
417         }
418       /* Finish off with dynamic sizing. */
419       if (!NILP (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii)))
420         {
421           dynamic_width = Feval (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii));
422           if (INTP (dynamic_width))
423             *width = XINT (dynamic_width);
424         }
425       if (!NILP (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii)))
426         {
427           dynamic_height = Feval (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii));
428           if (INTP (dynamic_height))
429             *height = XINT (dynamic_height);
430         }
431     }
432 }
433
434 static int
435 widget_layout (Lisp_Object image_instance,
436                int width, int height, int xoffset, int yoffset,
437                Lisp_Object domain)
438 {
439   Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
440   struct image_instantiator_methods* meths;
441
442   /* .. then try device specific methods ... */
443   meths = decode_device_ii_format (image_instance_device (image_instance),
444                                    IMAGE_INSTANCE_WIDGET_TYPE (ii),
445                                    ERROR_ME_NOT);
446   if (meths && HAS_IIFORMAT_METH_P (meths, layout))
447     return IIFORMAT_METH (meths, layout, (image_instance,
448                                           width, height, xoffset, yoffset,
449                                           domain));
450   else
451     {
452       /* ... then format specific methods ... */
453       meths = decode_device_ii_format (Qnil, IMAGE_INSTANCE_WIDGET_TYPE (ii),
454                                        ERROR_ME_NOT);
455       if (meths && HAS_IIFORMAT_METH_P (meths, layout))
456         return IIFORMAT_METH (meths, layout, (image_instance,
457                                               width, height, xoffset, yoffset,
458                                               domain));
459     }
460   return 1;
461 }
462
463 static void
464 widget_validate (Lisp_Object instantiator)
465 {
466   Lisp_Object desc = find_keyword_in_vector (instantiator, Q_descriptor);
467
468   if (NILP (desc))
469     syntax_error ("Must supply :descriptor", instantiator);
470
471   if (VECTORP (desc))
472     gui_parse_item_keywords (desc);
473
474   if (!NILP (find_keyword_in_vector (instantiator, Q_width))
475       && !NILP (find_keyword_in_vector (instantiator, Q_pixel_width)))
476     syntax_error ("Must supply only one of :width and :pixel-width", instantiator);
477
478   if (!NILP (find_keyword_in_vector (instantiator, Q_height))
479              && !NILP (find_keyword_in_vector (instantiator, Q_pixel_height)))
480     syntax_error ("Must supply only one of :height and :pixel-height", instantiator);
481 }
482
483 static void
484 combo_box_validate (Lisp_Object instantiator)
485 {
486   widget_validate (instantiator);
487   if (NILP (find_keyword_in_vector (instantiator, Q_items)))
488     syntax_error ("Must supply item list", instantiator);
489 }
490
491 /* we need to convert things like glyphs to images, eval expressions
492    etc.*/
493 static Lisp_Object
494 widget_normalize (Lisp_Object inst, Lisp_Object console_type,
495                   Lisp_Object dest_mask)
496 {
497   /* This function can call lisp */
498   Lisp_Object glyph = find_keyword_in_vector (inst, Q_image);
499
500   /* we need to eval glyph if its an expression, we do this for the
501      same reasons we normalize file to data.
502
503      #### should just normalize the data. */
504   if (!NILP (glyph))
505     {
506       substitute_keyword_value (inst, Q_image, glyph_instantiator_to_glyph (glyph));
507     }
508
509   return inst;
510 }
511
512 static void
513 initialize_widget_image_instance (Lisp_Image_Instance *ii, Lisp_Object type)
514 {
515   /*  initialize_subwindow_image_instance (ii);*/
516   IMAGE_INSTANCE_WIDGET_TYPE (ii) = type;
517   IMAGE_INSTANCE_WIDGET_PROPS (ii) = Qnil;
518   SET_IMAGE_INSTANCE_WIDGET_FACE (ii, Qnil);
519   IMAGE_INSTANCE_WIDGET_ITEMS (ii) = allocate_gui_item ();
520   IMAGE_INSTANCE_LAYOUT_CHILDREN (ii) = Qnil;
521   IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) = Qnil;
522   IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii) = Qnil;
523   IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii) = Qnil;
524   IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 1;
525   IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 1;
526   IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) = LAYOUT_HORIZONTAL;
527   IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii) = 0;
528 }
529
530 /* Instantiate a button widget. Unfortunately instantiated widgets are
531    particular to a frame since they need to have a parent. It's not
532    like images where you just select the image into the context you
533    want to display it in and BitBlt it. So image instances can have a
534    many-to-one relationship with things you see, whereas widgets can
535    only be one-to-one (i.e. per frame) */
536 void
537 widget_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
538                     Lisp_Object pointer_fg, Lisp_Object pointer_bg,
539                     int dest_mask, Lisp_Object domain)
540 {
541   /* #### practically all of this should be moved to widget_update()
542      so that users can dynamically change all possible widget
543      properties. */
544   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
545   Lisp_Object face = find_keyword_in_vector (instantiator, Q_face);
546   Lisp_Object height = find_keyword_in_vector (instantiator, Q_height);
547   Lisp_Object width = find_keyword_in_vector (instantiator, Q_width);
548   Lisp_Object pixwidth = find_keyword_in_vector (instantiator, Q_pixel_width);
549   Lisp_Object pixheight = find_keyword_in_vector (instantiator, Q_pixel_height);
550   Lisp_Object desc = find_keyword_in_vector (instantiator, Q_descriptor);
551   Lisp_Object glyph = find_keyword_in_vector (instantiator, Q_image);
552   Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
553   Lisp_Object orient = find_keyword_in_vector (instantiator, Q_orientation);
554   Lisp_Object mwidth = find_keyword_in_vector (instantiator, Q_margin_width);
555   Lisp_Object ifocus = find_keyword_in_vector (instantiator, Q_initial_focus);
556   int pw=0, ph=0, tw=0, th=0;
557
558   /* this just does pixel type sizing */
559   subwindow_instantiate (image_instance, instantiator, pointer_fg, pointer_bg,
560                          dest_mask, domain);
561
562   if (!(dest_mask & IMAGE_WIDGET_MASK))
563     incompatible_image_types (instantiator, dest_mask, IMAGE_WIDGET_MASK);
564
565   initialize_widget_image_instance (ii, XVECTOR_DATA (instantiator)[0]);
566
567   IMAGE_INSTANCE_TYPE (ii) = IMAGE_WIDGET;
568
569   /* retrieve the fg and bg colors */
570   if (!NILP (face))
571     SET_IMAGE_INSTANCE_WIDGET_FACE (ii, Fget_face (face));
572
573   /* retrieve the gui item information. This is easy if we have been
574      provided with a vector, more difficult if we have just been given
575      keywords */
576   if (STRINGP (desc) || NILP (desc))
577     {
578       /* big cheat - we rely on the fact that a gui item looks like an instantiator */
579       IMAGE_INSTANCE_WIDGET_ITEMS (ii) =
580         gui_parse_item_keywords_no_errors (instantiator);
581       IMAGE_INSTANCE_WIDGET_TEXT (ii) = desc;
582     }
583   else
584     IMAGE_INSTANCE_WIDGET_ITEMS (ii) =
585       gui_parse_item_keywords_no_errors (desc);
586
587   /* Pick up the orientation before we do our first layout. */
588   if (EQ (orient, Qleft) || EQ (orient, Qright) || EQ (orient, Qvertical))
589     IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) = LAYOUT_VERTICAL;
590
591   /* parse more gui items out of the properties */
592   if (!NILP (items) && !EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qlayout)
593       && !EQ (IMAGE_INSTANCE_WIDGET_TYPE (ii), Qnative_layout))
594     {
595       IMAGE_INSTANCE_WIDGET_ITEMS (ii) =
596         Fcons (IMAGE_INSTANCE_WIDGET_ITEMS (ii),
597                parse_gui_item_tree_children (items));
598     }
599
600   /* Normalize size information. We now only assign sizes if the user
601      gives us some explicitly, or there are some constraints that we
602      can't change later on. Otherwise we postpone sizing until query
603      geometry gets called. */
604   if (!NILP (pixwidth))         /* pixwidth takes precendent */
605     {
606       if (!INTP (pixwidth))
607         IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii) = pixwidth;
608       else
609         {
610           pw = XINT (pixwidth);
611           IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 0;
612         }
613     }
614   else if (!NILP (width))
615     {
616       tw = XINT (width);
617       IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 0;
618     }
619
620   if (!NILP (pixheight))
621     {
622       if (!INTP (pixheight))
623         IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii) = pixheight;
624       else
625         {
626           ph = XINT (pixheight);
627           IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 0;
628         }
629     }
630   else if (!NILP (height) && XINT (height) > 1)
631     {
632       th = XINT (height);
633       IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 0;
634     }
635
636   /* Taking the default face information when the user has specified
637      size in characters is probably as good as any since the widget
638      face is more likely to be proportional and thus give inadequate
639      results. Using character sizes can only ever be approximate
640      anyway. */
641   if (tw || th)
642     {
643       int charwidth, charheight;
644       default_face_font_info (domain, 0, 0, &charheight, &charwidth, 0);
645       if (tw)
646         pw = charwidth * tw;
647       if (th)
648         ph = charheight * th;
649     }
650
651   /* for a widget with an image pick up the dimensions from that */
652   if (!NILP (glyph))
653     {
654       if (!pw)
655         pw = glyph_width (glyph, image_instance) + 2 * WIDGET_BORDER_WIDTH;
656       if (!ph)
657         ph = glyph_height (glyph, image_instance) + 2 * WIDGET_BORDER_HEIGHT;
658       IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii) = 0;
659       IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii) = 0;
660     }
661
662   /* Pick up the margin width. */
663   if (!NILP (mwidth))
664     IMAGE_INSTANCE_MARGIN_WIDTH (ii) = XINT (mwidth);
665
666   IMAGE_INSTANCE_WANTS_INITIAL_FOCUS (ii) = !NILP (ifocus);
667
668   /* Layout for the layout widget is premature at this point since the
669      children will not have been instantiated. We can't instantiate
670      them until the device instantiation method for the layout has
671      been executed. We do however want to record any specified
672      dimensions. */
673   if (pw)       IMAGE_INSTANCE_WIDTH (ii) = pw;
674   if (ph)       IMAGE_INSTANCE_HEIGHT (ii) = ph;
675 }
676
677 static void
678 widget_post_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
679                          Lisp_Object domain)
680 {
681 #ifdef DEBUG_WIDGETS
682   debug_widget_instances++;
683   stderr_out ("instantiated ");
684   debug_print (instantiator);
685   stderr_out ("%d widgets instantiated\n", debug_widget_instances);
686 #endif
687 }
688
689 /* Get the geometry of a button control. We need to adjust the size
690    depending on the type of button. */
691 static void
692 button_query_geometry (Lisp_Object image_instance,
693                        int* width, int* height,
694                        enum image_instance_geometry disp, Lisp_Object domain)
695 {
696   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
697   int w, h;
698   query_string_geometry (IMAGE_INSTANCE_WIDGET_TEXT (ii),
699                          IMAGE_INSTANCE_WIDGET_FACE (ii),
700                          &w, &h, 0, domain);
701   /* Adjust the size for borders. */
702   if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
703     {
704       *width = w + 2 * WIDGET_BORDER_WIDTH;
705
706       if (EQ (XGUI_ITEM (IMAGE_INSTANCE_WIDGET_ITEM (ii))->style, Qradio)
707           ||
708           EQ (XGUI_ITEM (IMAGE_INSTANCE_WIDGET_ITEM (ii))->style, Qtoggle))
709         /* This is an approximation to the size of the actual button bit. */
710         *width += 12;
711     }
712   if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii))
713     *height = h +  2 * WIDGET_BORDER_HEIGHT;
714 }
715
716 /* tree-view geometry - get the height right */
717 static void
718 tree_view_query_geometry (Lisp_Object image_instance,
719                           int* width, int* height,
720                           enum image_instance_geometry disp, Lisp_Object domain)
721 {
722   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
723   Lisp_Object items = IMAGE_INSTANCE_WIDGET_ITEMS (ii);
724
725
726   if (*width)
727     {
728       /* #### what should this be. reconsider when X has tree views. */
729       query_string_geometry (IMAGE_INSTANCE_WIDGET_TEXT (ii),
730                              IMAGE_INSTANCE_WIDGET_FACE (ii),
731                              width, 0, 0, domain);
732     }
733   if (*height)
734     {
735       int len, h;
736       default_face_font_info (domain, 0, 0, &h, 0, 0);
737       GET_LIST_LENGTH (items, len);
738       *height = len * h;
739     }
740 }
741
742 /* Get the geometry of a tab control. This is based on the number of
743    items and text therin in the tab control. */
744 static void
745 tab_control_query_geometry (Lisp_Object image_instance,
746                             int* width, int* height,
747                             enum image_instance_geometry disp, Lisp_Object domain)
748 {
749   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
750   Lisp_Object items = XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii));
751   Lisp_Object rest;
752   int tw = 0, th = 0;
753
754   LIST_LOOP (rest, items)
755     {
756       int h, w;
757
758       query_string_geometry (XGUI_ITEM (XCAR (rest))->name,
759                              IMAGE_INSTANCE_WIDGET_FACE (ii),
760                              &w, &h, 0, domain);
761       tw += 5 * WIDGET_BORDER_WIDTH; /* some bias */
762       tw += w;
763       th = max (th, h + 2 * WIDGET_BORDER_HEIGHT);
764     }
765
766   /* Fixup returned values depending on orientation. */
767   if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii))
768     {
769       if (height)       *height = tw;
770       if (width)        *width = th;
771     }
772   else
773     {
774       if (height)       *height = th;
775       if (width)        *width = tw;
776     }
777 }
778
779 /* Update the contents of a tab control. */
780 static void
781 tab_control_update (Lisp_Object image_instance,
782                     Lisp_Object instantiator)
783 {
784   Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
785   /* Record new items for update. *_tab_control_redisplay will do the
786      rest. */
787   if (!NILP (items))
788     {
789       Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
790       check_valid_item_list (items);
791 #ifdef DEBUG_WIDGET_OUTPUT
792       stderr_out ("tab control %p updated\n", IMAGE_INSTANCE_SUBWINDOW_ID (ii));
793 #endif
794       /* Don't set the actual items since we might decide not to use
795          the new ones (because nothing has really changed). If we did
796          set them and didn't use them then we would get into whole
797          heaps of trouble when the old items get GC'd. */
798       IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) =
799         Fcons (XCAR (IMAGE_INSTANCE_WIDGET_ITEMS (ii)),
800                parse_gui_item_tree_children (items));
801       IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii) = 1;
802     }
803 }
804
805 /* Determine whether only the order has changed for a tab. */
806 int tab_control_order_only_changed (Lisp_Object image_instance)
807 {
808   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
809   int found = 0, len, pending_len;
810   Lisp_Object rest;
811
812   /* Degenerate case. */
813   if (NILP (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
814     return 1;
815
816   /* See whether we just need a change in order. */
817   GET_LIST_LENGTH (IMAGE_INSTANCE_WIDGET_ITEMS (ii), len);
818   GET_LIST_LENGTH (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii),
819                    pending_len);
820   if (len == pending_len)
821     {
822       LIST_LOOP (rest, XCDR (IMAGE_INSTANCE_WIDGET_ITEMS (ii)))
823         {
824           Lisp_Object pending_rest;
825           found = 0;
826           LIST_LOOP (pending_rest,
827                      XCDR (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
828             {
829               if (gui_item_equal_sans_selected (XCAR (rest),
830                                                 XCAR (pending_rest), 0))
831                 {
832                   found = 1;
833                   break;
834                 }
835             }
836           if (!found)
837             break;
838         }
839     }
840   return found;
841 }
842
843 /* Set the properties of a progress gauge */
844 static void
845 progress_gauge_update (Lisp_Object image_instance,
846                        Lisp_Object instantiator)
847 {
848   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
849   Lisp_Object value = find_keyword_in_vector (instantiator, Q_value);
850
851   if (!NILP (value))
852     {
853       CHECK_INT (value);
854 #ifdef DEBUG_WIDGET_OUTPUT
855       stderr_out ("progress gauge value set to %ld\n", XINT (value));
856 #endif
857       IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii) =
858         copy_gui_item_tree (IMAGE_INSTANCE_WIDGET_ITEMS (ii));
859 #ifdef ERROR_CHECK_GLYPHS
860       assert (GUI_ITEMP (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)));
861 #endif
862       if (GUI_ITEMP (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii)))
863         XGUI_ITEM (IMAGE_INSTANCE_WIDGET_PENDING_ITEMS (ii))->value = value;
864
865       IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (ii) = 1;
866     }
867 }
868
869 \f
870 /*****************************************************************************
871  *                              widget layout                               *
872  *****************************************************************************/
873 /* We need to cascade normalization.*/
874 static Lisp_Object
875 layout_normalize (Lisp_Object inst, Lisp_Object console_type,
876                   Lisp_Object dest_mask)
877 {
878   /* This function can call lisp */
879   struct gcpro gcpro1, gcpro2;
880   Lisp_Object alist = Qnil, new_items = Qnil, border;
881   /* This function can call lisp */
882   Lisp_Object items;
883
884   GCPRO2 (alist, new_items);
885   alist = tagged_vector_to_alist (inst);
886   items = assq_no_quit (Q_items, alist);
887
888   /* We need to normalize sub-objects. */
889   if (!NILP (items))
890     {
891       Lisp_Object rest;
892       LIST_LOOP (rest, XCDR (items))
893         {
894           /* Substitute the new instantiator */
895           new_items = Fcons (normalize_image_instantiator (XCAR (rest),
896                                                            console_type, dest_mask),
897                              new_items);
898         }
899       new_items = Fnreverse (new_items);
900       Fsetcdr (items, new_items);
901     }
902   /* Normalize the border spec. */
903   border = assq_no_quit (Q_border, alist);
904   if (!NILP (border) && VECTORP (XCDR (border)))
905     {
906       Fsetcdr (border, normalize_image_instantiator (XCDR (border),
907                                                      console_type, dest_mask));
908     }
909
910   {
911     Lisp_Object result = alist_to_tagged_vector (XVECTOR_DATA (inst)[0],
912                                                  alist);
913     free_alist (alist);
914     RETURN_UNGCPRO (result);
915   }
916 }
917
918 /* Update the instances in the layout. */
919 static void
920 layout_update (Lisp_Object image_instance, Lisp_Object instantiator)
921 {
922   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
923   Lisp_Object items = find_keyword_in_vector (instantiator, Q_items);
924   Lisp_Object border_inst = find_keyword_in_vector (instantiator, Q_border);
925   Lisp_Object border = Qnil;
926   Lisp_Object children = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii);
927   int structure_changed = 0;
928   struct gcpro gcpro1;
929
930   /* We want to avoid consing if we can. This is quite awkward because
931      we have to deal with the border as well as the items. */
932
933   GCPRO1 (border);
934
935   if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
936     {
937       border = XCAR (children);
938       children = XCDR (children);
939     }
940
941 #ifdef DEBUG_WIDGET_OUTPUT
942   stderr_out ("layout updated\n");
943 #endif
944   /* Update the border. */
945   if (!NILP (border_inst))
946     {
947       if (VECTORP (border_inst))
948         {
949           /* We are going to be sneaky here and add the border text as
950              just another child, the layout and output routines don't know
951              this and will just display at the offsets we prescribe. */
952           if (!NILP (border))
953             call3 (Qset_glyph_image, border, border_inst,
954                    IMAGE_INSTANCE_DOMAIN (ii));
955           else
956             {
957               border = Fcons (call1 (Qmake_glyph, border_inst), Qnil);
958               structure_changed = 1;
959             }
960           IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (0);
961         }
962       else
963         {
964           if (!NILP (border))
965             {
966               border = Qnil;
967               structure_changed = 1;
968             }
969           if (EQ (border_inst, Qt))
970               IMAGE_INSTANCE_LAYOUT_BORDER (ii) = Qetched_in;
971           else
972             IMAGE_INSTANCE_LAYOUT_BORDER (ii) = border_inst;
973         }
974     }
975
976   /* Pick up the sub-widgets. */
977   if (!NILP (items))
978     {
979       int len1, len2;
980       GET_LIST_LENGTH (items, len1);
981       GET_LIST_LENGTH (children, len2);
982       /* The structure hasn't changed so just update the images. */
983       if (!structure_changed && len1 == len2)
984         {
985           /* Pick up the sub-widgets. */
986           for (; !NILP (children); children = XCDR (children), items = XCDR (items))
987             {
988               call3 (Qset_glyph_image, XCAR (children), XCAR (items),
989                      IMAGE_INSTANCE_DOMAIN (ii));
990             }
991         }
992       /* The structure has changed so start over. */
993       else
994         {
995           /* Instantiate any new glyphs. */
996           for (; !NILP (items); items = XCDR (items))
997             {
998               border = Fcons (call1 (Qmake_glyph, XCAR (items)), border);
999             }
1000           IMAGE_INSTANCE_LAYOUT_CHILDREN (ii) = Fnreverse (border);
1001         }
1002     }
1003   UNGCPRO;
1004 }
1005
1006 static void
1007 layout_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
1008                     Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1009                     int dest_mask, Lisp_Object domain)
1010 {
1011   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1012   Lisp_Object orient = find_keyword_in_vector (instantiator, Q_orientation);
1013
1014 #ifdef DEBUG_WIDGET_OUTPUT
1015   stderr_out ("layout instantiated\n");
1016 #endif
1017   /* Do widget type instantiation first. */
1018   widget_instantiate (image_instance, instantiator, pointer_fg, pointer_bg,
1019                       dest_mask, domain);
1020
1021   if (NILP (orient))
1022     {
1023       IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii) = LAYOUT_VERTICAL;
1024     }
1025
1026   /* Get child glyphs and finish instantiation. We can't do image
1027      instance children yet as we might not have a containing
1028      window. */
1029   layout_update (image_instance, instantiator);
1030 }
1031
1032 static void
1033 layout_post_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
1034                          Lisp_Object domain)
1035 {
1036 }
1037
1038 /* Layout widget. Sizing commentary: we have a number of problems that
1039    we would like to address. Some consider some of these more
1040    important than others. It used to be that size information was
1041    determined at instantiation time and was then fixed forever
1042    after. Generally this is not what we want. Users want size to be
1043    "big enough" to accommodate whatever they are trying to show and
1044    this is dependent on text length, lines, font metrics etc. Of
1045    course these attributes can change dynamically and so the size
1046    should changed dynamically also. Only in a few limited cases should
1047    the size be fixed and remain fixed. Of course this actually means
1048    that we don't really want to specify the size *at all* for most
1049    widgets - we want it to be discovered dynamically. Thus we can
1050    envisage the following scenarios:
1051
1052    1. A button is sized to accommodate its text, the text changes and the
1053    button should change size also.
1054
1055    2. A button is given an explicit size. Its size should never change.
1056
1057    3. Layout is put inside an area. The size of the area changes, the
1058    layout should change with it.
1059
1060    4. A button grows to accommodate additional text. The whitespace
1061    around it should be modified to cope with the new layout
1062    requirements.
1063
1064    5. A button grows. The area surrounding it should grow also if
1065    possible.
1066
1067    What metrics are important?
1068    1. Actual width and height.
1069
1070    2. Whether the width and height are what the widget actually wants, or
1071    whether it can grow or shrink.
1072
1073    Text glyphs are particularly troublesome since their metrics depend
1074    on the context in which they are being viewed. For instance they
1075    can appear differently depending on the window face, frame face or
1076    glyph face. In order to simplify this text glyphs can now only have
1077    a glyph-face or image-instance face. All other glyphs are
1078    essentially fixed in appearance. Perhaps the problem is that text
1079    glyphs are cached on a device basis like most other glyphs. Instead
1080    they should be cached per-window and then the instance would be
1081    fixed and we wouldn't have to mess around with font metrics and the
1082    rest. */
1083
1084 /* Query the geometry of a layout widget. We assume that we can only
1085    get here if the size is not already fixed. */
1086 static void
1087 layout_query_geometry (Lisp_Object image_instance, int* width,
1088                        int* height, enum image_instance_geometry disp,
1089                        Lisp_Object domain)
1090 {
1091   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1092   Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii), rest;
1093   int maxph = 0, maxpw = 0, nitems = 0, ph_adjust = 0;
1094   int gheight, gwidth;
1095
1096   /* If we are not initialized then we won't have any children. */
1097   if (!IMAGE_INSTANCE_INITIALIZED (ii))
1098       return;
1099
1100   /* First just set up what we already have. */
1101   if (width)    *width = IMAGE_INSTANCE_WIDTH (ii);
1102   if (height)   *height = IMAGE_INSTANCE_HEIGHT (ii);
1103
1104   /* If we are not allowed to dynamically size then return. */
1105   if (!IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP (ii)
1106       &&
1107       !IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP (ii))
1108     return;
1109
1110   /* Pick up the border text if we have one. */
1111   if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
1112     {
1113       glyph_query_geometry (XCAR (items), &gwidth, &gheight, disp,
1114                             image_instance);
1115       ph_adjust = gheight / 2;
1116       items = XCDR (items);
1117     }
1118
1119   /* Flip through the items to work out how much stuff we have to display */
1120   LIST_LOOP (rest, items)
1121     {
1122       Lisp_Object glyph = XCAR (rest);
1123       glyph_query_geometry (glyph, &gwidth, &gheight, disp, image_instance);
1124
1125       nitems ++;
1126       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1127           == LAYOUT_HORIZONTAL)
1128         {
1129           maxph = max (maxph, gheight);
1130           maxpw += gwidth;
1131         }
1132       else
1133         {
1134           maxpw = max (maxpw, gwidth);
1135           maxph += gheight;
1136         }
1137     }
1138
1139   /* Work out minimum space we need to fit all the items. This could
1140      have been fixed by the user. */
1141   if (!NILP (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii)))
1142     {
1143       Lisp_Object dynamic_width =
1144         Feval (IMAGE_INSTANCE_WIDGET_WIDTH_SUBR (ii));
1145       if (INTP (dynamic_width))
1146         *width = XINT (dynamic_width);
1147     }
1148   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1149            == LAYOUT_HORIZONTAL)
1150     *width = maxpw + ((nitems + 1) * WIDGET_BORDER_WIDTH +
1151                       IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2;
1152   else
1153     *width = maxpw + 2 * (WIDGET_BORDER_WIDTH * 2 +
1154                           IMAGE_INSTANCE_MARGIN_WIDTH (ii));
1155
1156   /* Work out vertical spacings. */
1157   if (!NILP (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii)))
1158     {
1159       Lisp_Object dynamic_height =
1160         Feval (IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR (ii));
1161       if (INTP (dynamic_height))
1162         *height = XINT (dynamic_height);
1163     }
1164   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1165            == LAYOUT_VERTICAL)
1166     *height = maxph + ((nitems + 1) * WIDGET_BORDER_HEIGHT +
1167                        IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
1168   else
1169     *height = maxph + (2 * WIDGET_BORDER_HEIGHT +
1170                        IMAGE_INSTANCE_MARGIN_WIDTH (ii)) * 2 + ph_adjust;
1171 }
1172
1173 int
1174 layout_layout (Lisp_Object image_instance,
1175                int width, int height, int xoffset, int yoffset,
1176                Lisp_Object domain)
1177 {
1178   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1179   Lisp_Object rest;
1180   Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN (ii);
1181   int x, y, maxph = 0, maxpw = 0, nitems = 0,
1182     horiz_spacing, vert_spacing, ph_adjust = 0;
1183   int gheight, gwidth;
1184
1185   /* If we are not initialized then we won't have any children. */
1186   if (!IMAGE_INSTANCE_INITIALIZED (ii))
1187       return 0;
1188
1189   /* Pick up the border text if we have one. */
1190   if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)))
1191     {
1192       Lisp_Object border = XCAR (items);
1193       items = XCDR (items);
1194       glyph_query_geometry (border, &gwidth, &gheight,
1195                             IMAGE_DESIRED_GEOMETRY, image_instance);
1196       ph_adjust = gheight / 2;
1197       IMAGE_INSTANCE_LAYOUT_BORDER (ii) = make_int (ph_adjust);
1198
1199       /* #### Really, what should this be? */
1200       glyph_do_layout (border, gwidth, gheight, 10, 0,
1201                        image_instance);
1202     }
1203
1204   /* Flip through the items to work out how much stuff we have to display. */
1205   LIST_LOOP (rest, items)
1206     {
1207       Lisp_Object glyph = XCAR (rest);
1208
1209       glyph_query_geometry (glyph, &gwidth, &gheight,
1210                             IMAGE_DESIRED_GEOMETRY, image_instance);
1211       nitems ++;
1212       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1213           == LAYOUT_HORIZONTAL)
1214         {
1215           maxph = max (maxph, gheight);
1216           maxpw += gwidth;
1217         }
1218       else
1219         {
1220           maxpw = max (maxpw, gwidth);
1221           maxph += gheight;
1222         }
1223     }
1224
1225   /* work out spacing between items and bounds of the layout */
1226   if (width < maxpw)
1227     /* The user wants a smaller space than the largest item, so we
1228        just provide default spacing and will let the output routines
1229        clip.. */
1230     horiz_spacing = WIDGET_BORDER_WIDTH * 2;
1231   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1232            == LAYOUT_HORIZONTAL)
1233     /* We have a larger area to display in so distribute the space
1234        evenly. */
1235     horiz_spacing = (width - (maxpw +
1236                               IMAGE_INSTANCE_MARGIN_WIDTH (ii) * 2))
1237       / (nitems + 1);
1238   else
1239     horiz_spacing = (width - maxpw) / 2
1240       - IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1241
1242   if (height < maxph)
1243     vert_spacing = WIDGET_BORDER_HEIGHT * 2;
1244   else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1245            == LAYOUT_VERTICAL)
1246     vert_spacing = (height - (maxph + ph_adjust +
1247                               IMAGE_INSTANCE_MARGIN_WIDTH (ii) * 2))
1248       / (nitems + 1);
1249   else
1250     vert_spacing = (height - (maxph + ph_adjust)) / 2
1251       - IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1252
1253   y = vert_spacing + ph_adjust + IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1254   x = horiz_spacing + IMAGE_INSTANCE_MARGIN_WIDTH (ii);
1255
1256   /* Now flip through putting items where we want them, paying
1257      attention to justification. Make sure we don't mess with the
1258      border glyph. */
1259   LIST_LOOP (rest, items)
1260     {
1261       Lisp_Object glyph = XCAR (rest);
1262
1263       glyph_query_geometry (glyph, &gwidth, &gheight,
1264                             IMAGE_DESIRED_GEOMETRY, image_instance);
1265
1266       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1267           == LAYOUT_HORIZONTAL)
1268         {
1269           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1270               == LAYOUT_JUSTIFY_RIGHT)
1271             y = height - (gheight + vert_spacing);
1272           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1273               == LAYOUT_JUSTIFY_CENTER)
1274             y = (height - gheight) / 2;
1275         }
1276       else
1277         {
1278           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1279               == LAYOUT_JUSTIFY_RIGHT)
1280             x = width - (gwidth + horiz_spacing);
1281           if (IMAGE_INSTANCE_SUBWINDOW_JUSTIFY (ii)
1282               == LAYOUT_JUSTIFY_CENTER)
1283             x = (width - gwidth) / 2;
1284         }
1285
1286       /* Now layout subwidgets if they require it. */
1287       glyph_do_layout (glyph, gwidth, gheight, x, y, image_instance);
1288
1289       if (IMAGE_INSTANCE_SUBWINDOW_ORIENT (ii)
1290           == LAYOUT_HORIZONTAL)
1291         {
1292           x += (gwidth + horiz_spacing);
1293         }
1294       else
1295         {
1296           y += (gheight + vert_spacing);
1297         }
1298
1299     }
1300   return 1;
1301 }
1302
1303 /* Get the glyphs that comprise a layout. These are created internally
1304    and so are otherwise inaccessible to lisp. We need some way of getting
1305    properties from the widgets that comprise a layout and this is the
1306    simplest way of doing it.
1307
1308    #### Eventually we should allow some more intelligent access to
1309    sub-widgets. */
1310 static Lisp_Object
1311 layout_property (Lisp_Object image_instance, Lisp_Object prop)
1312 {
1313   /* This function can GC. */
1314   Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
1315   if (EQ (prop, Q_items))
1316     {
1317       if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (ii)) &&
1318           CONSP (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii)))
1319         return Fcopy_sequence (XCDR
1320                                (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii)));
1321       else
1322         return Fcopy_sequence (IMAGE_INSTANCE_LAYOUT_CHILDREN (ii));
1323     }
1324   return Qunbound;
1325 }
1326
1327 /* Layout subwindows if they are real subwindows. */
1328 static int
1329 native_layout_layout (Lisp_Object image_instance,
1330                       int width, int height, int xoffset, int yoffset,
1331                       Lisp_Object domain)
1332 {
1333   Lisp_Image_Instance* ii = XIMAGE_INSTANCE (image_instance);
1334   Lisp_Object rest;
1335
1336   /* The first time this gets called, the layout will be only
1337      partially instantiated. The children get done in
1338      post_instantiate. */
1339   if (!IMAGE_INSTANCE_INITIALIZED (ii))
1340     return 0;
1341
1342   /* Defining this overrides the default layout_layout so we first have to call that to get
1343      suitable instances and values set up. */
1344   layout_layout (image_instance, width, height, xoffset, yoffset, domain);
1345
1346   LIST_LOOP (rest, IMAGE_INSTANCE_LAYOUT_CHILDREN (ii))
1347     {
1348       struct display_glyph_area dga;
1349       dga.xoffset = 0;
1350       dga.yoffset = 0;
1351       dga.width = IMAGE_INSTANCE_WIDTH (ii);
1352       dga.height = IMAGE_INSTANCE_HEIGHT (ii);
1353
1354       map_subwindow (XCAR (rest),
1355                      IMAGE_INSTANCE_XOFFSET (ii),
1356                      IMAGE_INSTANCE_YOFFSET (ii), &dga);
1357     }
1358   return 1;
1359 }
1360
1361 \f
1362 /************************************************************************/
1363 /*                            initialization                            */
1364 /************************************************************************/
1365
1366 void
1367 syms_of_glyphs_widget (void)
1368 {
1369   DEFSYMBOL (Qetched_in);
1370   DEFSYMBOL (Qetched_out);
1371   DEFSYMBOL (Qbevel_in);
1372   DEFSYMBOL (Qbevel_out);
1373   DEFSYMBOL (Qmake_glyph);
1374 }
1375
1376 #define VALID_GUI_KEYWORDS(type) do {                                         \
1377   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_active, check_valid_anything);      \
1378   IIFORMAT_VALID_KEYWORD (type, Q_suffix, check_valid_anything);              \
1379   IIFORMAT_VALID_KEYWORD (type, Q_keys, check_valid_string);                  \
1380   IIFORMAT_VALID_KEYWORD (type, Q_style, check_valid_symbol);                 \
1381   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_selected, check_valid_anything);    \
1382   IIFORMAT_VALID_KEYWORD (type, Q_filter, check_valid_anything);              \
1383   IIFORMAT_VALID_KEYWORD (type, Q_config, check_valid_symbol);                \
1384   IIFORMAT_VALID_KEYWORD (type, Q_included, check_valid_anything);            \
1385   IIFORMAT_VALID_KEYWORD (type, Q_initial_focus, check_valid_anything);       \
1386   IIFORMAT_VALID_KEYWORD (type, Q_key_sequence, check_valid_string);          \
1387   IIFORMAT_VALID_KEYWORD (type, Q_accelerator, check_valid_string);           \
1388   IIFORMAT_VALID_KEYWORD (type, Q_label, check_valid_anything);               \
1389   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback, check_valid_callback);    \
1390   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback_ex, check_valid_callback); \
1391   IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_descriptor,                         \
1392                                   check_valid_string_or_vector);              \
1393 } while (0)
1394
1395 #define VALID_WIDGET_KEYWORDS(type) do {                                      \
1396   IIFORMAT_VALID_KEYWORD (type, Q_width, check_valid_int);                    \
1397   IIFORMAT_VALID_KEYWORD (type, Q_height, check_valid_int);                   \
1398   IIFORMAT_VALID_KEYWORD (type, Q_pixel_width, check_valid_int_or_function);  \
1399   IIFORMAT_VALID_KEYWORD (type, Q_pixel_height, check_valid_int_or_function); \
1400   IIFORMAT_VALID_KEYWORD (type, Q_face, check_valid_face);                    \
1401 } while (0)
1402
1403
1404 static void image_instantiator_widget (void)
1405 { /* we only do this for properties */
1406   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT_NO_SYM (widget, "widget");
1407   IIFORMAT_HAS_METHOD (widget, property);
1408   IIFORMAT_HAS_METHOD (widget, update);
1409   IIFORMAT_HAS_METHOD (widget, query_geometry);
1410   IIFORMAT_HAS_METHOD (widget, layout);
1411 }
1412
1413 static void image_instantiator_buttons (void)
1414 {
1415   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (button, "button");
1416   IIFORMAT_HAS_SHARED_METHOD (button, validate, widget);
1417   IIFORMAT_HAS_SHARED_METHOD (button, possible_dest_types, widget);
1418   IIFORMAT_HAS_SHARED_METHOD (button, instantiate, widget);
1419   IIFORMAT_HAS_SHARED_METHOD (button, post_instantiate, widget);
1420   IIFORMAT_HAS_SHARED_METHOD (button, normalize, widget);
1421   IIFORMAT_HAS_SHARED_METHOD (button, governing_domain, subwindow);
1422   IIFORMAT_HAS_METHOD (button, query_geometry);
1423   IIFORMAT_VALID_KEYWORD (button,
1424                           Q_image, check_valid_instantiator);
1425   VALID_WIDGET_KEYWORDS (button);
1426   VALID_GUI_KEYWORDS (button);
1427 }
1428
1429 static void image_instantiator_edit_fields (void)
1430 {
1431   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (edit_field, "edit-field");
1432   IIFORMAT_HAS_SHARED_METHOD (edit_field, validate, widget);
1433   IIFORMAT_HAS_SHARED_METHOD (edit_field, possible_dest_types, widget);
1434   IIFORMAT_HAS_SHARED_METHOD (edit_field, instantiate, widget);
1435   IIFORMAT_HAS_SHARED_METHOD (edit_field, post_instantiate, widget);
1436   IIFORMAT_HAS_SHARED_METHOD (edit_field, governing_domain, subwindow);
1437   VALID_WIDGET_KEYWORDS (edit_field);
1438   VALID_GUI_KEYWORDS (edit_field);
1439 }
1440
1441 static void image_instantiator_combo_box (void)
1442 {
1443   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (combo_box, "combo-box");
1444   IIFORMAT_HAS_METHOD (combo_box, validate);
1445   IIFORMAT_HAS_SHARED_METHOD (combo_box, possible_dest_types, widget);
1446   IIFORMAT_HAS_SHARED_METHOD (combo_box, governing_domain, subwindow);
1447
1448   VALID_GUI_KEYWORDS (combo_box);
1449
1450   IIFORMAT_VALID_KEYWORD (combo_box, Q_width, check_valid_int);
1451   IIFORMAT_VALID_KEYWORD (combo_box, Q_height, check_valid_int);
1452   IIFORMAT_VALID_KEYWORD (combo_box, Q_pixel_width,
1453                           check_valid_int_or_function);
1454   IIFORMAT_VALID_KEYWORD (combo_box, Q_face, check_valid_face);
1455   IIFORMAT_VALID_KEYWORD (combo_box, Q_items, check_valid_item_list);
1456 }
1457
1458 static void image_instantiator_scrollbar (void)
1459 {
1460   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (scrollbar, "scrollbar");
1461   IIFORMAT_HAS_SHARED_METHOD (scrollbar, validate, widget);
1462   IIFORMAT_HAS_SHARED_METHOD (scrollbar, possible_dest_types, widget);
1463   IIFORMAT_HAS_SHARED_METHOD (scrollbar, instantiate, widget);
1464   IIFORMAT_HAS_SHARED_METHOD (scrollbar, post_instantiate, widget);
1465   IIFORMAT_HAS_SHARED_METHOD (scrollbar, governing_domain, subwindow);
1466   VALID_GUI_KEYWORDS (scrollbar);
1467
1468   IIFORMAT_VALID_KEYWORD (scrollbar, Q_pixel_width,
1469                           check_valid_int_or_function);
1470   IIFORMAT_VALID_KEYWORD (scrollbar, Q_pixel_height,
1471                           check_valid_int_or_function);
1472   IIFORMAT_VALID_KEYWORD (scrollbar, Q_face, check_valid_face);
1473 }
1474
1475 static void image_instantiator_progress_guage (void)
1476 {
1477   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (progress_gauge, "progress-gauge");
1478   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, validate, widget);
1479   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, possible_dest_types, widget);
1480   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, instantiate, widget);
1481   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, post_instantiate, widget);
1482   IIFORMAT_HAS_SHARED_METHOD (progress_gauge, governing_domain, subwindow);
1483   IIFORMAT_HAS_METHOD (progress_gauge, update);
1484   VALID_WIDGET_KEYWORDS (progress_gauge);
1485   VALID_GUI_KEYWORDS (progress_gauge);
1486
1487   IIFORMAT_VALID_KEYWORD (progress_gauge, Q_value, check_valid_int);
1488 }
1489
1490 static void image_instantiator_tree_view (void)
1491 {
1492   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (tree_view, "tree-view");
1493   IIFORMAT_HAS_SHARED_METHOD (tree_view, validate, combo_box);
1494   IIFORMAT_HAS_SHARED_METHOD (tree_view, possible_dest_types, widget);
1495   IIFORMAT_HAS_SHARED_METHOD (tree_view, instantiate, widget);
1496   IIFORMAT_HAS_SHARED_METHOD (tree_view, post_instantiate, widget);
1497   IIFORMAT_HAS_SHARED_METHOD (tree_view, governing_domain, subwindow);
1498   IIFORMAT_HAS_SHARED_METHOD (tree_view, update, tab_control);
1499   IIFORMAT_HAS_METHOD (tree_view, query_geometry);
1500   VALID_WIDGET_KEYWORDS (tree_view);
1501   VALID_GUI_KEYWORDS (tree_view);
1502   IIFORMAT_VALID_KEYWORD (tree_view, Q_items, check_valid_item_list);
1503 }
1504
1505 static void image_instantiator_tab_control (void)
1506 {
1507   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (tab_control, "tab-control");
1508   IIFORMAT_HAS_SHARED_METHOD (tab_control, validate, combo_box);
1509   IIFORMAT_HAS_SHARED_METHOD (tab_control, possible_dest_types, widget);
1510   IIFORMAT_HAS_SHARED_METHOD (tab_control, instantiate, widget);
1511   IIFORMAT_HAS_SHARED_METHOD (tab_control, post_instantiate, widget);
1512   IIFORMAT_HAS_SHARED_METHOD (tab_control, governing_domain, subwindow);
1513   IIFORMAT_HAS_METHOD (tab_control, query_geometry);
1514   IIFORMAT_HAS_METHOD (tab_control, update);
1515   VALID_WIDGET_KEYWORDS (tab_control);
1516   VALID_GUI_KEYWORDS (tab_control);
1517   IIFORMAT_VALID_KEYWORD (tab_control, Q_orientation,
1518                           check_valid_tab_orientation);
1519   IIFORMAT_VALID_KEYWORD (tab_control, Q_items, check_valid_item_list);
1520 }
1521
1522 static void image_instantiator_labels (void)
1523 {
1524   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (label, "label");
1525   IIFORMAT_HAS_SHARED_METHOD (label, possible_dest_types, widget);
1526   IIFORMAT_HAS_SHARED_METHOD (label, instantiate, widget);
1527   IIFORMAT_HAS_SHARED_METHOD (label, post_instantiate, widget);
1528   IIFORMAT_HAS_SHARED_METHOD (label, governing_domain, subwindow);
1529   VALID_WIDGET_KEYWORDS (label);
1530   IIFORMAT_VALID_KEYWORD (label, Q_descriptor, check_valid_string);
1531 }
1532
1533 #define VALID_LAYOUT_KEYWORDS(layout)                                      \
1534   VALID_WIDGET_KEYWORDS (layout);                                          \
1535   IIFORMAT_VALID_KEYWORD (layout, Q_orientation, check_valid_orientation); \
1536   IIFORMAT_VALID_KEYWORD (layout, Q_justify, check_valid_justification);   \
1537   IIFORMAT_VALID_KEYWORD (layout, Q_border, check_valid_border);           \
1538   IIFORMAT_VALID_KEYWORD (layout, Q_margin_width, check_valid_int);        \
1539   IIFORMAT_VALID_KEYWORD (layout, Q_items,                                 \
1540                           check_valid_instantiator_list)
1541
1542 static void image_instantiator_layout (void)
1543 {
1544   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (layout, "layout");
1545   IIFORMAT_HAS_SHARED_METHOD (layout, possible_dest_types, widget);
1546   IIFORMAT_HAS_METHOD (layout, instantiate);
1547   IIFORMAT_HAS_METHOD (layout, post_instantiate);
1548   IIFORMAT_HAS_SHARED_METHOD (layout, governing_domain, subwindow);
1549   IIFORMAT_HAS_METHOD (layout, normalize);
1550   IIFORMAT_HAS_METHOD (layout, query_geometry);
1551   IIFORMAT_HAS_METHOD (layout, layout);
1552   IIFORMAT_HAS_METHOD (layout, update);
1553   IIFORMAT_HAS_METHOD (layout, property);
1554
1555   VALID_GUI_KEYWORDS (layout);
1556   VALID_LAYOUT_KEYWORDS (layout);
1557 }
1558
1559 static void image_instantiator_native_layout (void)
1560 {
1561   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (native_layout, "native-layout");
1562   IIFORMAT_HAS_SHARED_METHOD (native_layout, possible_dest_types, widget);
1563   IIFORMAT_HAS_SHARED_METHOD (native_layout, instantiate, layout);
1564   IIFORMAT_HAS_SHARED_METHOD (native_layout, post_instantiate, layout);
1565   IIFORMAT_HAS_METHOD (native_layout, layout);
1566   IIFORMAT_HAS_SHARED_METHOD (native_layout, governing_domain, subwindow);
1567   IIFORMAT_HAS_SHARED_METHOD (native_layout, normalize, layout);
1568   IIFORMAT_HAS_SHARED_METHOD (native_layout, query_geometry, layout);
1569   IIFORMAT_HAS_SHARED_METHOD (native_layout, layout, layout);
1570   IIFORMAT_HAS_SHARED_METHOD (native_layout, property, layout);
1571
1572   VALID_GUI_KEYWORDS (native_layout);
1573   VALID_LAYOUT_KEYWORDS (native_layout);
1574 }
1575
1576 void
1577 image_instantiator_format_create_glyphs_widget (void)
1578 {
1579   image_instantiator_widget();
1580   image_instantiator_buttons();
1581   image_instantiator_edit_fields();
1582   image_instantiator_combo_box();
1583   image_instantiator_scrollbar();
1584   image_instantiator_progress_guage();
1585   image_instantiator_tree_view();
1586   image_instantiator_tab_control();
1587   image_instantiator_labels();
1588   image_instantiator_layout();
1589   image_instantiator_native_layout();
1590 }
1591
1592 void
1593 reinit_vars_of_glyphs_widget (void)
1594 {
1595 #ifdef DEBUG_WIDGETS
1596   debug_widget_instances = 0;
1597 #endif
1598 }
1599
1600 void
1601 vars_of_glyphs_widget (void)
1602 {
1603   reinit_vars_of_glyphs_widget ();
1604 }