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 },
304 /* simple */
305 {
306 XtInheritChangeSensitive, /* change_sensitive */
307 },
308 /* list */
309 {
310 NULL, /* extension */
311 },
312 };
313
314 WidgetClass listWidgetClass = (WidgetClass)&listClassRec;
315
316 /*
317 * Implementation
318 */
319 static void
GetGCs(Widget w)320 GetGCs(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
353 static void
CalculatedValues(Widget w)354 CalculatedValues(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 */
404 static void
ResetList(Widget w,Bool changex,Bool changey)405 ResetList(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 */
432 static void
ChangeSize(Widget w,unsigned int width,unsigned int height)433 ChangeSize(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 /*FALLTROUGH*/
459 default:
460 break;
461 }
462 /*FALLTROUGH*/
463 default:
464 break;
465 }
466 }
467
468 /*ARGSUSED*/
469 static void
XawListInitialize(Widget temp1 _X_UNUSED,Widget cnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)470 XawListInitialize(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 */
518 static int
CvtToItem(Widget w,int xloc,int yloc,int * item)519 CvtToItem(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 */
571 static void
FindCornerItems(Widget w,XEvent * event,int * ul_ret,int * lr_ret)572 FindCornerItems(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 */
597 static Bool
ItemInRectangle(Widget w,int ul,int lr,int item)598 ItemInRectangle(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 */
627 static void
HighlightBackground(Widget w,int x,int y,GC gc)628 HighlightBackground(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 */
668 static void
ClipToShadowInteriorAndLongest(ListWidget lw,GC * gc_p,unsigned int x)669 ClipToShadowInteriorAndLongest(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
683 static void
PaintItemName(Widget w,int item)684 PaintItemName(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
762 static void
XawListRedisplay(Widget w,XEvent * event,Region region)763 XawListRedisplay(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 */
792 static XtGeometryResult
XawListQueryGeometry(Widget w,XtWidgetGeometry * intended,XtWidgetGeometry * requested)793 XawListQueryGeometry(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
833 static void
XawListResize(Widget w)834 XawListResize(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 */
859 static Bool
Layout(Widget w,Bool xfree,Bool yfree,Dimension * width,Dimension * height)860 Layout(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*/
978 static void
Notify(Widget w,XEvent * event,String * params _X_UNUSED,Cardinal * num_params _X_UNUSED)979 Notify(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*/
1025 static void
Unset(Widget w,XEvent * event _X_UNUSED,String * params _X_UNUSED,Cardinal * num_params _X_UNUSED)1026 Unset(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*/
1035 static void
Set(Widget w,XEvent * event,String * params _X_UNUSED,Cardinal * num_params _X_UNUSED)1036 Set(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*/
1054 static Boolean
XawListSetValues(Widget current,Widget request,Widget cnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)1055 XawListSetValues(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
1149 static void
XawListDestroy(Widget w)1150 XawListDestroy(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 */
1180 void
XawListChange(Widget w,_Xconst char ** list,int nitems,int longest,int resize_it)1181 XawListChange(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
1222 void
XawListUnhighlight(Widget w)1223 XawListUnhighlight(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
1232 void
XawListHighlight(Widget w,int item)1233 XawListHighlight(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 */
1255 XawListReturnStruct *
XawListShowCurrent(Widget w)1256 XawListShowCurrent(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