1 /* Widget-specific glyph objects.
2 Copyright (C) 1998, 1999, 2000, 2002 Andy Piper.
4 This file is part of SXEmacs
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Synched up with: Not in FSF. */
22 /* written by Andy Piper <andy@xemacs.org> */
39 DEFINE_IMAGE_INSTANTIATOR_FORMAT(button);
40 DEFINE_IMAGE_INSTANTIATOR_FORMAT(combo_box);
41 Lisp_Object Qcombo_box;
42 DEFINE_IMAGE_INSTANTIATOR_FORMAT(edit_field);
43 Lisp_Object Qedit_field;
44 DEFINE_IMAGE_INSTANTIATOR_FORMAT(scrollbar);
45 Lisp_Object Qscrollbar;
46 DEFINE_IMAGE_INSTANTIATOR_FORMAT(widget);
47 DEFINE_IMAGE_INSTANTIATOR_FORMAT(label);
49 DEFINE_IMAGE_INSTANTIATOR_FORMAT(progress_gauge);
50 Lisp_Object Qprogress_gauge;
51 DEFINE_IMAGE_INSTANTIATOR_FORMAT(tree_view);
52 Lisp_Object Qtree_view;
53 DEFINE_IMAGE_INSTANTIATOR_FORMAT(tab_control);
54 Lisp_Object Qtab_control;
55 DEFINE_IMAGE_INSTANTIATOR_FORMAT(layout);
57 DEFINE_IMAGE_INSTANTIATOR_FORMAT(native_layout);
58 Lisp_Object Qnative_layout;
60 Lisp_Object Qetched_in, Qetched_out, Qbevel_in, Qbevel_out;
61 Lisp_Object Qmake_glyph;
62 Lisp_Object Vwidget_border_width;
64 static int widget_border_width(Lisp_Object domain);
65 static int widget_spacing(Lisp_Object domain);
66 #define BORDER_FIDDLE_FACTOR 10
68 int debug_widget_instances;
72 - tooltips for controls, especially buttons.
74 - lisp configurable layout.
77 /* In MS-Windows normal windows work in pixels, dialog boxes work in
78 dialog box units. Why? sigh. We could reuse the metrics for dialogs
79 if this were not the case. As it is we have to position things
80 pixel wise. I'm not even sure that X has this problem at least for
82 static int widget_possible_dest_types(void)
84 return IMAGE_WIDGET_MASK;
87 static void check_valid_instantiator(Lisp_Object data)
89 Lisp_Object glyph = data;
91 glyph = XSYMBOL(data)->value;
93 if (!CONSP(glyph) && !VECTORP(glyph))
94 invalid_argument("instantiator item must be a vector", data);
97 static void check_valid_orientation(Lisp_Object data)
99 if (!EQ(data, Qhorizontal)
100 && !EQ(data, Qvertical))
101 invalid_argument("unknown orientation for layout", data);
104 static void check_valid_tab_orientation(Lisp_Object data)
107 && !EQ(data, Qbottom)
109 && !EQ(data, Qright))
110 invalid_argument("unknown orientation for tab control", data);
113 static void check_valid_justification(Lisp_Object data)
118 && !EQ(data, Qbottom)
119 && !EQ(data, Qcenter))
120 invalid_argument("unknown justification for layout", data);
123 static void check_valid_border(Lisp_Object data)
125 if (!EQ(data, Qt) && !EQ(data, Qetched_in) && !EQ(data, Qetched_out)
126 && !EQ(data, Qbevel_in) && !EQ(data, Qbevel_out)
127 && !GLYPHP(data) && !VECTORP(data))
128 invalid_argument("unknown border style for layout", data);
131 static void check_valid_anything(Lisp_Object data)
135 static void check_valid_callback(Lisp_Object data)
138 && !COMPILED_FUNCTIONP(data)
140 invalid_argument(":callback must be a function or expression",
145 static void check_valid_int_or_function(Lisp_Object data)
147 if (!INTP(data) && !CONSP(data) && !SYMBOLP(data))
148 invalid_argument("must be an integer or expresssion", data);
151 static void check_valid_symbol(Lisp_Object data)
156 static void check_valid_string_or_vector(Lisp_Object data)
158 if (!STRINGP(data) && !VECTORP(data))
159 invalid_argument(":descriptor must be a string or a vector",
163 void check_valid_item_list(Lisp_Object items)
168 EXTERNAL_LIST_LOOP(rest, items) {
169 if (STRINGP(XCAR(rest)))
170 CHECK_STRING(XCAR(rest));
171 else if (VECTORP(XCAR(rest)))
172 gui_parse_item_keywords(XCAR(rest));
173 else if (LISTP(XCAR(rest)))
174 check_valid_item_list(XCAR(rest));
177 ("Items must be vectors, lists or strings", items);
181 static void check_valid_instantiator_list(Lisp_Object data)
186 EXTERNAL_LIST_LOOP(rest, data) {
187 check_valid_instantiator(XCAR(rest));
191 static Lisp_Object glyph_instantiator_to_glyph(Lisp_Object sym)
193 /* This function calls lisp. */
194 Lisp_Object glyph = sym;
198 /* if we have a symbol get at the actual data */
200 glyph = XSYMBOL(glyph)->value;
203 glyph = Feval(glyph);
205 /* Be really helpful to the user. */
206 if (VECTORP(glyph)) {
207 glyph = call1(Qmake_glyph, glyph);
210 /* substitute the new glyph */
211 RETURN_UNGCPRO(glyph);
215 substitute_keyword_value(Lisp_Object inst, Lisp_Object key, Lisp_Object val)
218 /* substitute the new glyph */
219 for (i = 0; i < XVECTOR_LENGTH(inst); i++) {
220 if (EQ(key, XVECTOR_DATA(inst)[i])) {
221 XVECTOR_DATA(inst)[i + 1] = val;
227 /* Determine the border with of the widget. */
228 static int widget_border_width(Lisp_Object domain)
230 /* #### FIXME -- need to use specifiers (Vwidget_border_width) for
231 some portion of this. */
232 if (HAS_DEVMETH_P(DOMAIN_XDEVICE(domain), widget_border_width))
233 return DEVMETH(DOMAIN_XDEVICE(domain), widget_border_width, ());
235 return DEFAULT_WIDGET_BORDER_WIDTH;
238 static int widget_instance_border_width(Lisp_Image_Instance * ii)
240 return widget_border_width(IMAGE_INSTANCE_DOMAIN(ii));
243 /* #### Its not clear to me what the value of logical_unit_height should
244 be, or whether it should even depend on the current
245 image_instance. It really should probably only depend on the
246 default widget face and the domain, however you can envisage users
247 wanting different logical units for nested layouts - so using the
248 properties of the current lahyout is probably not so dumb. */
250 logical_unit_height(Lisp_Object text, Lisp_Object face, Lisp_Object domain)
253 query_string_geometry(text, face, 0, &charheight, 0, domain);
254 /* For the returned value to be useful it needs to be big enough to
255 accomodate the largest single-height widget. This is currently
257 return charheight + 2 * widget_spacing(domain)
258 + 4 * widget_border_width(domain);
261 static int widget_logical_unit_height(Lisp_Image_Instance * ii)
263 return logical_unit_height(NILP(IMAGE_INSTANCE_WIDGET_TEXT(ii)) ?
264 NILP(IMAGE_INSTANCE_NAME(ii)) ?
265 Fsymbol_name(Qwidget)
266 : IMAGE_INSTANCE_NAME(ii)
267 : IMAGE_INSTANCE_WIDGET_TEXT(ii),
268 IMAGE_INSTANCE_WIDGET_FACE(ii),
269 IMAGE_INSTANCE_DOMAIN(ii));
272 /* Wire widget property invocations to specific widgets. The problem
273 we are solving here is that when instantiators get converted to
274 instances they lose some type information (they just become
275 subwindows or widgets for example). For widgets we need to preserve
276 this type information so that we can do widget specific operations
277 on the instances. This is encoded in the widget type
278 field. widget_property gets invoked by decoding the primary type
279 (Qwidget), <widget>_property then invokes based on the secondary
280 type (Qedit_field for example). It is debatable whether we should
281 wire things in this generalised way rather than treating widgets
282 specially in image_instance_property. */
283 static Lisp_Object widget_property(Lisp_Object image_instance, Lisp_Object prop)
285 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
286 struct image_instantiator_methods *meths;
287 #if 0 /* The usefulness of this is dubious. */
288 /* first see if its a general property ... */
289 if (!NILP(Fplist_member(IMAGE_INSTANCE_WIDGET_PROPS(ii), prop)))
290 return Fplist_get(IMAGE_INSTANCE_WIDGET_PROPS(ii), prop, Qnil);
292 /* .. then try device specific methods ... */
293 meths = decode_device_ii_format(image_instance_device(image_instance),
294 IMAGE_INSTANCE_WIDGET_TYPE(ii),
296 if (meths && HAS_IIFORMAT_METH_P(meths, property))
297 return IIFORMAT_METH(meths, property, (image_instance, prop));
298 /* ... then format specific methods ... */
299 meths = decode_device_ii_format(Qnil, IMAGE_INSTANCE_WIDGET_TYPE(ii),
301 if (meths && HAS_IIFORMAT_METH_P(meths, property))
302 return IIFORMAT_METH(meths, property, (image_instance, prop));
307 /* Update the displayed properties of a widget.
309 #### This has been adapted from the original set_property functions
310 and thus reuses the state management of that. A better solution is
311 to simply re-parse the instantiator when items need updating. This
312 make comparing differences much simpler and obviates the need for a
313 lot of the state variables.
315 #### property is still a valid function since we have to be able to
316 extract information from the actual widget.
318 #### update_widget should probably be re-written to use the
319 instantiator. We probably want to keep a record of the differences
320 also to make this easy. We would also need a pending_instantiator
321 so that changes could be delayed. */
322 static void widget_update(Lisp_Object image_instance, Lisp_Object instantiator)
324 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
325 struct image_instantiator_methods *meths;
328 Lisp_Object text = find_keyword_in_vector(instantiator, Q_text);
329 Lisp_Object desc = find_keyword_in_vector(instantiator, Q_descriptor);
330 Lisp_Object items = find_keyword_in_vector(instantiator, Q_items);
331 Lisp_Object descriptor_item = Qnil;
333 GCPRO1(descriptor_item);
335 /* Pick up any generic properties that we might need to keep hold
337 #### This is potentially bogus because it is changing the items
338 in place rather than in the pending items. */
340 IMAGE_INSTANCE_WIDGET_TEXT(ii) = text;
341 IMAGE_INSTANCE_TEXT_CHANGED(ii) = 1;
344 /* Retrieve the gui item information. This is easy if we have been
345 provided with a vector, more difficult if we have just been given
348 #### This is inconsistent with instantiation in that you have to
349 have the :descriptor keyword for updates in order to recognise
352 descriptor_item = gui_parse_item_keywords_no_errors(desc);
354 /* Since we are updating the instantiator could be incomplete
355 and hence the gui item descriptor not well formed. We
356 therefore try updating and discard the results if nothing
358 descriptor_item = copy_gui_item(IMAGE_INSTANCE_WIDGET_ITEM(ii));
359 if (!update_gui_item_keywords(descriptor_item, instantiator))
360 descriptor_item = Qnil;
363 /* Record new items for update. *_redisplay will do the
365 if (!EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qlayout)
366 && !EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qnative_layout)) {
368 if (NILP(descriptor_item))
370 IMAGE_INSTANCE_WIDGET_ITEM(ii);
372 check_valid_item_list(items);
373 #ifdef DEBUG_WIDGET_OUTPUT
374 stderr_out("items for widget %p updated\n",
375 IMAGE_INSTANCE_SUBWINDOW_ID(ii));
377 /* Don't set the actual items since we might decide not to use
378 the new ones (because nothing has really changed). If we did
379 set them and didn't use them then we would get into whole
380 heaps of trouble when the old items get GC'd. */
382 Fcons(descriptor_item,
383 parse_gui_item_tree_children(items));
385 /* If the descriptor was updated but not the items we need to fill
386 in the `new' items. */
387 else if (!NILP(descriptor_item)
388 && CONSP(IMAGE_INSTANCE_WIDGET_ITEMS(ii))) {
389 descriptor_item = Fcons
391 copy_gui_item_tree(XCDR
392 (IMAGE_INSTANCE_WIDGET_ITEMS
397 if (!NILP(descriptor_item)) {
398 IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii) = descriptor_item;
399 IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii) = 1;
404 /* Now try device specific methods first ... */
405 meths = decode_device_ii_format(image_instance_device(image_instance),
406 IMAGE_INSTANCE_WIDGET_TYPE(ii),
408 MAYBE_IIFORMAT_METH(meths, update, (image_instance, instantiator));
409 /* ... then format specific methods ... */
410 meths = decode_device_ii_format(Qnil, IMAGE_INSTANCE_WIDGET_TYPE(ii),
412 MAYBE_IIFORMAT_METH(meths, update, (image_instance, instantiator));
413 #if 0 /* The usefulness of this is dubious. */
414 /* we didn't do any device specific properties, so shove the property in our plist. */
415 IMAGE_INSTANCE_WIDGET_PROPS(ii)
416 = Fplist_put(IMAGE_INSTANCE_WIDGET_PROPS(ii), prop, val);
420 /* Like the rest of redisplay, we want widget updates to occur
421 asynchronously. Thus toolkit specific methods for setting
422 properties must be called by redisplay instead of by *_update. Thus
423 *_update records the change and this function actually implements
424 it. We want to be slightly clever about this however by supplying
425 format specific functions for the updates instead of lumping them
426 all into this function. Note that there is no need for format
427 generic functions. This is not the same as widget_update! */
428 void redisplay_widget(Lisp_Object widget)
430 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(widget);
431 struct image_instantiator_methods *meths;
433 if (!WIDGET_IMAGE_INSTANCEP(widget)
434 || EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qlayout)
435 || EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qnative_layout))
438 /* Device-format specific methods - e.g. x_tab_control_redisplay () */
439 meths = decode_device_ii_format(image_instance_device(widget),
440 IMAGE_INSTANCE_WIDGET_TYPE(ii),
442 MAYBE_IIFORMAT_METH(meths, redisplay, (widget));
444 /* Device generic methods - e.g. x_redisplay_widget (). We must
445 update the widget's size as it may have been changed by the the
446 layout routines. We also do this here so that explicit resizing
447 from lisp does not result in synchronous updates. Do this last so
448 that format-specific methods have an opportunity to prevent
449 wholesale changes - e.g. rebuilding tabs. */
450 MAYBE_DEVMETH(DOMAIN_XDEVICE(IMAGE_INSTANCE_DOMAIN(ii)),
451 redisplay_widget, (ii));
453 /* Pick up the items we recorded earlier. */
454 if (IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii)) {
455 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
456 IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii);
457 IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii) = Qnil;
461 /* Determine the spacing of the widget. */
462 static int widget_spacing(Lisp_Object domain)
464 if (HAS_DEVMETH_P(DOMAIN_XDEVICE(domain), widget_spacing))
465 return DEVMETH(DOMAIN_XDEVICE(domain), widget_spacing, (0));
467 return DEFAULT_WIDGET_SPACING;
470 /* Query for a widgets desired geometry. If no type specific method is
471 provided then use the widget text to calculate sizes. */
473 widget_query_geometry(Lisp_Object image_instance,
474 int *width, int *height,
475 enum image_instance_geometry disp, Lisp_Object domain)
477 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
478 struct image_instantiator_methods *meths;
479 Lisp_Object dynamic_width = Qnil;
480 Lisp_Object dynamic_height = Qnil;
482 /* First just set up what we already have. */
484 *width = IMAGE_INSTANCE_WIDTH(ii);
486 *height = IMAGE_INSTANCE_HEIGHT(ii);
488 if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii)
489 || IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii)) {
490 /* .. then try device specific methods ... */
492 decode_device_ii_format(image_instance_device
494 IMAGE_INSTANCE_WIDGET_TYPE(ii),
496 if (meths && HAS_IIFORMAT_METH_P(meths, query_geometry))
497 IIFORMAT_METH(meths, query_geometry, (image_instance,
501 /* ... then format specific methods ... */
503 decode_device_ii_format(Qnil,
504 IMAGE_INSTANCE_WIDGET_TYPE
506 if (meths && HAS_IIFORMAT_METH_P(meths, query_geometry))
507 IIFORMAT_METH(meths, query_geometry,
508 (image_instance, width, height,
513 /* Then if we are allowed to resize the widget, make the
514 size the same as the text dimensions. */
515 query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT
517 IMAGE_INSTANCE_WIDGET_FACE
518 (ii), &w, &h, 0, domain);
519 /* Adjust the size for borders. */
520 if (width && IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii))
524 widget_instance_border_width(ii);
525 if (height && IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii))
529 widget_instance_border_width(ii);
532 /* Finish off with dynamic sizing. */
533 if (!NILP(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii))) {
535 Feval(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii));
536 if (INTP(dynamic_width) && width )
537 *width = XINT(dynamic_width);
539 if (!NILP(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii))) {
541 Feval(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii));
542 if (INTP(dynamic_height) && height)
543 *height = XINT(dynamic_height);
549 widget_layout(Lisp_Object image_instance,
550 int width, int height, int xoffset, int yoffset,
553 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
554 struct image_instantiator_methods *meths;
556 /* .. then try device specific methods ... */
557 meths = decode_device_ii_format(image_instance_device(image_instance),
558 IMAGE_INSTANCE_WIDGET_TYPE(ii),
560 if (meths && HAS_IIFORMAT_METH_P(meths, layout))
561 return IIFORMAT_METH(meths, layout, (image_instance,
562 width, height, xoffset,
565 /* ... then format specific methods ... */
567 decode_device_ii_format(Qnil,
568 IMAGE_INSTANCE_WIDGET_TYPE(ii),
570 if (meths && HAS_IIFORMAT_METH_P(meths, layout))
571 return IIFORMAT_METH(meths, layout, (image_instance,
579 static void widget_validate(Lisp_Object instantiator)
581 Lisp_Object desc = find_keyword_in_vector(instantiator, Q_descriptor);
584 syntax_error("Must supply :descriptor", instantiator);
587 gui_parse_item_keywords(desc);
589 if (!NILP(find_keyword_in_vector(instantiator, Q_width))
590 && !NILP(find_keyword_in_vector(instantiator, Q_pixel_width)))
591 syntax_error("Must supply only one of :width and :pixel-width",
594 if (!NILP(find_keyword_in_vector(instantiator, Q_height))
595 && !NILP(find_keyword_in_vector(instantiator, Q_pixel_height)))
597 ("Must supply only one of :height and :pixel-height",
601 static void combo_box_validate(Lisp_Object instantiator)
603 widget_validate(instantiator);
604 if (NILP(find_keyword_in_vector(instantiator, Q_items)))
605 syntax_error("Must supply item list", instantiator);
608 /* we need to convert things like glyphs to images, eval expressions
611 widget_normalize(Lisp_Object inst, Lisp_Object console_type,
612 Lisp_Object dest_mask)
614 /* This function can call lisp */
615 Lisp_Object glyph = find_keyword_in_vector(inst, Q_image);
617 /* we need to eval glyph if its an expression, we do this for the
618 same reasons we normalize file to data.
620 #### should just normalize the data. */
622 substitute_keyword_value(inst, Q_image,
623 glyph_instantiator_to_glyph(glyph));
630 initialize_widget_image_instance(Lisp_Image_Instance * ii, Lisp_Object type)
632 /* initialize_subwindow_image_instance (ii); */
633 IMAGE_INSTANCE_WIDGET_TYPE(ii) = type;
634 IMAGE_INSTANCE_WIDGET_PROPS(ii) = Qnil;
635 SET_IMAGE_INSTANCE_WIDGET_FACE(ii, Qnil);
636 IMAGE_INSTANCE_WIDGET_ITEMS(ii) = allocate_gui_item();
637 IMAGE_INSTANCE_LAYOUT_CHILDREN(ii) = Qnil;
638 IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii) = Qnil;
639 IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii) = Qnil;
640 IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii) = Qnil;
641 IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 1;
642 IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 1;
643 IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) = LAYOUT_HORIZONTAL;
644 IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) = 0;
645 IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) = 0;
648 /* Instantiate a button widget. Unfortunately instantiated widgets are
649 particular to a frame since they need to have a parent. It's not
650 like images where you just select the image into the context you
651 want to display it in and BitBlt it. So image instances can have a
652 many-to-one relationship with things you see, whereas widgets can
653 only be one-to-one (i.e. per frame) */
655 widget_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
656 Lisp_Object pointer_fg, Lisp_Object pointer_bg,
657 int dest_mask, Lisp_Object domain)
659 /* #### practically all of this should be moved to widget_update()
660 so that users can dynamically change all possible widget
662 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
663 Lisp_Object face = find_keyword_in_vector(instantiator, Q_face);
664 Lisp_Object height = find_keyword_in_vector(instantiator, Q_height);
665 Lisp_Object width = find_keyword_in_vector(instantiator, Q_width);
666 Lisp_Object pixwidth =
667 find_keyword_in_vector(instantiator, Q_pixel_width);
668 Lisp_Object pixheight =
669 find_keyword_in_vector(instantiator, Q_pixel_height);
670 Lisp_Object desc = find_keyword_in_vector(instantiator, Q_descriptor);
671 Lisp_Object glyph = find_keyword_in_vector(instantiator, Q_image);
672 Lisp_Object items = find_keyword_in_vector(instantiator, Q_items);
674 find_keyword_in_vector(instantiator, Q_orientation);
676 find_keyword_in_vector(instantiator, Q_margin_width);
678 find_keyword_in_vector(instantiator, Q_initial_focus);
679 int pw = 0, ph = 0, tw = 0, th = 0;
681 /* this just does pixel type sizing */
682 subwindow_instantiate(image_instance, instantiator, pointer_fg,
683 pointer_bg, dest_mask, domain);
685 if (!(dest_mask & IMAGE_WIDGET_MASK))
686 incompatible_image_types(instantiator, dest_mask,
689 initialize_widget_image_instance(ii, XVECTOR_DATA(instantiator)[0]);
691 IMAGE_INSTANCE_TYPE(ii) = IMAGE_WIDGET;
693 /* retrieve the fg and bg colors */
695 SET_IMAGE_INSTANCE_WIDGET_FACE(ii, Fget_face(face));
697 /* Retrieve the gui item information. This is easy if we have been
698 provided with a vector, more difficult if we have just been given
699 keywords. Note that standard gui descriptor shortcuts will not work
700 because of keyword parsing.
702 #### This is bogus in that descriptor and items share the same slot,
703 we should rationalize. */
705 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
706 gui_parse_item_keywords_no_errors(desc);
708 /* big cheat - we rely on the fact that a gui item looks like an instantiator */
709 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
710 widget_gui_parse_item_keywords(instantiator);
713 /* Pick up the orientation before we do our first layout. */
714 if (EQ(orient, Qleft) || EQ(orient, Qright) || EQ(orient, Qvertical))
715 IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) = LAYOUT_VERTICAL;
717 /* parse more gui items out of the properties */
718 if (!NILP(items) && !EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qlayout)
719 && !EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qnative_layout)) {
720 IMAGE_INSTANCE_WIDGET_ITEMS(ii) =
721 Fcons(IMAGE_INSTANCE_WIDGET_ITEMS(ii),
722 parse_gui_item_tree_children(items));
725 /* Normalize size information. We now only assign sizes if the user
726 gives us some explicitly, or there are some constraints that we
727 can't change later on. Otherwise we postpone sizing until query
728 geometry gets called. */
729 if (!NILP(pixwidth)) { /* pixwidth takes precendent */
731 IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii) = pixwidth;
734 IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 0;
736 } else if (!NILP(width)) {
738 IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 0;
741 if (!NILP(pixheight)) {
742 if (!INTP(pixheight))
743 IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii) = pixheight;
745 ph = XINT(pixheight);
746 IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 0;
748 } else if (!NILP(height) && XINT(height) > 1) {
750 IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 0;
753 /* Taking the default face information when the user has specified
754 size in characters is probably as good as any since the widget
755 face is more likely to be proportional and thus give inadequate
756 results. Using character sizes can only ever be approximate
757 anyway. :height is measured in logical characters which take into
758 account the borders and spacing on widgets. */
761 default_face_font_info(domain, 0, 0, 0, &charwidth, 0);
762 pw = ROUND_UP(charwidth * tw +
763 4 * widget_instance_border_width(ii), charwidth);
766 /* For heights the widget face is more appropriate. */
769 if (!NILP(IMAGE_INSTANCE_WIDGET_TEXT(ii))) {
770 query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
771 IMAGE_INSTANCE_WIDGET_FACE(ii),
772 0, &charheight, 0, domain);
774 default_face_font_info(domain, 0, 0, &charheight, 0, 0);
776 ph = (charheight + 2 * widget_instance_border_width(ii)) * th;
778 /* For heights > 1 use logical units. */
780 ph = widget_logical_unit_height(ii) * th;
783 /* for a widget with an image pick up the dimensions from that */
786 pw = glyph_width(glyph,
788 2 * widget_instance_border_width(ii);
790 ph = glyph_height(glyph,
792 2 * widget_instance_border_width(ii);
793 IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii) = 0;
794 IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii) = 0;
797 /* Pick up the margin width. */
799 IMAGE_INSTANCE_MARGIN_WIDTH(ii) = XINT(mwidth);
801 IMAGE_INSTANCE_WANTS_INITIAL_FOCUS(ii) = !NILP(ifocus);
803 /* Layout for the layout widget is premature at this point since the
804 children will not have been instantiated. We can't instantiate
805 them until the device instantiation method for the layout has
806 been executed. We do however want to record any specified
809 IMAGE_INSTANCE_WIDTH(ii) = pw;
811 IMAGE_INSTANCE_HEIGHT(ii) = ph;
815 widget_post_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
819 debug_widget_instances++;
820 stderr_out("instantiated ");
821 debug_print(instantiator);
822 stderr_out("%d widgets instantiated\n", debug_widget_instances);
826 /* Get the geometry of a button control. We need to adjust the size
827 depending on the type of button. */
829 button_query_geometry(Lisp_Object image_instance,
830 int *width, int *height,
831 enum image_instance_geometry disp, Lisp_Object domain)
833 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
835 query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
836 IMAGE_INSTANCE_WIDGET_FACE(ii),
838 /* Adjust the size for borders. */
839 if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii)) {
840 *width = w + 3 * widget_instance_border_width(ii);
842 if (EQ(XGUI_ITEM(IMAGE_INSTANCE_WIDGET_ITEM(ii))->style, Qradio)
844 EQ(XGUI_ITEM(IMAGE_INSTANCE_WIDGET_ITEM(ii))->style,
846 /* This is an approximation to the size of the actual button bit. */
849 if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii))
850 *height = h + 3 * widget_instance_border_width(ii);
853 /* Get the geometry of an edit field. */
855 edit_field_query_geometry(Lisp_Object image_instance,
856 int *width, int *height,
857 enum image_instance_geometry disp, Lisp_Object domain)
859 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
861 query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
862 IMAGE_INSTANCE_WIDGET_FACE(ii),
864 /* Adjust the size for borders. */
865 if (IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii))
866 *width = w + 4 * widget_instance_border_width(ii);
867 if (IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii))
868 *height = h + 4 * widget_instance_border_width(ii);
871 /* tree-view geometry - get the height right */
873 tree_view_query_geometry(Lisp_Object image_instance,
874 int *width, int *height,
875 enum image_instance_geometry disp, Lisp_Object domain)
877 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
878 Lisp_Object items = IMAGE_INSTANCE_WIDGET_ITEMS(ii);
881 /* #### what should this be. reconsider when X has tree views. */
882 query_string_geometry(IMAGE_INSTANCE_WIDGET_TEXT(ii),
883 IMAGE_INSTANCE_WIDGET_FACE(ii),
884 width, 0, 0, domain);
888 /* #### widget face would be better here. */
889 default_face_font_info(domain, 0, 0, &h, 0, 0);
890 GET_LIST_LENGTH(items, len);
895 /* Get the geometry of a tab control. This is based on the number of
896 items and text therin in the tab control. */
898 tab_control_query_geometry(Lisp_Object image_instance,
899 int *width, int *height,
900 enum image_instance_geometry disp,
903 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
904 Lisp_Object items = XCDR(IMAGE_INSTANCE_WIDGET_ITEMS(ii));
908 LIST_LOOP(rest, items) {
911 query_string_geometry(XGUI_ITEM(XCAR(rest))->name,
912 IMAGE_INSTANCE_WIDGET_FACE(ii),
914 tw += 5 * widget_instance_border_width(ii); /* some bias */
916 th = max(th, h + 2 * widget_instance_border_width(ii));
919 /* Fixup returned values depending on orientation. */
920 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)) {
933 /* Determine whether only the order has changed for a tab. */
934 int tab_control_order_only_changed(Lisp_Object image_instance)
936 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
937 int found = 0, len, pending_len;
940 /* Degenerate case. */
941 if (NILP(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)))
944 /* See whether we just need a change in order. */
945 GET_LIST_LENGTH(IMAGE_INSTANCE_WIDGET_ITEMS(ii), len);
946 GET_LIST_LENGTH(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii), pending_len);
947 if (len == pending_len) {
948 LIST_LOOP(rest, XCDR(IMAGE_INSTANCE_WIDGET_ITEMS(ii))) {
949 Lisp_Object pending_rest;
951 LIST_LOOP(pending_rest,
952 XCDR(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)))
954 if (gui_item_equal_sans_selected(XCAR(rest),
969 /*****************************************************************************
971 *****************************************************************************/
972 /* We need to cascade normalization.*/
974 layout_normalize(Lisp_Object inst, Lisp_Object console_type,
975 Lisp_Object dest_mask)
977 /* This function can call lisp */
978 struct gcpro gcpro1, gcpro2;
979 Lisp_Object alist = Qnil, new_items = Qnil, border;
980 /* This function can call lisp */
983 GCPRO2(alist, new_items);
984 alist = tagged_vector_to_alist(inst);
985 items = assq_no_quit(Q_items, alist);
987 /* We need to normalize sub-objects. */
990 LIST_LOOP(rest, XCDR(items)) {
991 /* Substitute the new instantiator */
993 Fcons(normalize_image_instantiator
994 (XCAR(rest), console_type, dest_mask),
997 new_items = Fnreverse(new_items);
998 Fsetcdr(items, new_items);
1000 /* Normalize the border spec. */
1001 border = assq_no_quit(Q_border, alist);
1002 if (!NILP(border) && VECTORP(XCDR(border))) {
1003 Fsetcdr(border, normalize_image_instantiator(XCDR(border),
1009 Lisp_Object result =
1010 alist_to_tagged_vector(XVECTOR_DATA(inst)[0],
1013 RETURN_UNGCPRO(result);
1017 /* Update the instances in the layout. */
1018 static void layout_update(Lisp_Object image_instance, Lisp_Object instantiator)
1020 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1021 Lisp_Object items = find_keyword_in_vector(instantiator, Q_items);
1022 Lisp_Object border_inst =
1023 find_keyword_in_vector(instantiator, Q_border);
1024 Lisp_Object justify = find_keyword_in_vector(instantiator, Q_justify);
1025 Lisp_Object hjustify =
1026 find_keyword_in_vector(instantiator, Q_horizontally_justify);
1027 Lisp_Object vjustify =
1028 find_keyword_in_vector(instantiator, Q_vertically_justify);
1029 Lisp_Object border = Qnil;
1030 Lisp_Object children = IMAGE_INSTANCE_LAYOUT_CHILDREN(ii);
1031 int structure_changed = 0;
1032 struct gcpro gcpro1;
1034 /* Pick up horizontal justification, left is the default. */
1035 if (!NILP(hjustify)) {
1036 if (EQ(hjustify, Qright) || EQ(hjustify, Qbottom))
1037 IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1038 LAYOUT_JUSTIFY_RIGHT;
1039 else if (EQ(hjustify, Qcenter))
1040 IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1041 LAYOUT_JUSTIFY_CENTER;
1043 /* If not set use general justification. */
1044 else if (!NILP(justify)) {
1045 if (EQ(justify, Qright) || EQ(justify, Qbottom))
1046 IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1047 LAYOUT_JUSTIFY_RIGHT;
1048 else if (EQ(justify, Qcenter))
1049 IMAGE_INSTANCE_SUBWINDOW_H_JUSTIFY(ii) =
1050 LAYOUT_JUSTIFY_CENTER;
1053 /* Pick up vertical justification, top is the default. */
1054 if (!NILP(vjustify)) {
1055 if (EQ(vjustify, Qright) || EQ(vjustify, Qbottom))
1056 IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1057 LAYOUT_JUSTIFY_BOTTOM;
1058 else if (EQ(vjustify, Qcenter))
1059 IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1060 LAYOUT_JUSTIFY_CENTER;
1062 /* If not set use general justification. */
1063 else if (!NILP(justify)) {
1064 if (EQ(justify, Qright) || EQ(justify, Qbottom))
1065 IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1066 LAYOUT_JUSTIFY_BOTTOM;
1067 else if (EQ(justify, Qcenter))
1068 IMAGE_INSTANCE_SUBWINDOW_V_JUSTIFY(ii) =
1069 LAYOUT_JUSTIFY_CENTER;
1072 /* We want to avoid consing if we can. This is quite awkward because
1073 we have to deal with the border as well as the items. */
1076 if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii))) {
1077 border = XCAR(children);
1078 children = XCDR(children);
1080 #ifdef DEBUG_WIDGET_OUTPUT
1081 stderr_out("layout updated\n");
1083 /* Update the border. */
1084 if (!NILP(border_inst)) {
1085 if (VECTORP(border_inst)) {
1086 /* We are going to be sneaky here and add the border text as
1087 just another child, the layout and output routines don't know
1088 this and will just display at the offsets we prescribe. */
1090 call3(Qset_glyph_image, border, border_inst,
1091 IMAGE_INSTANCE_DOMAIN(ii));
1094 Fcons(call1(Qmake_glyph, border_inst),
1096 structure_changed = 1;
1098 IMAGE_INSTANCE_LAYOUT_BORDER(ii) = make_int(0);
1100 if (!NILP(border)) {
1102 structure_changed = 1;
1104 if (EQ(border_inst, Qt))
1105 IMAGE_INSTANCE_LAYOUT_BORDER(ii) = Qetched_in;
1107 IMAGE_INSTANCE_LAYOUT_BORDER(ii) = border_inst;
1111 /* Pick up the sub-widgets. */
1114 GET_LIST_LENGTH(items, len1);
1115 GET_LIST_LENGTH(children, len2);
1116 /* The structure hasn't changed so just update the images. */
1117 if (!structure_changed && len1 == len2) {
1118 /* Pick up the sub-widgets. */
1119 for (; !NILP(children);
1120 children = XCDR(children), items = XCDR(items)) {
1121 call3(Qset_glyph_image, XCAR(children),
1122 XCAR(items), IMAGE_INSTANCE_DOMAIN(ii));
1125 /* The structure has changed so start over. */
1127 /* Instantiate any new glyphs. */
1128 for (; !NILP(items); items = XCDR(items)) {
1129 /* #### We really want to use call_with_suspended_errors
1130 here, but it won't allow us to call lisp. */
1132 Fcons(call1(Qmake_glyph, XCAR(items)),
1135 IMAGE_INSTANCE_LAYOUT_CHILDREN(ii) = Fnreverse(border);
1142 layout_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1143 Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1144 int dest_mask, Lisp_Object domain)
1146 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1147 Lisp_Object orient =
1148 find_keyword_in_vector(instantiator, Q_orientation);
1150 #ifdef DEBUG_WIDGET_OUTPUT
1151 stderr_out("layout instantiated\n");
1153 /* Do widget type instantiation first. */
1154 widget_instantiate(image_instance, instantiator, pointer_fg, pointer_bg,
1158 IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) = LAYOUT_VERTICAL;
1161 /* Get child glyphs and finish instantiation. We can't do image
1162 instance children yet as we might not have a containing
1164 layout_update(image_instance, instantiator);
1168 layout_post_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1173 /* Layout widget. Sizing commentary: we have a number of problems that
1174 we would like to address. Some consider some of these more
1175 important than others. It used to be that size information was
1176 determined at instantiation time and was then fixed forever
1177 after. Generally this is not what we want. Users want size to be
1178 "big enough" to accommodate whatever they are trying to show and
1179 this is dependent on text length, lines, font metrics etc. Of
1180 course these attributes can change dynamically and so the size
1181 should changed dynamically also. Only in a few limited cases should
1182 the size be fixed and remain fixed. Of course this actually means
1183 that we don't really want to specify the size *at all* for most
1184 widgets - we want it to be discovered dynamically. Thus we can
1185 envisage the following scenarios:
1187 1. A button is sized to accommodate its text, the text changes and the
1188 button should change size also.
1190 2. A button is given an explicit size. Its size should never change.
1192 3. Layout is put inside an area. The size of the area changes, the
1193 layout should change with it.
1195 4. A button grows to accommodate additional text. The whitespace
1196 around it should be modified to cope with the new layout
1199 5. A button grows. The area surrounding it should grow also if
1202 What metrics are important?
1203 1. Actual width and height.
1205 2. Whether the width and height are what the widget actually wants, or
1206 whether it can grow or shrink.
1208 Text glyphs are particularly troublesome since their metrics depend
1209 on the context in which they are being viewed. For instance they
1210 can appear differently depending on the window face, frame face or
1211 glyph face. In order to simplify this text glyphs can now only have
1212 a glyph-face or image-instance face. All other glyphs are
1213 essentially fixed in appearance. Perhaps the problem is that text
1214 glyphs are cached on a device basis like most other glyphs. Instead
1215 they should be cached per-window and then the instance would be
1216 fixed and we wouldn't have to mess around with font metrics and the
1219 Another sizing problem is alignment. We provide layout widgets that
1220 allow users to stack widgets vertically or horizontally. These
1221 layouts also allow the widgets to be centered (space evenly
1222 distributed), left or right justified (fixed spacing widgets
1223 stacked against the left, righ, top or bottom edge). Unfortunately
1224 this doesn't allow widgets in different layouts to be aligned. For
1225 instance how should the search dialog be organized for alignment?
1226 The obvious choice of two vertical columns does not work since the
1227 size of individual widgets will affect where they get placed. The
1228 same is true for several rows of widgets. To solve this problem we
1229 introduce the notion of `logical_unit_height'. This is a size
1230 quantity that is designed to be big enough to accomodate the
1231 largest `single height unit'. The function
1232 widget_logical_unit_height() determines the value of this in
1233 pixels. It is dependent on the widget face and some combination of
1234 spacing and border-width. Thus if users specify left or right
1235 justification in a vertical layout they get something in logical
1236 units. To simplify this the functions
1237 `widget-logical-to-character-height' and
1238 `widget-logical-to-character-width' allow conversion between
1239 characters and logical units so that frames can be sized
1242 /* Query the geometry of a layout widget. */
1244 layout_query_geometry(Lisp_Object image_instance, int *width,
1245 int *height, enum image_instance_geometry disp,
1248 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1249 Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN(ii), rest;
1250 int maxph = 0, maxpw = 0, nitems = 0, ph_adjust = 0;
1251 int gheight, gwidth, luh;
1253 /* If we are not initialized then we won't have any children. */
1254 if (!IMAGE_INSTANCE_INITIALIZED(ii))
1257 /* First just set up what we already have. */
1259 *width = IMAGE_INSTANCE_WIDTH(ii);
1261 *height = IMAGE_INSTANCE_HEIGHT(ii);
1263 /* If we are not allowed to dynamically size then return. */
1264 if (!IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii)
1265 && !IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii))
1268 luh = widget_logical_unit_height(ii);
1270 /* Pick up the border text if we have one. */
1271 if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii))) {
1272 glyph_query_geometry(XCAR(items), &gwidth, &gheight, disp,
1274 ph_adjust = gheight;
1275 /* Include text width in vertical layouts. */
1276 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_VERTICAL)
1277 maxpw = gwidth + BORDER_FIDDLE_FACTOR;
1278 items = XCDR(items);
1281 /* Flip through the items to work out how much stuff we have to display */
1282 LIST_LOOP(rest, items) {
1283 Lisp_Object glyph = XCAR(rest);
1284 glyph_query_geometry(glyph, &gwidth, &gheight, disp,
1288 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_HORIZONTAL) {
1289 maxph = max(maxph, gheight);
1292 maxpw = max(maxpw, gwidth);
1297 /* Work out minimum space we need to fit all the items. This could
1298 have been fixed by the user. */
1299 if (width && IMAGE_INSTANCE_SUBWINDOW_H_RESIZEP(ii)) {
1300 if (!NILP(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii))) {
1301 Lisp_Object dynamic_width =
1302 Feval(IMAGE_INSTANCE_WIDGET_WIDTH_SUBR(ii));
1303 if (INTP(dynamic_width))
1304 *width = XINT(dynamic_width);
1305 } else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) ==
1306 LAYOUT_HORIZONTAL) {
1309 ((nitems + 1) * widget_instance_border_width(ii) +
1310 IMAGE_INSTANCE_MARGIN_WIDTH(ii)) * 2;
1313 maxpw + 2 * (widget_instance_border_width(ii) * 2 +
1314 IMAGE_INSTANCE_MARGIN_WIDTH(ii));
1318 /* Work out vertical spacings. */
1319 if (height && IMAGE_INSTANCE_SUBWINDOW_V_RESIZEP(ii)) {
1320 if (!NILP(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii))) {
1321 Lisp_Object dynamic_height =
1322 Feval(IMAGE_INSTANCE_WIDGET_HEIGHT_SUBR(ii));
1323 if (INTP(dynamic_height))
1324 *height = XINT(dynamic_height);
1325 } else if (IMAGE_INSTANCE_SUBWINDOW_LOGICAL_LAYOUT(ii)) {
1326 *height = nitems * luh + ph_adjust;
1327 } else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) ==
1331 ((nitems + 1) * widget_instance_border_width(ii) +
1332 IMAGE_INSTANCE_MARGIN_WIDTH(ii)) * 2 + ph_adjust;
1335 maxph + (2 * widget_instance_border_width(ii) +
1336 IMAGE_INSTANCE_MARGIN_WIDTH(ii)) * 2 +
1340 #ifdef DEBUG_WIDGET_OUTPUT
1341 stderr_out("layout wants %dx%d\n", *width, *height);
1346 layout_layout(Lisp_Object image_instance,
1347 int width, int height, int xoffset, int yoffset,
1350 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1352 Lisp_Object items = IMAGE_INSTANCE_LAYOUT_CHILDREN(ii);
1353 int x, y, maxph = 0, maxpw = 0, nitems = 0,
1354 horiz_spacing, vert_spacing, ph_adjust = 0;
1355 int gheight, gwidth;
1356 /* See comments in widget_logical_unit_height(). */
1357 int luh = widget_logical_unit_height(ii);
1359 /* If we are not initialized then we won't have any children. */
1360 if (!IMAGE_INSTANCE_INITIALIZED(ii))
1363 #ifdef DEBUG_WIDGET_OUTPUT
1364 stderr_out("layout output %dx%d\n", width, height);
1367 /* Pick up the border text if we have one. A border can have the
1368 values Qetched_in, Qetched_out, Qbevel_in, Qbevel_out or an
1369 integer. The first four just affect the display properties of the
1370 border that is drawn. The last is an offset and implies that the
1371 first item in the list of subcontrols is a text control that
1372 should be displayed on the border. */
1373 if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii))) {
1374 Lisp_Object border = XCAR(items);
1375 items = XCDR(items);
1376 glyph_query_geometry(border, &gwidth, &gheight,
1377 IMAGE_DESIRED_GEOMETRY, image_instance);
1378 /* The vertical offset for subsequent items is the full height
1379 of the border glyph. */
1380 ph_adjust = gheight;
1381 /* The offset for the border is half the glyph height. */
1382 IMAGE_INSTANCE_LAYOUT_BORDER(ii) = make_int(gheight / 2);
1384 /* #### Really, what should this be? */
1385 glyph_do_layout(border, gwidth, gheight, BORDER_FIDDLE_FACTOR,
1389 /* Flip through the items to work out how much stuff we have to display. */
1390 LIST_LOOP(rest, items) {
1391 Lisp_Object glyph = XCAR(rest);
1393 glyph_query_geometry(glyph, &gwidth, &gheight,
1394 IMAGE_DESIRED_GEOMETRY, image_instance);
1396 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)
1397 == LAYOUT_HORIZONTAL) {
1398 maxph = max(maxph, gheight);
1401 maxpw = max(maxpw, gwidth);
1406 /* work out spacing between items and bounds of the layout */
1408 /* The user wants a smaller space than the largest item, so we
1409 just provide default spacing and will let the output routines
1411 horiz_spacing = widget_spacing(IMAGE_INSTANCE_DOMAIN(ii));
1412 else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)
1413 == LAYOUT_HORIZONTAL)
1414 /* We have a larger area to display in so distribute the space
1416 horiz_spacing = (width - (maxpw +
1417 IMAGE_INSTANCE_MARGIN_WIDTH(ii) * 2))
1420 horiz_spacing = (width - maxpw) / 2
1421 - IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1423 /* We are trying here to get widgets to line up when they are left
1424 or right justified vertically. This means that we must position
1425 widgets on logical unit boundaries, even though their height may
1426 be greater or less than a logical unit. In order to avoid
1427 clipping we need to determine how big the widget wants to be and
1428 then allocate as many logical units as necessary in order to
1431 vert_spacing = widget_spacing(IMAGE_INSTANCE_DOMAIN(ii)) * 2;
1432 else if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii)
1433 == LAYOUT_VERTICAL) {
1434 if (!IMAGE_INSTANCE_SUBWINDOW_V_CENTERED(ii))
1436 widget_spacing(IMAGE_INSTANCE_DOMAIN(ii)) * 2;
1438 vert_spacing = (height - (maxph + ph_adjust +
1439 IMAGE_INSTANCE_MARGIN_WIDTH
1443 vert_spacing = (height - (maxph + ph_adjust)) / 2
1444 - IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1447 vert_spacing + ph_adjust + IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1448 x = horiz_spacing + IMAGE_INSTANCE_MARGIN_WIDTH(ii);
1450 /* Now flip through putting items where we want them, paying
1451 attention to justification. Make sure we don't mess with the
1453 LIST_LOOP(rest, items) {
1454 Lisp_Object glyph = XCAR(rest);
1456 glyph_query_geometry(glyph, &gwidth, &gheight,
1457 IMAGE_DESIRED_GEOMETRY, image_instance);
1459 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_HORIZONTAL) {
1460 if (IMAGE_INSTANCE_SUBWINDOW_BOTTOM_JUSTIFIED(ii))
1461 y = height - (gheight + vert_spacing);
1462 else if (IMAGE_INSTANCE_SUBWINDOW_V_CENTERED(ii))
1463 y = (height - gheight) / 2;
1465 if (IMAGE_INSTANCE_SUBWINDOW_RIGHT_JUSTIFIED(ii))
1466 x = width - (gwidth + horiz_spacing);
1467 else if (IMAGE_INSTANCE_SUBWINDOW_H_CENTERED(ii))
1468 x = (width - gwidth) / 2;
1471 /* Now layout subwidgets if they require it. */
1472 glyph_do_layout(glyph, gwidth, gheight, x, y, image_instance);
1474 if (IMAGE_INSTANCE_SUBWINDOW_ORIENT(ii) == LAYOUT_HORIZONTAL) {
1475 x += (gwidth + horiz_spacing);
1477 y += (gheight + vert_spacing);
1478 if (!IMAGE_INSTANCE_SUBWINDOW_V_CENTERED(ii)) {
1479 /* justified, vertical layout, try and align on logical unit
1481 y = ROUND_UP(y - yoffset, luh) + yoffset;
1489 /* Get the glyphs that comprise a layout. These are created internally
1490 and so are otherwise inaccessible to lisp. We need some way of getting
1491 properties from the widgets that comprise a layout and this is the
1492 simplest way of doing it.
1494 #### Eventually we should allow some more intelligent access to
1496 static Lisp_Object layout_property(Lisp_Object image_instance, Lisp_Object prop)
1498 /* This function can GC. */
1499 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1500 if (EQ(prop, Q_items)) {
1501 if (INTP(IMAGE_INSTANCE_LAYOUT_BORDER(ii)) &&
1502 CONSP(IMAGE_INSTANCE_LAYOUT_CHILDREN(ii)))
1503 return Fcopy_sequence(XCDR
1504 (IMAGE_INSTANCE_LAYOUT_CHILDREN
1508 Fcopy_sequence(IMAGE_INSTANCE_LAYOUT_CHILDREN(ii));
1513 /* Layout subwindows if they are real subwindows. */
1515 native_layout_layout(Lisp_Object image_instance,
1516 int width, int height, int xoffset, int yoffset,
1519 Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1522 /* The first time this gets called, the layout will be only
1523 partially instantiated. The children get done in
1524 post_instantiate. */
1525 if (!IMAGE_INSTANCE_INITIALIZED(ii))
1528 /* Defining this overrides the default layout_layout so we first have to call that to get
1529 suitable instances and values set up. */
1530 layout_layout(image_instance, width, height, xoffset, yoffset, domain);
1532 LIST_LOOP(rest, IMAGE_INSTANCE_LAYOUT_CHILDREN(ii)) {
1533 struct display_glyph_area dga;
1536 dga.width = IMAGE_INSTANCE_WIDTH(ii);
1537 dga.height = IMAGE_INSTANCE_HEIGHT(ii);
1539 map_subwindow(XCAR(rest),
1540 IMAGE_INSTANCE_XOFFSET(ii),
1541 IMAGE_INSTANCE_YOFFSET(ii), &dga);
1546 DEFUN("widget-logical-to-character-width", Fwidget_logical_to_character_width, 1, 3, 0, /*
1547 Convert the width in logical widget units to characters.
1548 Logical widget units do not take into account adjusments made for
1549 layout borders, so this adjusment is approximated.
1551 (width, face, domain))
1553 int w, neww, charwidth;
1554 int border_width = DEFAULT_WIDGET_BORDER_WIDTH;
1557 domain = Fselected_frame(Qnil);
1562 if (HAS_DEVMETH_P(DOMAIN_XDEVICE(domain), widget_border_width))
1564 DEVMETH(DOMAIN_XDEVICE(domain), widget_border_width, ());
1566 default_face_font_info(domain, 0, 0, 0, &charwidth, 0);
1568 ROUND_UP(charwidth * w + 4 * border_width +
1569 2 * widget_spacing(domain), charwidth) / charwidth;
1571 return make_int(neww);
1574 DEFUN("widget-logical-to-character-height", Fwidget_logical_to_character_height, 1, 3, 0, /*
1575 Convert the height in logical widget units to characters.
1576 Logical widget units do not take into account adjusments made for
1577 layout borders, so this adjustment is approximated.
1579 If the components of a widget layout are justified to the top or the
1580 bottom then they are aligned in terms of `logical units'. This is a
1581 size quantity that is designed to be big enough to accomodate the
1582 largest `single height' widget. It is dependent on the widget face and
1583 some combination of spacing and border-width. Thus if you specify top
1584 or bottom justification in a vertical layout the subcontrols are laid
1585 out one per logical unit. This allows adjoining layouts to have
1586 identical alignment for their subcontrols.
1588 Since frame sizes are measured in characters, this function allows you
1589 to do appropriate conversion between logical units and characters.
1591 (height, face, domain))
1593 int h, newh, charheight;
1597 domain = Fselected_frame(Qnil);
1601 default_face_font_info(domain, 0, 0, &charheight, 0, 0);
1602 newh = ROUND_UP(logical_unit_height(Fsymbol_name(Qwidget),
1603 Vwidget_face, domain) * h,
1607 return make_int(newh);
1610 /************************************************************************/
1611 /* initialization */
1612 /************************************************************************/
1614 void syms_of_glyphs_widget(void)
1616 DEFSYMBOL(Qetched_in);
1617 DEFSYMBOL(Qetched_out);
1618 DEFSYMBOL(Qbevel_in);
1619 DEFSYMBOL(Qbevel_out);
1620 DEFSYMBOL(Qmake_glyph);
1622 DEFSUBR(Fwidget_logical_to_character_height);
1623 DEFSUBR(Fwidget_logical_to_character_width);
1626 #define VALID_GUI_KEYWORDS(type) do { \
1627 IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_active, check_valid_anything); \
1628 IIFORMAT_VALID_KEYWORD (type, Q_suffix, check_valid_anything); \
1629 IIFORMAT_VALID_KEYWORD (type, Q_keys, check_valid_string); \
1630 IIFORMAT_VALID_KEYWORD (type, Q_style, check_valid_symbol); \
1631 IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_selected, check_valid_anything); \
1632 IIFORMAT_VALID_KEYWORD (type, Q_filter, check_valid_anything); \
1633 IIFORMAT_VALID_KEYWORD (type, Q_config, check_valid_symbol); \
1634 IIFORMAT_VALID_KEYWORD (type, Q_included, check_valid_anything); \
1635 IIFORMAT_VALID_KEYWORD (type, Q_initial_focus, check_valid_anything); \
1636 IIFORMAT_VALID_KEYWORD (type, Q_key_sequence, check_valid_string); \
1637 IIFORMAT_VALID_KEYWORD (type, Q_accelerator, check_valid_string); \
1638 IIFORMAT_VALID_KEYWORD (type, Q_label, check_valid_anything); \
1639 IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback, check_valid_callback); \
1640 IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_callback_ex, check_valid_callback); \
1641 IIFORMAT_VALID_NONCOPY_KEYWORD (type, Q_descriptor, \
1642 check_valid_string_or_vector); \
1645 #define VALID_WIDGET_KEYWORDS(type) do { \
1646 IIFORMAT_VALID_KEYWORD (type, Q_width, check_valid_int); \
1647 IIFORMAT_VALID_KEYWORD (type, Q_height, check_valid_int); \
1648 IIFORMAT_VALID_KEYWORD (type, Q_pixel_width, check_valid_int_or_function); \
1649 IIFORMAT_VALID_KEYWORD (type, Q_pixel_height, check_valid_int_or_function); \
1650 IIFORMAT_VALID_KEYWORD (type, Q_face, check_valid_face); \
1653 static void image_instantiator_widget(void)
1654 { /* we only do this for properties */
1655 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT_NO_SYM(widget, "widget");
1656 IIFORMAT_HAS_METHOD(widget, property);
1657 IIFORMAT_HAS_METHOD(widget, update);
1658 IIFORMAT_HAS_METHOD(widget, query_geometry);
1659 IIFORMAT_HAS_METHOD(widget, layout);
1662 static void image_instantiator_buttons(void)
1664 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(button, "button");
1665 IIFORMAT_HAS_SHARED_METHOD(button, validate, widget);
1666 IIFORMAT_HAS_SHARED_METHOD(button, possible_dest_types, widget);
1667 IIFORMAT_HAS_SHARED_METHOD(button, instantiate, widget);
1668 IIFORMAT_HAS_SHARED_METHOD(button, post_instantiate, widget);
1669 IIFORMAT_HAS_SHARED_METHOD(button, normalize, widget);
1670 IIFORMAT_HAS_SHARED_METHOD(button, governing_domain, subwindow);
1671 IIFORMAT_HAS_METHOD(button, query_geometry);
1672 IIFORMAT_VALID_KEYWORD(button, Q_image, check_valid_instantiator);
1673 VALID_WIDGET_KEYWORDS(button);
1674 VALID_GUI_KEYWORDS(button);
1677 static void image_instantiator_edit_fields(void)
1679 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(edit_field, "edit-field");
1680 IIFORMAT_HAS_SHARED_METHOD(edit_field, validate, widget);
1681 IIFORMAT_HAS_SHARED_METHOD(edit_field, possible_dest_types, widget);
1682 IIFORMAT_HAS_SHARED_METHOD(edit_field, instantiate, widget);
1683 IIFORMAT_HAS_SHARED_METHOD(edit_field, post_instantiate, widget);
1684 IIFORMAT_HAS_SHARED_METHOD(edit_field, governing_domain, subwindow);
1685 IIFORMAT_HAS_METHOD(edit_field, query_geometry);
1686 VALID_WIDGET_KEYWORDS(edit_field);
1687 VALID_GUI_KEYWORDS(edit_field);
1690 static void image_instantiator_combo_box(void)
1692 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(combo_box, "combo-box");
1693 IIFORMAT_HAS_METHOD(combo_box, validate);
1694 IIFORMAT_HAS_SHARED_METHOD(combo_box, possible_dest_types, widget);
1695 IIFORMAT_HAS_SHARED_METHOD(combo_box, governing_domain, subwindow);
1697 VALID_GUI_KEYWORDS(combo_box);
1699 IIFORMAT_VALID_KEYWORD(combo_box, Q_width, check_valid_int);
1700 IIFORMAT_VALID_KEYWORD(combo_box, Q_height, check_valid_int);
1701 IIFORMAT_VALID_KEYWORD(combo_box, Q_pixel_width,
1702 check_valid_int_or_function);
1703 IIFORMAT_VALID_KEYWORD(combo_box, Q_face, check_valid_face);
1704 IIFORMAT_VALID_KEYWORD(combo_box, Q_items, check_valid_item_list);
1707 static void image_instantiator_scrollbar(void)
1709 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(scrollbar, "scrollbar");
1710 IIFORMAT_HAS_SHARED_METHOD(scrollbar, validate, widget);
1711 IIFORMAT_HAS_SHARED_METHOD(scrollbar, possible_dest_types, widget);
1712 IIFORMAT_HAS_SHARED_METHOD(scrollbar, instantiate, widget);
1713 IIFORMAT_HAS_SHARED_METHOD(scrollbar, post_instantiate, widget);
1714 IIFORMAT_HAS_SHARED_METHOD(scrollbar, governing_domain, subwindow);
1715 VALID_GUI_KEYWORDS(scrollbar);
1717 IIFORMAT_VALID_KEYWORD(scrollbar, Q_pixel_width,
1718 check_valid_int_or_function);
1719 IIFORMAT_VALID_KEYWORD(scrollbar, Q_pixel_height,
1720 check_valid_int_or_function);
1721 IIFORMAT_VALID_KEYWORD(scrollbar, Q_face, check_valid_face);
1724 static void image_instantiator_progress_guage(void)
1726 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(progress_gauge, "progress-gauge");
1727 IIFORMAT_HAS_SHARED_METHOD(progress_gauge, validate, widget);
1728 IIFORMAT_HAS_SHARED_METHOD(progress_gauge, possible_dest_types, widget);
1729 IIFORMAT_HAS_SHARED_METHOD(progress_gauge, instantiate, widget);
1730 IIFORMAT_HAS_SHARED_METHOD(progress_gauge, post_instantiate, widget);
1731 IIFORMAT_HAS_SHARED_METHOD(progress_gauge, governing_domain, subwindow);
1732 VALID_WIDGET_KEYWORDS(progress_gauge);
1733 VALID_GUI_KEYWORDS(progress_gauge);
1735 IIFORMAT_VALID_KEYWORD(progress_gauge, Q_value, check_valid_int);
1738 static void image_instantiator_tree_view(void)
1740 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(tree_view, "tree-view");
1741 IIFORMAT_HAS_SHARED_METHOD(tree_view, validate, combo_box);
1742 IIFORMAT_HAS_SHARED_METHOD(tree_view, possible_dest_types, widget);
1743 IIFORMAT_HAS_SHARED_METHOD(tree_view, instantiate, widget);
1744 IIFORMAT_HAS_SHARED_METHOD(tree_view, post_instantiate, widget);
1745 IIFORMAT_HAS_SHARED_METHOD(tree_view, governing_domain, subwindow);
1746 IIFORMAT_HAS_METHOD(tree_view, query_geometry);
1747 VALID_WIDGET_KEYWORDS(tree_view);
1748 VALID_GUI_KEYWORDS(tree_view);
1749 IIFORMAT_VALID_KEYWORD(tree_view, Q_items, check_valid_item_list);
1752 static void image_instantiator_tab_control(void)
1754 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(tab_control, "tab-control");
1755 IIFORMAT_HAS_SHARED_METHOD(tab_control, validate, combo_box);
1756 IIFORMAT_HAS_SHARED_METHOD(tab_control, possible_dest_types, widget);
1757 IIFORMAT_HAS_SHARED_METHOD(tab_control, instantiate, widget);
1758 IIFORMAT_HAS_SHARED_METHOD(tab_control, post_instantiate, widget);
1759 IIFORMAT_HAS_SHARED_METHOD(tab_control, governing_domain, subwindow);
1760 IIFORMAT_HAS_METHOD(tab_control, query_geometry);
1761 VALID_WIDGET_KEYWORDS(tab_control);
1762 VALID_GUI_KEYWORDS(tab_control);
1763 IIFORMAT_VALID_KEYWORD(tab_control, Q_orientation,
1764 check_valid_tab_orientation);
1765 IIFORMAT_VALID_KEYWORD(tab_control, Q_items, check_valid_item_list);
1768 static void image_instantiator_labels(void)
1770 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(label, "label");
1771 IIFORMAT_HAS_SHARED_METHOD(label, possible_dest_types, widget);
1772 IIFORMAT_HAS_SHARED_METHOD(label, instantiate, widget);
1773 IIFORMAT_HAS_SHARED_METHOD(label, post_instantiate, widget);
1774 IIFORMAT_HAS_SHARED_METHOD(label, governing_domain, subwindow);
1775 VALID_WIDGET_KEYWORDS(label);
1776 IIFORMAT_VALID_KEYWORD(label, Q_descriptor, check_valid_string);
1779 #define VALID_LAYOUT_KEYWORDS(layout) \
1780 VALID_WIDGET_KEYWORDS (layout); \
1781 IIFORMAT_VALID_KEYWORD (layout, Q_orientation, check_valid_orientation); \
1782 IIFORMAT_VALID_KEYWORD (layout, Q_justify, check_valid_justification); \
1783 IIFORMAT_VALID_KEYWORD (layout, Q_vertically_justify, check_valid_justification); \
1784 IIFORMAT_VALID_KEYWORD (layout, Q_horizontally_justify, check_valid_justification); \
1785 IIFORMAT_VALID_KEYWORD (layout, Q_border, check_valid_border); \
1786 IIFORMAT_VALID_KEYWORD (layout, Q_margin_width, check_valid_int); \
1787 IIFORMAT_VALID_KEYWORD (layout, Q_items, \
1788 check_valid_instantiator_list)
1790 static void image_instantiator_layout(void)
1792 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(layout, "layout");
1793 IIFORMAT_HAS_SHARED_METHOD(layout, possible_dest_types, widget);
1794 IIFORMAT_HAS_METHOD(layout, instantiate);
1795 IIFORMAT_HAS_METHOD(layout, post_instantiate);
1796 IIFORMAT_HAS_SHARED_METHOD(layout, governing_domain, subwindow);
1797 IIFORMAT_HAS_METHOD(layout, normalize);
1798 IIFORMAT_HAS_METHOD(layout, query_geometry);
1799 IIFORMAT_HAS_METHOD(layout, layout);
1800 IIFORMAT_HAS_METHOD(layout, update);
1801 IIFORMAT_HAS_METHOD(layout, property);
1803 VALID_GUI_KEYWORDS(layout);
1804 VALID_LAYOUT_KEYWORDS(layout);
1807 static void image_instantiator_native_layout(void)
1809 INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(native_layout, "native-layout");
1810 IIFORMAT_HAS_SHARED_METHOD(native_layout, possible_dest_types, widget);
1811 IIFORMAT_HAS_SHARED_METHOD(native_layout, instantiate, layout);
1812 IIFORMAT_HAS_SHARED_METHOD(native_layout, post_instantiate, layout);
1813 IIFORMAT_HAS_METHOD(native_layout, layout);
1814 IIFORMAT_HAS_SHARED_METHOD(native_layout, governing_domain, subwindow);
1815 IIFORMAT_HAS_SHARED_METHOD(native_layout, normalize, layout);
1816 IIFORMAT_HAS_SHARED_METHOD(native_layout, query_geometry, layout);
1817 IIFORMAT_HAS_SHARED_METHOD(native_layout, layout, layout);
1818 IIFORMAT_HAS_SHARED_METHOD(native_layout, property, layout);
1820 VALID_GUI_KEYWORDS(native_layout);
1821 VALID_LAYOUT_KEYWORDS(native_layout);
1824 void image_instantiator_format_create_glyphs_widget(void)
1826 image_instantiator_widget();
1827 image_instantiator_buttons();
1828 image_instantiator_edit_fields();
1829 image_instantiator_combo_box();
1830 image_instantiator_scrollbar();
1831 image_instantiator_progress_guage();
1832 image_instantiator_tree_view();
1833 image_instantiator_tab_control();
1834 image_instantiator_labels();
1835 image_instantiator_layout();
1836 image_instantiator_native_layout();
1839 void reinit_vars_of_glyphs_widget(void)
1841 #ifdef DEBUG_WIDGETS
1842 debug_widget_instances = 0;
1846 void vars_of_glyphs_widget(void)
1848 reinit_vars_of_glyphs_widget();
1851 void specifier_vars_of_glyphs_widget(void)
1853 DEFVAR_SPECIFIER("widget-border-width", &Vwidget_border_width /*
1854 *Border width of widgets.
1855 This is a specifier; use `set-specifier' to change it.
1857 Vwidget_border_width = Fmake_specifier(Qnatnum);