List.c revision 5b16253f
1/*
2Copyright 1989, 1994, 1998  The Open Group
3
4Permission to use, copy, modify, distribute, and sell this software and its
5documentation for any purpose is hereby granted without fee, provided that
6the above copyright notice appear in all copies and that both that
7copyright notice and this permission notice appear in supporting
8documentation.
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20Except as contained in this notice, the name of The Open Group shall not be
21used in advertising or otherwise to promote the sale, use or other dealings
22in this Software without prior written authorization from The Open Group.
23*/
24
25/*
26 * List.c - List widget
27 *
28 * This is a List widget.  It allows the user to select an item in a list and
29 * notifies the application through a callback function.
30 *
31 *	Created:	8/13/88
32 *	By:		Chris D. Peterson
33 *                      MIT X Consortium
34 */
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39#include <stdio.h>
40#include <ctype.h>
41#include <X11/IntrinsicP.h>
42#include <X11/StringDefs.h>
43#include <X11/Xmu/Drawing.h>
44#include <X11/Xaw/ListP.h>
45#include <X11/Xaw/XawInit.h>
46#include "Private.h"
47
48#define HeightLock  1
49#define WidthLock   2
50#define LongestLock 4
51
52#define HeightFree(w)	!(((ListWidget)(w))->list.freedoms & HeightLock)
53#define WidthFree(w)	!(((ListWidget)(w))->list.freedoms & WidthLock)
54#define LongestFree(w)	!(((ListWidget)(w))->list.freedoms & LongestLock)
55
56#define MaxSize 32767
57
58/*
59 * Class Methods
60 */
61static void XawListDestroy(Widget);
62static void XawListInitialize(Widget, Widget, ArgList, Cardinal*);
63static XtGeometryResult XawListQueryGeometry(Widget, XtWidgetGeometry*,
64					     XtWidgetGeometry*);
65static void XawListRedisplay(Widget, XEvent*, Region);
66static void XawListResize(Widget);
67static Boolean XawListSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
68
69/*
70 * Prototypes
71 */
72static void CalculatedValues(Widget);
73static void ChangeSize(Widget, unsigned int, unsigned int);
74static void ClipToShadowInteriorAndLongest(ListWidget, GC*, unsigned int);
75static int CvtToItem(Widget, int, int, int*);
76static void FindCornerItems(Widget, XEvent*, int*, int*);
77static void GetGCs(Widget);
78static void HighlightBackground(Widget, int, int, GC);
79static Bool ItemInRectangle(Widget, int, int, int);
80static Bool Layout(Widget, Bool, Bool, Dimension*, Dimension*);
81static void PaintItemName(Widget, int);
82static void ResetList(Widget, Bool, Bool);
83
84/*
85 * Actions
86 */
87static void Notify(Widget, XEvent*, String*, Cardinal*);
88static void Set(Widget, XEvent*, String*, Cardinal*);
89static void Unset(Widget, XEvent*, String*, Cardinal*);
90
91/*
92 * Initialization
93 */
94static char defaultTranslations[] =
95"<Btn1Down>:"	"Set()\n"
96"<Btn1Up>:"	"Notify()\n"
97;
98
99#define offset(field) XtOffsetOf(ListRec, field)
100static XtResource resources[] = {
101  {
102    XtNforeground,
103    XtCForeground,
104    XtRPixel,
105    sizeof(Pixel),
106    offset(list.foreground),
107    XtRString,
108    (XtPointer)XtDefaultForeground
109  },
110  {
111    XtNcursor,
112    XtCCursor,
113    XtRCursor,
114    sizeof(Cursor),
115    offset(simple.cursor),
116    XtRString,
117    (XtPointer)"left_ptr"
118  },
119  {
120    XtNfont,
121    XtCFont,
122    XtRFontStruct,
123    sizeof(XFontStruct*),
124    offset(list.font),
125    XtRString,
126    (XtPointer)XtDefaultFont
127  },
128  {
129    XtNfontSet,
130    XtCFontSet,
131    XtRFontSet,
132    sizeof(XFontSet),
133    offset(list.fontset),
134    XtRString,
135    (XtPointer)XtDefaultFontSet
136  },
137  {
138    XtNlist,
139    XtCList,
140    XtRPointer,
141    sizeof(char**),
142    offset(list.list),
143#ifdef notyet
144    XtRStringArray,
145#else
146    XtRString,
147#endif
148    NULL
149  },
150  {
151    XtNdefaultColumns,
152    XtCColumns,
153    XtRInt,
154    sizeof(int),
155    offset(list.default_cols),
156    XtRImmediate,
157    (XtPointer)2
158  },
159  {
160    XtNlongest,
161    XtCLongest,
162    XtRInt,
163    sizeof(int),
164    offset(list.longest),
165    XtRImmediate,
166    (XtPointer)0
167  },
168  {
169    XtNnumberStrings,
170    XtCNumberStrings,
171    XtRInt,
172    sizeof(int),
173    offset(list.nitems),
174    XtRImmediate,
175    (XtPointer)0
176  },
177  {
178    XtNpasteBuffer,
179    XtCBoolean,
180    XtRBoolean,
181    sizeof(Boolean),
182    offset(list.paste),
183    XtRImmediate,
184    (XtPointer)False
185  },
186  {
187    XtNforceColumns,
188    XtCColumns,
189    XtRBoolean,
190    sizeof(Boolean),
191    offset(list.force_cols),
192    XtRImmediate,
193    (XtPointer)False
194  },
195  {
196    XtNverticalList,
197    XtCBoolean,
198    XtRBoolean,
199    sizeof(Boolean),
200    offset(list.vertical_cols),
201    XtRImmediate,
202    (XtPointer)False
203  },
204  {
205    XtNinternalWidth,
206    XtCWidth,
207    XtRDimension,
208    sizeof(Dimension),
209    offset(list.internal_width),
210    XtRImmediate,
211    (XtPointer)2
212  },
213  {
214    XtNinternalHeight,
215    XtCHeight,
216    XtRDimension,
217    sizeof(Dimension),
218    offset(list.internal_height),
219    XtRImmediate,
220    (XtPointer)2
221  },
222  {
223    XtNcolumnSpacing,
224    XtCSpacing,
225    XtRDimension,
226    sizeof(Dimension),
227    offset(list.column_space),
228    XtRImmediate,
229    (XtPointer)6
230  },
231  {
232    XtNrowSpacing,
233    XtCSpacing,
234    XtRDimension,
235    sizeof(Dimension),
236    offset(list.row_space),
237    XtRImmediate,
238    (XtPointer)2
239  },
240  {
241    XtNcallback,
242    XtCCallback,
243    XtRCallback,
244    sizeof(XtPointer),
245    offset(list.callback),
246    XtRCallback,
247    NULL
248  },
249#ifndef OLDXAW
250  {
251    XtNshowCurrent,
252    XtCBoolean,
253    XtRBoolean,
254    sizeof(Boolean),
255    offset(list.show_current),
256    XtRImmediate,
257    (XtPointer)False
258  },
259#endif
260};
261#undef offset
262
263static XtActionsRec actions[] = {
264      {"Notify",	Notify},
265      {"Set",		Set},
266      {"Unset",		Unset},
267};
268
269#define Superclass (&simpleClassRec)
270ListClassRec listClassRec = {
271  /* core */
272  {
273    (WidgetClass)Superclass,		/* superclass */
274    "List",				/* class_name */
275    sizeof(ListRec),			/* widget_size */
276    XawInitializeWidgetSet,		/* class_initialize */
277    NULL,				/* class_part_initialize */
278    False,				/* class_inited */
279    XawListInitialize,			/* initialize */
280    NULL,				/* initialize_hook */
281    XtInheritRealize,			/* realize */
282    actions,				/* actions */
283    XtNumber(actions),			/* num_actions */
284    resources,				/* resources */
285    XtNumber(resources),		/* num_resources */
286    NULLQUARK,				/* xrm_class */
287    True,				/* compress_motion */
288    False,				/* compress_exposure */
289    True,				/* compress_enterleave */
290    False,				/* visible_interest */
291    XawListDestroy,			/* destroy */
292    XawListResize,			/* resize */
293    XawListRedisplay,			/* expose */
294    XawListSetValues,			/* set_values */
295    NULL,				/* set_values_hook */
296    XtInheritSetValuesAlmost,		/* set_values_almost */
297    NULL,				/* get_values_hook */
298    NULL,				/* accept_focus */
299    XtVersion,				/* version */
300    NULL,				/* callback_private */
301    defaultTranslations,		/* tm_table */
302    XawListQueryGeometry,		/* query_geometry */
303  },
304  /* simple */
305  {
306    XtInheritChangeSensitive,		/* change_sensitive */
307  },
308  /* list */
309  {
310    NULL,				/* extension */
311  },
312};
313
314WidgetClass listWidgetClass = (WidgetClass)&listClassRec;
315
316/*
317 * Implementation
318 */
319static void
320GetGCs(Widget w)
321{
322    XGCValues	values;
323    ListWidget lw = (ListWidget)w;
324
325    values.foreground	= lw->list.foreground;
326    values.font		= lw->list.font->fid;
327
328    if (lw->simple.international == True)
329	lw->list.normgc = XtAllocateGC(w, 0, GCForeground, &values, GCFont, 0);
330    else
331	lw->list.normgc = XtGetGC(w, GCForeground | GCFont, &values);
332
333    values.foreground	= lw->core.background_pixel;
334
335    if (lw->simple.international == True)
336	lw->list.revgc = XtAllocateGC(w, 0, GCForeground, &values, GCFont, 0);
337    else
338	lw->list.revgc = XtGetGC(w, GCForeground | GCFont, &values);
339
340    values.tile       = XmuCreateStippledPixmap(XtScreen(w),
341						lw->list.foreground,
342						lw->core.background_pixel,
343						lw->core.depth);
344    values.fill_style = FillTiled;
345
346    if (lw->simple.international == True)
347	lw->list.graygc = XtAllocateGC(w, 0, GCTile | GCFillStyle,
348				       &values, GCFont, 0);
349    else
350	lw->list.graygc = XtGetGC(w, GCFont | GCTile | GCFillStyle, &values);
351}
352
353static void
354CalculatedValues(Widget w)
355{
356    int i, len;
357    ListWidget lw = (ListWidget)w;
358
359    /* If list is NULL then the list will just be the name of the widget */
360    if (lw->list.list == NULL) {
361	lw->list.list = &lw->core.name;
362	lw->list.nitems = 1;
363    }
364
365    /* Get number of items */
366    if (lw->list.nitems == 0)
367	for (; lw->list.list[lw->list.nitems] != NULL ; lw->list.nitems++)
368	    ;
369
370    /* Get column width */
371    if (LongestFree(lw)) {
372	lw->list.longest = 0; /* so it will accumulate real longest below */
373
374	for (i = 0 ; i < lw->list.nitems; i++) {
375	    if (lw->simple.international == True)
376		len = XmbTextEscapement(lw->list.fontset, lw->list.list[i],
377					(int)strlen(lw->list.list[i]));
378	    else
379		len = XTextWidth(lw->list.font, lw->list.list[i],
380				 (int)strlen(lw->list.list[i]));
381	    if (len > lw->list.longest)
382		lw->list.longest = len;
383	}
384    }
385
386    lw->list.col_width = lw->list.longest + lw->list.column_space;
387}
388
389/*
390 * Function:
391 *	ResetList
392 *
393 * Parameters:
394 *	w	- list widget
395 *	changex - allow the height or width to change?
396 *	changey - ""
397 *
398 * Description:
399 *	Resets the new list when important things change.
400 *
401 * Returns:
402 *	True if width or height have been changed
403 */
404static void
405ResetList(Widget w, Bool changex, Bool changey)
406{
407    Dimension width = XtWidth(w);
408    Dimension height = XtHeight(w);
409
410    CalculatedValues(w);
411
412    if (Layout(w, changex, changey, &width, &height)) {
413	if (XtIsComposite(XtParent(w)))
414	    ChangeSize(w, width, height);
415	else {
416	    XtWidth(w) = width;
417	    XtHeight(w) = height;
418	}
419    }
420}
421
422/*
423 * Function:
424 *	ChangeSize
425 *
426 * Parameters:
427 *	w - widget to try change the size of
428 *
429 * Description:
430 *	Laysout the widget.
431 */
432static void
433ChangeSize(Widget w, unsigned int width, unsigned int height)
434{
435    XtWidgetGeometry request, reply;
436
437    request.request_mode = CWWidth | CWHeight;
438    request.width = (Dimension)width;
439    request.height = (Dimension)height;
440
441    switch (XtMakeGeometryRequest(w, &request, &reply)) {
442	case XtGeometryYes:
443	case XtGeometryNo:
444	    break;
445	case XtGeometryAlmost:
446	    Layout(w, request.height != reply.height,
447		   request.width != reply.width, &reply.width, &reply.height);
448	    request = reply;
449	    switch (XtMakeGeometryRequest(w, &request, &reply)) {
450		case XtGeometryYes:
451		case XtGeometryNo:
452		    break;
453		case XtGeometryAlmost:
454		    request = reply;
455		    Layout(w, False, False, &request.width, &request.height);
456		    request.request_mode = CWWidth | CWHeight;
457		    XtMakeGeometryRequest(w, &request, &reply);
458		    /*FALLTHROUGH*/
459		default:
460		    break;
461	}
462	/*FALLTHROUGH*/
463	default:
464	    break;
465    }
466}
467
468/*ARGSUSED*/
469static void
470XawListInitialize(Widget temp1 _X_UNUSED, Widget cnew, ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
471{
472    ListWidget lw = (ListWidget)cnew;
473
474    if (!lw->list.font) XtError("Aborting: no font found\n");
475    if (lw->simple.international && !lw->list.fontset)
476	XtError("Aborting: no fontset found\n");
477
478    /*
479     * Initialize all private resources
480     */
481    /* record for posterity if we are free */
482    lw->list.freedoms = ((XtWidth(lw) != 0) * WidthLock +
483			 (XtHeight(lw) != 0) * HeightLock +
484			 (lw->list.longest != 0) * LongestLock);
485
486    GetGCs(cnew);
487
488    /* Set row height, based on font or fontset */
489    if (lw->simple.international == True)
490	lw->list.row_height =
491		XExtentsOfFontSet(lw->list.fontset)->max_ink_extent.height +
492				  lw->list.row_space;
493    else
494	lw->list.row_height = lw->list.font->max_bounds.ascent +
495			      lw->list.font->max_bounds.descent +
496			      lw->list.row_space;
497
498    ResetList(cnew, WidthFree(lw), HeightFree(lw));
499
500    lw->list.highlight = lw->list.is_highlighted = NO_HIGHLIGHT;
501}
502
503/*
504 * Function:
505 *	CvtToItem
506 *
507 * Parameters:
508 *	w    - list widget
509 *	xloc - x location
510 *	yloc - y location
511 *
512 * Description:
513 *	Converts Xcoord to item number of item containing that point.
514 *
515 * Returns:
516 *	Item number
517 */
518static int
519CvtToItem(Widget w, int xloc, int yloc, int *item)
520{
521    int one, another;
522    ListWidget lw = (ListWidget)w;
523    int ret_val = OKAY;
524
525    if (lw->list.vertical_cols) {
526	one = lw->list.nrows * ((xloc - (int)lw->list.internal_width)
527			        / lw->list.col_width);
528	another = (yloc - (int)lw->list.internal_height) / lw->list.row_height;
529	/* If out of range, return minimum possible value */
530	if (another >= lw->list.nrows) {
531	    another = lw->list.nrows - 1;
532	    ret_val = OUT_OF_RANGE;
533	}
534    }
535    else {
536	one = (lw->list.ncols * ((yloc - (int)lw->list.internal_height)
537				 / lw->list.row_height));
538	/* If in right margin handle things right */
539	another = (xloc - (int)lw->list.internal_width) / lw->list.col_width;
540	if (another >= lw->list.ncols) {
541	    another = lw->list.ncols - 1;
542	    ret_val = OUT_OF_RANGE;
543	}
544    }
545    if (xloc < 0 || yloc < 0)
546	ret_val = OUT_OF_RANGE;
547    if (one < 0)
548	one = 0;
549    if (another < 0)
550	another = 0;
551    *item = one + another;
552    if (*item >= lw->list.nitems)
553	return (OUT_OF_RANGE);
554
555  return (ret_val);
556}
557
558/*
559 * Function:
560 *	FindCornerItems
561 *
562 * Arguments:
563 *	w      - list widget
564 *	event  - event structure that has the rectangle it it
565 *	ul_ret - the corners (return)
566 *	lr_ret - ""
567 *
568 * Description:
569 *	Find the corners of the rectangle in item space.
570 */
571static void
572FindCornerItems(Widget w, XEvent *event, int *ul_ret, int *lr_ret)
573{
574    int xloc, yloc;
575
576    xloc = event->xexpose.x;
577    yloc = event->xexpose.y;
578    CvtToItem(w, xloc, yloc, ul_ret);
579    xloc += event->xexpose.width;
580    yloc += event->xexpose.height;
581    CvtToItem(w, xloc, yloc, lr_ret);
582}
583
584/*
585 * Function:
586 *	ItemInRectangle
587 *
588 * Parameters:
589 *	w    - list widget
590 *	ul   - corners of the rectangle in item space
591 *	lr   - ""
592 *	item - item to check
593 *
594 * Returns:
595 *	True if the item passed is in the given rectangle
596 */
597static Bool
598ItemInRectangle(Widget w, int ul, int lr, int item)
599{
600    ListWidget lw = (ListWidget)w;
601    int mod_item;
602    int things;
603
604    if (item < ul || item > lr)
605      return (False);
606    if (lw->list.vertical_cols)
607	things = lw->list.nrows;
608    else
609	things = lw->list.ncols;
610
611    mod_item = item % things;
612    if ((mod_item >= ul % things) && (mod_item <= lr % things))
613      return (True);
614
615    return (False);
616}
617
618/* HighlightBackground()
619 *
620 * Paints the color of the background for the given item.  It performs
621 * clipping to the interior of internal_width/height by hand, as its a
622 * simple calculation and probably much faster than using Xlib and a clip mask.
623 *
624 *  x, y - ul corner of the area item occupies.
625 *  gc - the gc to use to paint this rectangle
626 */
627static void
628HighlightBackground(Widget w, int x, int y, GC gc)
629{
630    ListWidget lw = (ListWidget)w;
631    Dimension width = (Dimension)lw->list.col_width;
632    Dimension height = (Dimension)lw->list.row_height;
633    Dimension frame_limited_width = (Dimension)(XtWidth(w) - lw->list.internal_width - x);
634    Dimension frame_limited_height= (Dimension)(XtHeight(w) - lw->list.internal_height - y);
635
636    /* Clip the rectangle width and height to the edge of the drawable area */
637    if  (width > frame_limited_width)
638	width = frame_limited_width;
639    if  (height > frame_limited_height)
640	height = frame_limited_height;
641
642    /* Clip the rectangle x and y to the edge of the drawable area */
643    if (x < lw->list.internal_width) {
644	width = (Dimension)(width - (lw->list.internal_width - x));
645	x = lw->list.internal_width;
646    }
647    if (y < lw->list.internal_height) {
648      height = (Dimension)(height - (lw->list.internal_height - y));
649	y = lw->list.internal_height;
650    }
651
652    if (gc == lw->list.revgc && lw->core.background_pixmap != XtUnspecifiedPixmap)
653	XClearArea(XtDisplay(w), XtWindow(w), x, y, width, height, False);
654    else
655	XFillRectangle(XtDisplay(w), XtWindow(w), gc, x, y, width, height);
656}
657
658
659/* ClipToShadowInteriorAndLongest()
660 *
661 * Converts the passed gc so that any drawing done with that GC will not
662 * write in the empty margin (specified by internal_width/height) (which also
663 * prevents erasing the shadow.  It also clips against the value longest.
664 * If the user doesn't set longest, this has no effect (as longest is the
665 * maximum of all item lengths).  If the user does specify, say, 80 pixel
666 * columns, though, this prevents items from overwriting other items.
667 */
668static void
669ClipToShadowInteriorAndLongest(ListWidget lw, GC *gc_p, unsigned int x)
670{
671    XRectangle rect;
672
673    rect.x = (short)x;
674    rect.y = (short)lw->list.internal_height;
675    rect.height = (unsigned short)(XtHeight(lw) - (lw->list.internal_height << 1));
676    rect.width = (unsigned short)(XtWidth(lw) - (unsigned)lw->list.internal_width - x);
677    if (rect.width > lw->list.longest)
678	rect.width = (unsigned short)lw->list.longest;
679
680    XSetClipRectangles(XtDisplay((Widget)lw), *gc_p, 0, 0, &rect, 1, YXBanded);
681}
682
683static void
684PaintItemName(Widget w, int item)
685{
686    _Xconst char *str;
687    GC gc;
688    int x, y, str_y;
689    ListWidget lw = (ListWidget)w;
690    XFontSetExtents *ext  = XExtentsOfFontSet(lw->list.fontset);
691
692    if (!XtIsRealized(w) || item > lw->list.nitems)
693      return;
694
695    if (lw->list.vertical_cols) {
696	x = lw->list.col_width * (item / lw->list.nrows)
697	  + lw->list.internal_width;
698	y = lw->list.row_height * (item % lw->list.nrows)
699	  + lw->list.internal_height;
700    }
701    else {
702	x = lw->list.col_width * (item % lw->list.ncols)
703	  + lw->list.internal_width;
704	y = lw->list.row_height * (item / lw->list.ncols)
705	  + lw->list.internal_height;
706    }
707
708    if ( lw->simple.international == True )
709	str_y = y + XawAbs(ext->max_ink_extent.y);
710    else
711	str_y = y + lw->list.font->max_bounds.ascent;
712
713    if (item == lw->list.is_highlighted) {
714	if (item == lw->list.highlight) {
715	    gc = lw->list.revgc;
716	    HighlightBackground(w, x, y, lw->list.normgc);
717	}
718	else {
719	    if (XtIsSensitive(w))
720		gc = lw->list.normgc;
721	    else
722		gc = lw->list.graygc;
723	    HighlightBackground(w, x, y, lw->list.revgc);
724	    lw->list.is_highlighted = NO_HIGHLIGHT;
725	}
726    }
727    else {
728	if (item == lw->list.highlight) {
729	    gc = lw->list.revgc;
730	    HighlightBackground(w, x, y, lw->list.normgc);
731	    lw->list.is_highlighted = item;
732	}
733	else {
734	    if (XtIsSensitive(w))
735		gc = lw->list.normgc;
736	    else
737		gc = lw->list.graygc;
738	}
739    }
740
741    /* List's overall width contains the same number of inter-column
742       column_space's as columns.  There should thus be a half
743       column_width margin on each side of each column.
744       The row case is symmetric */
745
746    x += lw->list.column_space >> 1;
747    str_y += lw->list.row_space >> 1;
748
749    str =  lw->list.list[item];	/* draw it */
750
751    ClipToShadowInteriorAndLongest(lw, &gc, (unsigned)x);
752
753    if (lw->simple.international == True)
754	XmbDrawString(XtDisplay(w), XtWindow(w), lw->list.fontset,
755		      gc, x, str_y, str, (int)strlen(str));
756    else
757	XDrawString(XtDisplay(w), XtWindow(w), gc, x, str_y, str, (int)strlen(str));
758
759    XSetClipMask(XtDisplay(w), gc, None);
760}
761
762static void
763XawListRedisplay(Widget w, XEvent *event, Region region)
764{
765    int item;			/* an item to work with */
766    int ul_item, lr_item;	/* corners of items we need to paint */
767    ListWidget lw = (ListWidget)w;
768
769    if (event == NULL) {
770	ul_item = 0;
771	lr_item = lw->list.nrows * lw->list.ncols - 1;
772	XClearWindow(XtDisplay(w), XtWindow(w));
773    }
774    else
775	FindCornerItems(w, event, &ul_item, &lr_item);
776
777    if (Superclass->core_class.expose)
778    (Superclass->core_class.expose)(w, event, region);
779
780    for (item = ul_item; item <= lr_item && item < lw->list.nitems; item++)
781	if (ItemInRectangle(w, ul_item, lr_item, item))
782	    PaintItemName(w, item);
783}
784
785/* XawListQueryGeometry()
786 *
787 * This tells the parent what size we would like to be
788 * given certain constraints.
789 * w - the widget.
790 * intended - what the parent intends to do with us.
791 * requested - what we want to happen */
792static XtGeometryResult
793XawListQueryGeometry(Widget w, XtWidgetGeometry *intended,
794		     XtWidgetGeometry *requested)
795{
796    Dimension new_width, new_height;
797    Bool change, width_req, height_req;
798
799    width_req = intended->request_mode & CWWidth;
800    height_req = intended->request_mode & CWHeight;
801
802    if (width_req)
803	new_width = intended->width;
804    else
805	new_width = XtWidth(w);
806
807    if (height_req)
808	new_height = intended->height;
809    else
810	new_height = XtHeight(w);
811
812    requested->request_mode = 0;
813
814   /*
815    * We only care about our height and width
816    */
817    if (!width_req && !height_req)
818	return (XtGeometryYes);
819
820    change = Layout(w, !width_req, !height_req, &new_width, &new_height);
821
822    requested->request_mode |= CWWidth;
823    requested->width = new_width;
824    requested->request_mode |= CWHeight;
825    requested->height = new_height;
826
827    if (change)
828	return (XtGeometryAlmost);
829
830    return (XtGeometryYes);
831}
832
833static void
834XawListResize(Widget w)
835{
836    Dimension width, height;
837
838    width = XtWidth(w);
839    height = XtHeight(w);
840
841    if (Layout(w, False, False, &width, &height))
842	XtAppWarning(XtWidgetToApplicationContext(w),
843		     "List Widget: Size changed when it shouldn't "
844		     "have when resising.");
845}
846
847/* Layout()
848 *
849 * lays out the item in the list.
850 * w - the widget.
851 * xfree, yfree - True if we are free to resize the widget in
852 *		this direction.
853 * width, height- the is the current width and height that we are going
854 *		we are going to layout the list widget to,
855 *		depending on xfree and yfree of course.
856 *
857 * Return:
858 *	True if width or height have been changed */
859static Bool
860Layout(Widget w, Bool xfree, Bool yfree, Dimension *width, Dimension *height)
861{
862    ListWidget lw = (ListWidget)w;
863    Bool change = False;
864    unsigned long width2 = 0, height2 = 0;
865
866    /*
867     * If force columns is set then always use number of columns specified
868     * by default_cols
869     */
870    if (lw->list.force_cols) {
871	lw->list.ncols = lw->list.default_cols;
872	if (lw->list.ncols <= 0)
873	    lw->list.ncols = 1;
874	lw->list.nrows = ((lw->list.nitems - 1) / lw->list.ncols) + 1;
875	if (xfree) {
876	    /* this counts the same number
877	       of inter-column column_space 's as columns.  There should thus
878	       be a half column_space margin on each side of each column...*/
879	    width2 = (unsigned long)(lw->list.ncols * lw->list.col_width +
880		     (lw->list.internal_width << 1));
881	    change = True;
882	}
883	if (yfree) {
884	    height2 = (unsigned long)(lw->list.nrows * lw->list.row_height +
885		      (lw->list.internal_height << 1));
886	    change = True;
887	}
888    }
889
890    /*
891     * If both width and height are free to change the use default_cols
892     * to determine the number columns and set new width and height to
893     * just fit the window
894     */
895    else if (xfree && yfree) {
896	lw->list.ncols = lw->list.default_cols;
897	if (lw->list.ncols <= 0) {
898	    int wid = (int)XtWidth(lw) - (int)(lw->list.internal_width << 1)
899	      + (int)lw->list.column_space;
900
901	    if (wid <= 0 || lw->list.col_width <= 0
902		|| (lw->list.ncols = wid / lw->list.col_width) <= 0)
903		lw->list.ncols = 1;
904	}
905	width2 = (unsigned long)((lw->list.ncols * lw->list.col_width)
906		  + (lw->list.internal_width << 1));
907	height2 = (unsigned long)((lw->list.nrows * lw->list.row_height)
908		   + (lw->list.internal_height << 1));
909	change = True;
910    }
911
912    /*
913     * If the width is fixed then use it to determine the number of columns.
914     * If the height is free to move (width still fixed) then resize the height
915     * of the widget to fit the current list exactly
916     */
917    else if (!xfree) {
918	lw->list.ncols = ((int)(*width - (lw->list.internal_width << 1))
919			  / (int)lw->list.col_width);
920	if (lw->list.ncols <= 0)
921	    lw->list.ncols = 1;
922	lw->list.nrows = ((lw->list.nitems - 1) / lw->list.ncols) + 1;
923	if (yfree) {
924	    height2 = (unsigned long)((lw->list.nrows * lw->list.row_height) +
925		      (lw->list.internal_height << 1));
926	    change = True;
927	}
928    }
929
930    /*
931     * The last case is xfree and !yfree we use the height to determine
932     * the number of rows and then set the width to just fit the resulting
933     * number of columns
934     */
935    else if (!yfree) {
936	lw->list.nrows = ((int)(*height - (lw->list.internal_height << 1))
937			  / (int)lw->list.row_height);
938	if (lw->list.nrows <= 0)
939	    lw->list.nrows = 1;
940	lw->list.ncols = ((lw->list.nitems - 1) / lw->list.nrows) + 1;
941	width2 = (unsigned long)((lw->list.ncols * lw->list.col_width) +
942		 (lw->list.internal_width << 1));
943	change = True;
944    }
945
946    if (!lw->list.force_cols && lw->list.nrows) {
947	/*CONSTCOND*/
948	while (1) {
949	    lw->list.nrows = ((lw->list.nitems - 1) / lw->list.ncols) + 1;
950	    width2 = (unsigned long)((lw->list.ncols * lw->list.col_width) +
951		     (lw->list.internal_width << 1));
952	    height2 = (unsigned long)((lw->list.nrows * lw->list.row_height) +
953		      (lw->list.internal_height << 1));
954	    if (width2 >= MaxSize && height2 >= MaxSize)
955		break;
956	    if (height2 > MaxSize)
957		++lw->list.ncols;
958	    else if (width2 > MaxSize && lw->list.ncols > 1)
959		--lw->list.ncols;
960	    else
961		break;
962	}
963    }
964    if (width2)
965	*width = (Dimension)width2;
966    if (height2)
967	*height = (Dimension)height2;
968
969    return (change);
970}
971
972/* Notify() - Action
973 *
974 * Notifies the user that a button has been pressed, and
975 * calls the callback; if the XtNpasteBuffer resource is true
976 * then the name of the item is also put in CUT_BUFFER0 */
977/*ARGSUSED*/
978static void
979Notify(Widget w, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
980{
981    ListWidget lw = (ListWidget)w;
982    int item, item_len;
983    XawListReturnStruct ret_value;
984
985    /*
986     * Find item and if out of range then unhighlight and return
987     *
988     * If the current item is unhighlighted then the user has aborted the
989     * notify, so unhighlight and return
990     */
991    if ((CvtToItem(w, event->xbutton.x, event->xbutton.y, &item) == OUT_OF_RANGE)
992	|| lw->list.highlight != item) {
993#ifndef OLDXAW
994	if (!lw->list.show_current || lw->list.selected == NO_HIGHLIGHT)
995	    XawListUnhighlight(w);
996	else
997	    XawListHighlight(w, lw->list.selected);
998#else
999	XawListUnhighlight(w);
1000#endif
1001	return;
1002    }
1003
1004    item_len = (int)strlen(lw->list.list[item]);
1005
1006    if (lw->list.paste)		/* if XtNpasteBuffer set then paste it */
1007	XStoreBytes(XtDisplay(w), lw->list.list[item], item_len);
1008
1009#ifndef OLDXAW
1010    lw->list.selected = item;
1011#endif
1012    /*
1013     * Call Callback function
1014     */
1015    ret_value.string = lw->list.list[item];
1016    ret_value.list_index = item;
1017
1018    XtCallCallbacks(w, XtNcallback, (XtPointer)&ret_value);
1019}
1020
1021/* Unset() - Action
1022 *
1023 * unhighlights the current element */
1024/*ARGSUSED*/
1025static void
1026Unset(Widget w, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
1027{
1028    XawListUnhighlight(w);
1029}
1030
1031/* Set() - Action
1032 *
1033 * Highlights the current element */
1034/*ARGSUSED*/
1035static void
1036Set(Widget w, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
1037{
1038    int item;
1039    ListWidget lw = (ListWidget)w;
1040
1041#ifndef OLDXAW
1042    lw->list.selected = lw->list.highlight;
1043#endif
1044    if (CvtToItem(w, event->xbutton.x, event->xbutton.y, &item) == OUT_OF_RANGE)
1045	XawListUnhighlight(w);			/* Unhighlight current item */
1046    else if (lw->list.is_highlighted != item)	/* If this item is not */
1047	XawListHighlight(w, item);		/* highlighted then do it */
1048}
1049
1050/*
1051 * Set specified arguments into widget
1052 */
1053/*ARGSUSED*/
1054static Boolean
1055XawListSetValues(Widget current, Widget request, Widget cnew,
1056		 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
1057{
1058    ListWidget cl = (ListWidget)current;
1059    ListWidget rl = (ListWidget)request;
1060    ListWidget nl = (ListWidget)cnew;
1061    Bool redraw = False;
1062    XFontSetExtents *ext = XExtentsOfFontSet(nl->list.fontset);
1063
1064    /* If the request height/width is different, lock it.  Unless its 0. If
1065       neither new nor 0, leave it as it was.  Not in R5 */
1066    if (XtWidth(nl) != XtWidth(cl))
1067	nl->list.freedoms |= WidthLock;
1068    if (XtWidth(nl) == 0)
1069	nl->list.freedoms &= ~WidthLock;
1070
1071    if (XtHeight(nl) != XtHeight(cl))
1072	nl->list.freedoms |= HeightLock;
1073    if (XtHeight(nl) == 0)
1074	nl->list.freedoms &= ~HeightLock;
1075
1076    if (nl->list.longest != cl->list.longest)
1077	nl->list.freedoms |= LongestLock;
1078    if (nl->list.longest == 0)
1079	nl->list.freedoms &= ~LongestLock;
1080
1081    if (cl->list.foreground != nl->list.foreground ||
1082	cl->core.background_pixel != nl->core.background_pixel ||
1083	cl->list.font != nl->list.font) {
1084	XGCValues values;
1085
1086	XGetGCValues(XtDisplay(current), cl->list.graygc, GCTile, &values);
1087	XmuReleaseStippledPixmap(XtScreen(current), values.tile);
1088	XtReleaseGC(current, cl->list.graygc);
1089	XtReleaseGC(current, cl->list.revgc);
1090	XtReleaseGC(current, cl->list.normgc);
1091	GetGCs(cnew);
1092	redraw = True;
1093    }
1094
1095    if (cl->list.font != nl->list.font && cl->simple.international == False)
1096	nl->list.row_height = nl->list.font->max_bounds.ascent
1097			    + nl->list.font->max_bounds.descent
1098			    + nl->list.row_space;
1099    else if (cl->list.fontset != nl->list.fontset
1100	&& cl->simple.international == True)
1101	nl->list.row_height = ext->max_ink_extent.height + nl->list.row_space;
1102
1103    /* ...If the above two font(set) change checkers above both failed, check
1104       if row_space was altered.  If one of the above passed, row_height will
1105       already have been re-calculated */
1106    else if (cl->list.row_space != nl->list.row_space) {
1107	if (cl->simple.international == True)
1108	    nl->list.row_height = ext->max_ink_extent.height + nl->list.row_space;
1109	else
1110	    nl->list.row_height = nl->list.font->max_bounds.ascent
1111				+ nl->list.font->max_bounds.descent
1112				+ nl->list.row_space;
1113    }
1114
1115    if (XtWidth(cl) != XtWidth(nl) || XtHeight(cl) != XtHeight(nl)
1116	|| cl->list.internal_width != nl->list.internal_width
1117	|| cl->list.internal_height != nl->list.internal_height
1118	|| cl->list.column_space != nl->list.column_space
1119	|| cl->list.row_space != nl->list.row_space
1120	|| cl->list.default_cols != nl->list.default_cols
1121	|| (cl->list.force_cols != nl->list.force_cols
1122	    && rl->list.force_cols != nl->list.ncols)
1123	|| cl->list.vertical_cols != nl->list.vertical_cols
1124	|| cl->list.longest != nl->list.longest
1125	|| cl->list.nitems != nl->list.nitems
1126	|| cl->list.font != nl->list.font
1127	/* Equiv. fontsets might have different values, but the same fonts,
1128	   so the next comparison is sloppy but not dangerous  */
1129	|| cl->list.fontset != nl->list.fontset
1130	|| cl->list.list != nl->list.list) {
1131	CalculatedValues(cnew);
1132	Layout(cnew, WidthFree(nl), HeightFree(nl),
1133	       &nl->core.width, &nl->core.height);
1134	redraw = True;
1135    }
1136
1137    if (cl->list.list != nl->list.list || cl->list.nitems != nl->list.nitems)
1138	nl->list.is_highlighted = nl->list.highlight = NO_HIGHLIGHT;
1139
1140    if (cl->core.sensitive != nl->core.sensitive
1141	|| cl->core.ancestor_sensitive != nl->core.ancestor_sensitive) {
1142	nl->list.highlight = NO_HIGHLIGHT;
1143	redraw = True;
1144    }
1145
1146    return (Boolean)(redraw);
1147}
1148
1149static void
1150XawListDestroy(Widget w)
1151{
1152    ListWidget lw = (ListWidget)w;
1153    XGCValues values;
1154
1155    XGetGCValues(XtDisplay(w), lw->list.graygc, GCTile, &values);
1156    XmuReleaseStippledPixmap(XtScreen(w), values.tile);
1157    XtReleaseGC(w, lw->list.graygc);
1158    XtReleaseGC(w, lw->list.revgc);
1159    XtReleaseGC(w, lw->list.normgc);
1160}
1161
1162/*
1163 * Function:
1164 *	XawListChange
1165 *
1166 * Parameters:
1167 *	w - list widget
1168 *	list - new list
1169 *	nitems - number of items in the list
1170 *	longest - length (in Pixels) of the longest element in the list
1171 *	resize - if True the the list widget will try to resize itself
1172 *
1173 * Description:
1174 *	Changes the list being used and shown.
1175 *
1176 * Note:
1177 *	If nitems of longest are <= 0 then they will be calculated
1178 *	If nitems is <= 0 then the list needs to be NULL terminated
1179 */
1180void
1181XawListChange(Widget w, _Xconst char **list, int nitems, int longest,
1182#if NeedWidePrototypes
1183	int resize_it
1184#else
1185	Boolean resize_it
1186#endif
1187)
1188{
1189    ListWidget lw = (ListWidget)w;
1190    Dimension new_width = XtWidth(w);
1191    Dimension new_height = XtHeight(w);
1192
1193    lw->list.list = list;
1194
1195    if (nitems <= 0)
1196	nitems = 0;
1197    lw->list.nitems = nitems;
1198    if (longest <= 0)
1199	longest = 0;
1200
1201    /* If the user passes 0 meaning "calculate it", it must be free */
1202    if (longest != 0)
1203	lw->list.freedoms |= LongestLock;
1204    else
1205	lw->list.freedoms &= ~LongestLock;
1206
1207    if (resize_it)
1208	lw->list.freedoms &= ~WidthLock & ~HeightLock;
1209
1210    lw->list.longest = longest;
1211
1212    CalculatedValues(w);
1213
1214    if (Layout(w, WidthFree(w), HeightFree(w), &new_width, &new_height))
1215	ChangeSize(w, new_width, new_height);
1216
1217    lw->list.is_highlighted = lw->list.highlight = NO_HIGHLIGHT;
1218    if (XtIsRealized(w))
1219	XawListRedisplay(w, NULL, NULL);
1220}
1221
1222void
1223XawListUnhighlight(Widget w)
1224{
1225    ListWidget lw = (ListWidget)w;
1226
1227    lw->list.highlight = NO_HIGHLIGHT;
1228    if (lw->list.is_highlighted != NO_HIGHLIGHT)
1229    PaintItemName(w, lw->list.is_highlighted);
1230}
1231
1232void
1233XawListHighlight(Widget w, int item)
1234{
1235    ListWidget lw = (ListWidget)w;
1236
1237    if (XtIsSensitive(w)) {
1238	lw->list.highlight = item;
1239	if (lw->list.is_highlighted != NO_HIGHLIGHT)
1240	PaintItemName(w, lw->list.is_highlighted);
1241	PaintItemName(w, item);
1242    }
1243}
1244
1245/*
1246 * Function:
1247 *	XawListShowCurrent
1248 *
1249 * Parameters:
1250 *	w - list widget
1251 *
1252 * Returns:
1253 *	Info about the currently highlighted object
1254 */
1255XawListReturnStruct *
1256XawListShowCurrent(Widget w)
1257{
1258    ListWidget lw = (ListWidget)w;
1259    XawListReturnStruct *ret_val;
1260
1261    ret_val = (XawListReturnStruct *)XtMalloc(sizeof(XawListReturnStruct));
1262
1263    ret_val->list_index = lw->list.highlight;
1264    if (ret_val->list_index == XAW_LIST_NONE)
1265	ret_val->string = "";
1266    else
1267	ret_val->string = lw->list.list[ret_val->list_index];
1268
1269    return (ret_val);
1270}
1271