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