1 /*
2  *  Copyright (C) 1995, 1996  Karl-Johan Johnsson.
3  */
4 
5 #include <X11/IntrinsicP.h>
6 #include <X11/StringDefs.h>
7 #include <X11/ShellP.h>
8 #include <string.h>
9 
10 #include "Compat.h"
11 #include "ScrListP.h"
12 #include "Util.h"
13 
14 #define FIRST(w)  ((w)->scrollable.pos_y)
15 #define SHOWN(w)  ((w)->scrollable.shown_y)
16 #define LINES(w)  ((w)->scrollable.height)
17 
18 static void grey90_default_proc(Widget, int, XrmValue*);
19 
20 static XtResource resources[] = {
21 #define offset(field) XtOffsetOf(ScrListRec, scrlist.field)
22     {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension),
23      XtOffsetOf(ShadowRec, shadow.shadow_width), XtRImmediate, (XtPointer)1},
24     {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
25      offset(foreground_pixel), XtRString, XtDefaultForeground},
26     {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
27      offset(font), XtRString, XtDefaultFont},
28     {XtNhighlightColor, XtCHighlightColor, XtRPixel, sizeof(Pixel),
29      offset(highlight_pixel), XtRCallProc, (XtPointer)grey90_default_proc},
30     {XtNrowSpacing, XtCRowSpacing, XtRDimension, sizeof(Dimension),
31      offset(row_spacing), XtRImmediate, (XtPointer)0},
32     {XtNnAlloc, XtCNAlloc, XtRLong, sizeof(long),
33      offset(n_alloc), XtRImmediate, (XtPointer)0},
34     {XtNinternalWidth, XtCInternalWidth, XtRDimension, sizeof(Dimension),
35      offset(internal_width), XtRImmediate, (XtPointer)8},
36     {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension),
37      offset(internal_height), XtRImmediate, (XtPointer)4},
38     {XtNinternalItemWidth, XtCInternalItemWidth,
39      XtRDimension, sizeof(Dimension),
40      offset(internal_item_width), XtRImmediate, (XtPointer)8},
41     {XtNinternalItemHeight, XtCInternalItemHeight,
42      XtRDimension, sizeof(Dimension),
43      offset(internal_item_height), XtRImmediate, (XtPointer)1},
44     {XtNpixmapWidth, XtCPixmapWidth, XtRDimension, sizeof(Dimension),
45      offset(pixmap_width), XtRImmediate, (XtPointer)0},
46     {XtNpixmapHeight, XtCPixmapHeight, XtRDimension, sizeof(Dimension),
47      offset(pixmap_height), XtRImmediate, (XtPointer)0},
48     {XtNpixmapSpacing, XtCPixmapSpacing, XtRDimension, sizeof(Dimension),
49      offset(pixmap_spacing), XtRImmediate, (XtPointer)8},
50     {XtNpreferredLines, XtCPreferredLines, XtRDimension, sizeof(Dimension),
51      offset(preferred_lines), XtRImmediate, (XtPointer)12},
52     {XtNpreferredColumns, XtCPreferredColumns, XtRDimension, sizeof(Dimension),
53      offset(preferred_columns), XtRImmediate, (XtPointer)80},
54     {XtNdepthOne, XtCDepthOne, XtRBoolean, sizeof(Boolean),
55      offset(depth_one), XtRImmediate, (XtPointer)True},
56     {XtNatLeastOne, XtCAtLeastOne, XtRBoolean, sizeof(Boolean),
57      offset(at_least_one), XtRImmediate, (XtPointer)False},
58     {XtNatMostOne, XtCAtMostOne, XtRBoolean, sizeof(Boolean),
59      offset(at_most_one), XtRImmediate, (XtPointer)True},
60     {XtNallowDnd, XtCAllowDnd, XtRBoolean, sizeof(Boolean),
61      offset(allow_dnd), XtRImmediate, (XtPointer)False},
62     {XtNusePixmaps, XtCUsePixmaps, XtRBoolean, sizeof(Boolean),
63      offset(use_pixmaps), XtRImmediate, (XtPointer)False},
64     {XtNselectCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
65      offset(select_callback), XtRCallback, (XtPointer)NULL},
66     {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
67      offset(callback), XtRCallback, (XtPointer)NULL},
68     {XtNsecondCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
69      offset(second_callback), XtRCallback, (XtPointer)NULL},
70     {XtNdndCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
71      offset(dnd_callback), XtRCallback, (XtPointer)NULL},
72     {XtNdndCursor, XtCCursor, XtRCursor, sizeof(Cursor),
73      offset(dnd_cursor), XtRString, (XtPointer)"hand2"},
74     {XtNmarginUp, XtCMargin, XtRDimension, sizeof(Dimension),
75      offset(margin_up), XtRImmediate, (XtPointer)1},
76     {XtNmarginDown, XtCMargin, XtRDimension, sizeof(Dimension),
77      offset(margin_down), XtRImmediate, (XtPointer)1},
78     {XtNpageUp, XtCPage, XtRBoolean, sizeof(Boolean),
79      offset(page_up), XtRImmediate, (XtPointer)True},
80     {XtNpageDown, XtCPage, XtRBoolean, sizeof(Boolean),
81      offset(page_down), XtRImmediate, (XtPointer)True},
82 #undef offset
83 };
84 
85 static void	Initialize(Widget, Widget, ArgList, Cardinal*);
86 static void	Destroy(Widget);
87 static void	Redisplay(Widget, XEvent*, Region);
88 static void	Resize(Widget);
89 static void	Realize(Widget, XtValueMask*, XSetWindowAttributes*);
90 static Boolean	SetValues(Widget, Widget, Widget, ArgList, Cardinal*);
91 static void	SetVPos(ScrollableWidget, long);
92 static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*,
93 				      XtWidgetGeometry*);
94 
95 static void	notify(Widget, XEvent*, String*, Cardinal*);
96 static void	notify_second(Widget, XEvent*, String*, Cardinal*);
97 static void	select_action(Widget, XEvent*, String*, Cardinal*);
98 static void	drag_select(Widget, XEvent*, String*, Cardinal*);
99 static void	dnd_start(Widget, XEvent*, String*, Cardinal*);
100 static void	dnd_do(Widget, XEvent*, String*, Cardinal*);
101 static void	dnd_end(Widget, XEvent*, String*, Cardinal*);
102 
103 static XtActionsRec actions[] = {
104     {"notify",		notify},
105     {"notify-second",	notify_second},
106     {"select",		select_action},
107     {"drag-select",	drag_select},
108     {"dnd-start",	dnd_start},
109     {"dnd-do",		dnd_do},
110     {"dnd-end",		dnd_end},
111 };
112 
113 static char translations[] =
114 "<Btn1Down>:	select() \n"
115 "<Btn1Down>(2):	notify() \n";
116 
117 ScrListClassRec scrListClassRec = {
118     { /* core_class fields */
119 	(WidgetClass) &scrollableClassRec,	/* superclass		*/
120 	"ScrList",			/* class_name			*/
121 	sizeof(ScrListRec),		/* widget_size			*/
122 	NULL,				/* class_initialize		*/
123 	NULL,				/* class_part_initialize	*/
124 	FALSE,				/* class_inited			*/
125 	Initialize,			/* initialize			*/
126 	NULL,				/* initialize_hook		*/
127 	Realize,			/* realize			*/
128 	actions,			/* actions			*/
129 	XtNumber(actions),		/* num_actions			*/
130 	resources,			/* resources			*/
131 	XtNumber(resources),		/* num_resources		*/
132 	NULLQUARK,			/* xrm_class			*/
133 	TRUE,				/* compress_motion		*/
134 #if (XtSpecificationRelease < 4) /* It really needs GraphicsExpose ... */
135 	True,				/* compress_exposure		*/
136 #elif (XtSpecificationRelease < 6)
137 	XtExposeCompressMaximal | XtExposeGraphicsExposeMerged,
138 					/* compress_exposure		*/
139 #else
140 	XtExposeCompressMaximal | XtExposeGraphicsExposeMerged |
141 	XtExposeNoRegion,		/* compress_exposure		*/
142 #endif
143 	TRUE,				/* compress_enterleave		*/
144 	FALSE,				/* visible_interest		*/
145 	Destroy,			/* destroy			*/
146 	Resize,				/* resize			*/
147 	Redisplay,			/* expose			*/
148 	SetValues,			/* set_values			*/
149 	NULL,				/* set_values_hook		*/
150 	XtInheritSetValuesAlmost,	/* set_values_almost		*/
151 	NULL,				/* get_values_hook		*/
152 	NULL,				/* accept_focus			*/
153 	XtVersion,			/* version			*/
154 	NULL,				/* callback_private		*/
155 	translations,			/* tm_table			*/
156 	QueryGeometry,			/* query_geometry		*/
157 	XtInheritDisplayAccelerator,	/* display_accelerator		*/
158 	NULL				/* extension			*/
159     },
160     {					/* shadow fields		*/
161 	XtOffsetOf(ScrListRec, scrlist.highlight_pixel), /* pixel_offset */
162 	True,				/* use_arm_for_background	*/
163 	XtInheritAllocShadowColors,	/* alloc_shadow_colors		*/
164 	XtInheritAllocShadowPixmaps,	/* alloc_shadow_pixmaps		*/
165 	NULL,				/* alloc_arm_color		*/
166 	NULL,				/* alloc_arm_pixmap		*/
167 	XtInheritAllocGCs,		/* alloc_gcs			*/
168 	NULL,				/* extension			*/
169     },
170     {					/* scrollable fields		*/
171 	XtInheritScrollableSetPos,	/* set_hpos			*/
172 	SetVPos,			/* set_vpos			*/
173 	NULL,				/* suspend_hook			*/
174 	NULL,				/* extension			*/
175     },
176     {					/* scrlist fields		*/
177 	NULL				/* extension			*/
178     }
179 };
180 
181 WidgetClass scrListWidgetClass = (WidgetClass)&scrListClassRec;
182 
183 /*************************************************************************/
184 
grey90_default_proc(Widget w,int offset,XrmValue * to)185 static void grey90_default_proc(Widget w, int offset, XrmValue *to)
186 {
187     static Pixel	grey90;
188     Display		*disp = XtDisplay(w);
189 
190     grey90 = w->core.background_pixel;
191 
192     if (w->core.depth != 1) {
193 	XColor		col;
194 
195 	col.red = col.green = col.blue = 0xe5e5;
196 
197 	if (XAllocColor(disp, w->core.colormap, &col))
198 	    if ((col.red   == 0 &&
199 		 col.green == 0 &&
200 		 col.blue  == 0) ||
201 		(col.red   == 0xffff &&
202 		 col.green == 0xffff &&
203 		 col.blue  == 0xffff))
204 		XFreeColors(disp, w->core.colormap, &col.pixel, 1, 0);
205 	    else
206 		grey90 = col.pixel;
207     }
208 
209     to->addr = (XPointer)&grey90;
210 }
211 
init_gcs(ScrListWidget w)212 static void init_gcs(ScrListWidget w)
213 {
214     XGCValues	values;
215 
216     values.foreground = w->scrlist.foreground_pixel;
217     values.background = w->core.background_pixel;
218     values.font = w->scrlist.font->fid;
219     w->scrlist.default_gc =
220 	XtGetGC((Widget)w, GCForeground | GCBackground | GCFont, &values);
221     values.background = w->scrlist.highlight_pixel;
222     w->scrlist.selected_gc =
223 	XtGetGC((Widget)w, GCForeground | GCBackground, &values);
224     if (w->shadow.line_mode)
225 	w->scrlist.highlight_gc = (GC)0;
226     else {
227 	values.foreground = w->scrlist.highlight_pixel;
228 	w->scrlist.highlight_gc = XtGetGC((Widget)w, GCForeground, &values);
229     }
230 }
231 
free_gcs(ScrListWidget w)232 static void free_gcs(ScrListWidget w)
233 {
234     XtReleaseGC((Widget)w, w->scrlist.default_gc);
235     XtReleaseGC((Widget)w, w->scrlist.highlight_gc);
236     XtReleaseGC((Widget)w, w->scrlist.selected_gc);
237 }
238 
alloc_list(ScrListWidget w,long n_alloc)239 static void alloc_list(ScrListWidget w, long n_alloc)
240 {
241     long	n = w->scrlist.n_alloc;
242     int		use_pixmaps = w->scrlist.use_pixmaps;
243 
244     if (n_alloc < LINES(w))
245 	n_alloc = LINES(w);
246 
247     w->scrlist.strings =
248 	(char **)XtRealloc((char *)w->scrlist.strings,
249 			   n_alloc * sizeof(char*));
250     w->scrlist.selected =
251 	(Boolean *)XtRealloc((char *)w->scrlist.selected,
252 			     n_alloc * sizeof(Boolean));
253     if (use_pixmaps)
254 	w->scrlist.pixmaps =
255 	    (Pixmap *)XtRealloc((char *)w->scrlist.pixmaps,
256 				n_alloc * sizeof(Pixmap));
257     w->scrlist.n_alloc = n_alloc;
258 
259     while (n < n_alloc) {
260 	w->scrlist.strings[n] = NULL;
261 	w->scrlist.selected[n] = False;
262 	if (use_pixmaps)
263 	    w->scrlist.pixmaps[n] = None;
264 	n++;
265     }
266 }
267 
calc_shown(ScrListWidget w)268 static void calc_shown(ScrListWidget w)
269 {
270     int h, dh = 0;
271 
272     h  = w->core.height -
273 	2 * w->scrlist.internal_height +
274 	w->scrlist.row_spacing;
275     dh = w->scrlist.font->ascent + w->scrlist.font->descent;
276     if (w->scrlist.use_pixmaps && (int)w->scrlist.pixmap_height > dh)
277 	dh = w->scrlist.pixmap_height;
278     dh += w->scrlist.row_spacing +
279 	2 * (w->scrlist.internal_item_height + w->shadow.shadow_width);
280 
281     SHOWN(w) = (dh <= 0) ? 0 : h/dh;
282 }
283 
draw_list_item(ScrListWidget w,String * string,Pixmap * pixmap,Boolean * selected,int x,int y,int width,int height)284 static void draw_list_item(ScrListWidget w, String *string,
285 			   Pixmap *pixmap, Boolean *selected,
286 			   int x, int y, int width, int height)
287 {
288     Display	*disp = XtDisplay(w);
289     Window	win = XtWindow(w);
290     int		sw = w->shadow.shadow_width;
291 
292     if (*selected) {
293 	ShadowDrawShadows((ShadowWidget)w, (Position)x, (Position)y,
294 			  (Dimension)width, (Dimension)height, False);
295 	if (!w->shadow.line_mode)
296 	    XFillRectangle(disp, win, w->scrlist.highlight_gc,
297 			   x + sw, y + sw, width - 2*sw, height - 2*sw);
298     }
299 
300     x += w->scrlist.internal_item_width + w->shadow.shadow_width;
301     if (pixmap) {
302 	if (*pixmap != None) {
303 	    if (w->scrlist.depth_one)
304 		XCopyPlane(disp, *pixmap, win,
305 			   *selected ? w->scrlist.selected_gc :
306 			   w->scrlist.default_gc,
307 			   0, 0, w->scrlist.pixmap_width,
308 			   w->scrlist.pixmap_height, x,
309 			   y + (height - (int)w->scrlist.pixmap_height) / 2,
310 			   1);
311 	    else
312 		XCopyArea(disp, *pixmap, win, w->scrlist.default_gc, 0, 0,
313 			  w->scrlist.pixmap_width, w->scrlist.pixmap_height,
314 			  x, y + (height - (int)w->scrlist.pixmap_height) / 2);
315 	}
316 	x += w->scrlist.pixmap_width + w->scrlist.pixmap_spacing;
317     }
318 
319     if (string && *string != (String)NULL)
320 	XDrawString(disp, win, w->scrlist.default_gc, x,
321 		    y + (height + w->scrlist.font->ascent -
322 			 w->scrlist.font->descent)/2,
323 		    *string, strlen(*string));
324 }
325 
redraw_items(ScrListWidget w,long start,long stop,Boolean clear)326 static void redraw_items(ScrListWidget w, long start, long stop, Boolean clear)
327 {
328     String	*string = w->scrlist.strings;
329     Pixmap	*pixmap = w->scrlist.pixmaps;
330     int		x = w->scrlist.internal_width;
331     int		width = w->core.width - 2 * w->scrlist.internal_width;
332     int		y = w->scrlist.internal_height;
333     int		height = 0;
334 
335     if (width <= 0 || LINES(w) == 0)
336 	return;
337     if (start < FIRST(w))
338 	start = FIRST(w);
339     if (stop >= FIRST(w) + SHOWN(w))
340 	stop = FIRST(w) + SHOWN(w) - 1;
341     if (stop >= LINES(w) - 1)
342 	stop = LINES(w) - 1;
343     if (start > stop)
344 	return;
345 
346     if (string)
347 	string += start;
348     if (pixmap)
349 	pixmap += start;
350     if (string)
351 	height = w->scrlist.font->ascent + w->scrlist.font->descent;
352     if (pixmap && (int)w->scrlist.pixmap_height > height)
353 	height = w->scrlist.pixmap_height;
354     height += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width);
355     if (width <= 0 || height == 0)
356 	return;
357     y += (start - FIRST(w)) * (height + w->scrlist.row_spacing);
358 
359     if (clear) {
360 	int	h = (stop - start + 1) * (height + w->scrlist.row_spacing);
361 
362 	XClearArea(XtDisplay(w), XtWindow(w), 0, y,
363 		   w->core.width, h, False);
364     }
365 
366     while (start <= stop) {
367 	draw_list_item(w, string, pixmap,
368 		       &(w->scrlist.selected[start]),
369 		       x, y, width, height);
370 	y += height + w->scrlist.row_spacing;
371 	if (string)
372 	    string++;
373 	if (pixmap)
374 	    pixmap++;
375 	start++;
376     }
377 }
378 
preferred_width(ScrListWidget w)379 static Dimension preferred_width(ScrListWidget w)
380 {
381     int	ret;
382 
383     ret = w->scrlist.max_width;
384     if (ret <= 0)
385 	ret = w->scrlist.preferred_columns * w->scrlist.font->max_bounds.width;
386 
387     ret += 2 * (w->shadow.shadow_width + w->scrlist.internal_width +
388 		w->scrlist.internal_item_width);
389 
390     if (w->scrlist.use_pixmaps)
391 	ret += w->scrlist.pixmap_width + w->scrlist.pixmap_spacing;
392 
393     return ret;
394 }
395 
preferred_height(ScrListWidget w)396 static Dimension preferred_height(ScrListWidget w)
397 {
398     long	height = 0;
399     long	n = w->scrlist.preferred_lines;
400 
401     if (w->scrlist.strings || !w->scrlist.pixmaps)
402 	height = w->scrlist.font->ascent + w->scrlist.font->descent;
403     if (w->scrlist.pixmaps && height < (int)w->scrlist.pixmap_height)
404 	height = w->scrlist.pixmap_height;
405 
406     height += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width +
407 		   w->scrlist.row_spacing);
408     height *= n;
409     height += 2 * w->scrlist.internal_height - w->scrlist.row_spacing;
410 
411     if (height > 32767)
412 	height = 32767;
413     else if (height <= 0)
414 	height = 1;
415 
416     return height;
417 }
418 
make_visible(ScrListWidget w,long i,int may_page)419 static void make_visible(ScrListWidget w, long i, int may_page)
420 {
421     long	first = FIRST(w);
422 
423     if (LINES(w) <= 0)
424 	return;
425 
426     if (i >= LINES(w))
427 	i = LINES(w) - 1;
428     if (i < 0)
429 	i = 0;
430 
431     if (i < FIRST(w) + w->scrlist.margin_up)
432 	if (may_page && w->scrlist.page_up)
433 	    first = i - SHOWN(w) + w->scrlist.margin_down + 1;
434 	else
435 	    first = i - w->scrlist.margin_up;
436     else if (i >= FIRST(w) + SHOWN(w) - w->scrlist.margin_down)
437 	if (may_page && w->scrlist.page_down)
438 	    first = i - w->scrlist.margin_up;
439 	else
440 	    first = i - SHOWN(w) + w->scrlist.margin_down + 1;
441 
442     if (i < first)
443 	i = first;
444     else if (i >= first + SHOWN(w))
445 	first = i - SHOWN(w) + 1;
446     if (first < 0)
447 	first = 0;
448 
449     if (first != FIRST(w))
450 	ScrollableSetVPos((Widget)w, first);
451 }
452 
453 /*************************************************************************/
454 
event_to_index(ScrListWidget w,XEvent * event)455 static long event_to_index(ScrListWidget w, XEvent *event)
456 {
457     int		ex, ey;
458     int		y, h = 0;
459     long	i, n;
460 
461     if (!get_event_xy(event, &ex, &ey)) {
462 	XBell(XtDisplay(w), 0);
463 	return -1;
464     }
465 
466     if (LINES(w) <= 0 || ex < (int)w->scrlist.internal_width ||
467 	ex > (int)(w->core.width - w->scrlist.internal_width))
468 	return -1;
469 
470 
471     y = w->scrlist.internal_height;
472     if (w->scrlist.strings)
473 	h = w->scrlist.font->ascent + w->scrlist.font->descent;
474     if (w->scrlist.pixmaps && h < (int)w->scrlist.pixmap_height)
475 	h = w->scrlist.pixmap_height;
476     h += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width);
477     if (ey < y)
478 	return FIRST(w) > 0 ? FIRST(w) - 1 : 0;
479 
480     i = FIRST(w);
481     n = FIRST(w) + SHOWN(w);
482     if (n > LINES(w))
483 	n = LINES(w);
484 
485     while (i < n) {
486 	if (y <= ey && ey < y + h)
487 	    return i;
488 	y += h + w->scrlist.row_spacing;
489 	i++;
490     }
491 
492     n = FIRST(w) + SHOWN(w);
493     if (n >= LINES(w))
494 	n = LINES(w) - 1;
495 
496     return n;
497 }
498 
select_action(Widget gw,XEvent * event,String * params,Cardinal * no_params)499 static void select_action(Widget	 gw,
500 			  XEvent	*event,
501 			  String	*params,
502 			  Cardinal	*no_params)
503 {
504     ScrListWidget	w = (ScrListWidget)gw;
505     XtCallbackList	c_list = w->scrlist.select_callback;
506     long		i;
507 
508     if (!w->scrlist.active)
509 	return;
510 
511     i = event_to_index(w, event);
512     if (i < 0 || i >= LINES(w))
513 	return;
514 
515     ScrListSetSelected((Widget)w, i, !w->scrlist.selected[i]);
516 
517     if (c_list)
518 	XtCallCallbackList((Widget)w, c_list, (XtPointer)i);
519 }
520 
notify(Widget gw,XEvent * event,String * params,Cardinal * no_params)521 static void notify(Widget	 gw,
522 		   XEvent	*event,
523 		   String	*params,
524 		   Cardinal	*no_params)
525 {
526     ScrListWidget	w = (ScrListWidget)gw;
527     XtCallbackList	c_list = w->scrlist.callback;
528     long		i;
529 
530     if (!w->scrlist.active || !c_list)
531 	return;
532 
533     i = event_to_index(w, event);
534     if (i < 0 || i >= LINES(w))
535 	return;
536 
537     XtCallCallbackList((Widget)w, c_list, (XtPointer)i);
538 }
539 
notify_second(Widget gw,XEvent * event,String * params,Cardinal * no_params)540 static void notify_second(Widget	 gw,
541 			  XEvent	*event,
542 			  String	*params,
543 			  Cardinal	*no_params)
544 {
545     ScrListWidget	w = (ScrListWidget)gw;
546     XtCallbackList	c_list = w->scrlist.second_callback;
547     long		i;
548 
549     if (!w->scrlist.active || !c_list)
550 	return;
551 
552     i = event_to_index(w, event);
553     if (i < 0 || i >= LINES(w))
554 	return;
555 
556     XtCallCallbackList((Widget)w, c_list, (XtPointer)i);
557 }
558 
drag_select(Widget gw,XEvent * event,String * params,Cardinal * no_params)559 static void drag_select(Widget		 gw,
560 			XEvent		*event,
561 			String		*params,
562 			Cardinal	*no_params)
563 {
564     ScrListWidget	w = (ScrListWidget)gw;
565     XtCallbackList	c_list = w->scrlist.select_callback;
566     long		i;
567 
568     if (!w->scrlist.active)
569 	return;
570 
571     i = event_to_index(w, event);
572     if (i < 0 || i >= LINES(w))
573 	return;
574 
575     if (!w->scrlist.selected[i]) {
576 	ScrListSetSelected((Widget)w, i, True);
577 	if (c_list)
578 	    XtCallCallbackList((Widget)w, c_list, (XtPointer)i);
579     }
580 }
581 
dnd_start(Widget gw,XEvent * event,String * params,Cardinal * no_params)582 static void dnd_start(Widget	 gw,
583 		      XEvent	*event,
584 		      String	*params,
585 		      Cardinal	*no_params)
586 {
587     ScrListWidget	w = (ScrListWidget)gw;
588     long		i = event_to_index(w, event);
589 
590     if (!w->scrlist.allow_dnd || i < 0) {
591 	w->scrlist.dnd_start = -1;
592 	XBell(XtDisplay(w), 0);
593 	return;
594     }
595 
596     w->scrlist.dnd_start = i;
597     XDefineCursor(XtDisplay(w), XtWindow(w), w->scrlist.dnd_cursor);
598 }
599 
dnd_do(Widget gw,XEvent * event,String * params,Cardinal * no_params)600 static void dnd_do(Widget gw, XEvent *event,
601 		   String *params, Cardinal *no_params)
602 {
603     ScrListWidget	w = (ScrListWidget)gw;
604     long		i = event_to_index(w, event);
605 
606     if (w->scrlist.dnd_start < 0) {
607 	XBell(XtDisplay(w), 0);
608 	return;
609     }
610 
611     if (LINES(w) <= 0)
612 	return;
613 
614     if (i >= LINES(w))
615 	i = LINES(w) - 1;
616     if (i < 0)
617 	i = 0;
618 
619     make_visible(w, i, False);
620 }
621 
dnd_end(Widget gw,XEvent * event,String * params,Cardinal * no_params)622 static void dnd_end(Widget	 gw,
623 		    XEvent	*event,
624 		    String	*params,
625 		    Cardinal	*no_params)
626 {
627     ScrListWidget	w = (ScrListWidget)gw;
628     XtCallbackList	c_list = w->scrlist.dnd_callback;
629     long		index[3];
630     long		i;
631     int			use_pixmaps = w->scrlist.use_pixmaps;
632     char		*tempstr = NULL;
633     Pixmap		temppix = None;
634     Boolean		tempsel = False;
635 
636     XDefineCursor(XtDisplay(w), XtWindow(w), None);
637 
638     index[0] = w->scrlist.dnd_start;
639     w->scrlist.dnd_start = -1;
640     index[1] = event_to_index(w, event);
641     if (!w->scrlist.allow_dnd || index[0] == index[1] ||
642 	index[0] < 0 || index[0] >= LINES(w) ||
643 	index[1] < 0 || index[1] >= LINES(w)) {
644 	XBell(XtDisplay((Widget)w), 0);
645 	return;
646     }
647 
648     if (c_list) {
649 	index[2] = False;
650 	XtCallCallbackList((Widget)w, c_list, (XtPointer)index);
651 	if (!index[2])
652 	    return;
653     }
654 
655     tempstr = w->scrlist.strings[index[0]];
656     if (use_pixmaps)
657 	temppix = w->scrlist.pixmaps[index[0]];
658     tempsel = w->scrlist.selected[index[0]];
659 
660     if (index[0] < index[1])
661 	for (i = index[0] ; i < index[1] ; i++) {
662 	    w->scrlist.strings[i] = w->scrlist.strings[i + 1];
663 	    if (use_pixmaps)
664 		w->scrlist.pixmaps[i] = w->scrlist.pixmaps[i + 1];
665 	    w->scrlist.selected[i] = w->scrlist.selected[i + 1];
666 	}
667     else
668 	for (i = index[0] ; i > index[1] ; i--) {
669 	    w->scrlist.strings[i] = w->scrlist.strings[i - 1];
670 	    if (use_pixmaps)
671 		w->scrlist.pixmaps[i] = w->scrlist.pixmaps[i - 1];
672 	    w->scrlist.selected[i] = w->scrlist.selected[i - 1];
673 	}
674 
675     w->scrlist.strings[index[1]] = tempstr;
676     if (use_pixmaps)
677 	w->scrlist.pixmaps[index[1]] = temppix;
678     w->scrlist.selected[index[1]] = tempsel;
679 
680     if (index[0] < index[1])
681 	redraw_items(w, index[0], index[1], True);
682     else
683 	redraw_items(w, index[1], index[0], True);
684 }
685 
686 /*************************************************************************/
687 
Initialize(Widget grequest,Widget gnew,ArgList args,Cardinal * num_args)688 static void Initialize(Widget grequest, Widget gnew,
689 		       ArgList args, Cardinal *num_args)
690 {
691     ScrListWidget	new = (ScrListWidget)gnew;
692     long		n_alloc = new->scrlist.n_alloc;
693 
694     new->scrlist.active = True;
695     new->scrlist.strings = NULL;
696     new->scrlist.pixmaps = NULL;
697     new->scrlist.selected = NULL;
698     new->scrlist.n_sel = 0;
699     new->scrlist.n_alloc = 0;
700     new->scrlist.max_width = 0;
701 
702     init_gcs(new);
703     calc_shown(new);
704     alloc_list(new, n_alloc > 0 ? n_alloc : 1);
705     new->scrlist.dnd_start = -1;
706     if (new->scrlist.at_least_one) {
707 	new->scrlist.selected[0] = True;
708 	new->scrlist.n_sel = 1;
709     }
710     if (new->core.width == 0)
711 	new->core.width = preferred_width(new);
712     if (new->core.height == 0)
713 	new->core.height = preferred_height(new);
714 }
715 
Destroy(Widget gw)716 static void Destroy(Widget gw)
717 {
718     ScrListWidget	w = (ScrListWidget)gw;
719 
720     XtFree(w->scrlist.selected);
721     free_gcs(w);
722 }
723 
Redisplay(Widget gw,XEvent * event,Region region)724 static void Redisplay(Widget gw, XEvent *event, Region region)
725 {
726     ScrListWidget	w = (ScrListWidget)gw;
727     long		start, stop;
728     int			height = 0;
729 
730     if (!XtIsRealized((Widget)w) || LINES(w) == 0)
731 	return;
732 
733     if (w->scrlist.strings)
734 	height = w->scrlist.font->ascent + w->scrlist.font->descent;
735     if (w->scrlist.use_pixmaps && (int)w->scrlist.pixmap_height > height)
736 	height = w->scrlist.pixmap_height;
737     height += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width) +
738 	w->scrlist.row_spacing;
739     if (height == 0)
740 	return;
741 
742     if (event && event->type == Expose) {
743 	int	y = w->scrlist.internal_height;
744 	int	y1 = event->xexpose.y - y;
745 	int	y2 = y1 + event->xexpose.height;
746 
747 	start = FIRST(w) + y1/height;
748 	stop = FIRST(w) + y2/height;
749     } else if (event && event->type == GraphicsExpose) {
750 	int	y = w->scrlist.internal_height;
751 	int	y1 = event->xgraphicsexpose.y - y;
752 	int	y2 = y1 + event->xgraphicsexpose.height;
753 
754 	start = FIRST(w) + y1/height;
755 	stop = FIRST(w) + y2/height;
756     } else {
757 	start = FIRST(w);
758 	stop = start + SHOWN(w) - 1;
759     }
760 
761     redraw_items(w, start, stop, False);
762 }
763 
Resize(Widget gw)764 static void Resize(Widget gw)
765 {
766     ScrListWidget	w = (ScrListWidget)gw;
767 
768     ScrollableHFromGeometry((ScrollableWidget)w);
769     calc_shown(w);
770     ScrollableFitHBar((ScrollableWidget)w);
771     ScrollableFitVBar((ScrollableWidget)w);
772 }
773 
Realize(Widget gw,XtValueMask * mask,XSetWindowAttributes * attributes)774 static void Realize(Widget		   gw,
775 		    XtValueMask		  *mask,
776 		    XSetWindowAttributes  *attributes)
777 {
778     ScrListWidget	w = (ScrListWidget)gw;
779 
780     ScrollableHFromGeometry((ScrollableWidget)w);
781     calc_shown(w);
782     scrollableWidgetClass->core_class.realize((Widget)w, mask, attributes);
783 }
784 
SetValues(Widget gcurrent,Widget grequest,Widget gnew,ArgList args,Cardinal * num_args)785 static Boolean SetValues(Widget		 gcurrent,
786 			 Widget		 grequest,
787 			 Widget		 gnew,
788 			 ArgList	 args,
789 			 Cardinal	*num_args)
790 {
791     ScrListWidget	new = (ScrListWidget)gnew;
792     ScrListWidget	current = (ScrListWidget)gcurrent;
793     Boolean		redisplay = False;
794 
795     new->scrlist.use_pixmaps = current->scrlist.use_pixmaps;
796 
797     if (new->core.background_pixel    != current->core.background_pixel    ||
798 	new->scrlist.foreground_pixel != current->scrlist.foreground_pixel ||
799 	new->scrlist.highlight_pixel  != current->scrlist.highlight_pixel  ||
800 	new->scrlist.font             != current->scrlist.font) {
801 	free_gcs(current);
802 	init_gcs(new);
803 	redisplay = True;
804     }
805 
806     if (new->scrlist.n_alloc != current->scrlist.n_alloc) {
807 	long	n_alloc = new->scrlist.n_alloc;
808 
809 	new->scrlist.n_alloc = current->scrlist.n_alloc;
810 	alloc_list(new, n_alloc);
811     }
812 
813     if (new->scrlist.row_spacing     != current->scrlist.row_spacing     ||
814 	new->shadow.shadow_width     != current->shadow.shadow_width     ||
815 	new->scrlist.internal_width  != current->scrlist.internal_width  ||
816 	new->scrlist.internal_height != current->scrlist.internal_height ||
817 	new->scrlist.pixmap_width    != current->scrlist.pixmap_width    ||
818 	new->scrlist.pixmap_height   != current->scrlist.pixmap_height   ||
819 	new->scrlist.pixmap_spacing  != current->scrlist.pixmap_spacing  ||
820 	new->scrlist.depth_one       != current->scrlist.depth_one)
821 	redisplay = True;
822 
823     if (new->scrlist.internal_item_width !=
824 	current->scrlist.internal_item_width ||
825 	new->scrlist.internal_item_height !=
826 	current->scrlist.internal_item_height)
827 	redisplay = True;
828 
829     if (new->scrlist.at_least_one != current->scrlist.at_least_one &&
830 	new->scrlist.n_sel == 0 && LINES(new) > 0) {
831 	new->scrlist.selected[0] = True;
832 	redisplay = True;
833     }
834 
835     if (new->scrlist.at_most_one != current->scrlist.at_most_one &&
836 	new->scrlist.n_sel > 1) {
837 	Boolean	*loop = new->scrlist.selected;
838 	int	n = LINES(new);
839 
840 	while (n-- > 0)
841 	    if (*loop++)
842 		break;
843 
844 	if (n > 0)
845 	    memset(loop, 0, n);
846     }
847 
848     if (new->scrlist.preferred_lines != current->scrlist.preferred_lines)
849 	(void)XtMakeResizeRequest((Widget)new, preferred_width(new),
850 				  preferred_height(new), NULL, NULL);
851 
852     if (redisplay)
853 	calc_shown(new);
854 
855     return redisplay;
856 }
857 
SetVPos(ScrollableWidget gw,long pos_y)858 static void SetVPos(ScrollableWidget gw, long pos_y)
859 {
860     ScrListWidget	w = (ScrListWidget)gw;
861     Display		*disp = XtDisplay(w);
862     Window		win = XtWindow(w);
863     long		old = FIRST(w);
864     long		diff;
865     long		n = SHOWN(w);
866     int			y = w->scrlist.internal_height;
867     int			dy = 0;
868 
869     if (LINES(w) <= 0)
870 	return;
871 
872     if (pos_y > LINES(w) - SHOWN(w))
873 	pos_y = LINES(w) - SHOWN(w);
874     if (pos_y < 0)
875 	pos_y = 0;
876 
877     diff = pos_y - old;
878     FIRST(w) = pos_y;
879     if (w->scrlist.strings)
880 	dy = w->scrlist.font->ascent + w->scrlist.font->descent;
881     if (w->scrlist.pixmaps && (int)w->scrlist.pixmap_height > dy)
882 	dy = w->scrlist.pixmap_height;
883     dy += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width) +
884 	w->scrlist.row_spacing;
885     if (dy == 0 || diff == 0 || !XtIsRealized((Widget)w))
886 	return;
887 
888     if (diff <= -n || diff >= n) {
889 	XClearWindow(disp, win);
890 	redraw_items(w, pos_y, pos_y + n - 1, False);
891     } else if (diff < 0) {
892 	XCopyArea(disp, win, win, w->scrlist.default_gc,
893 		  0, y,
894 		  w->core.width, (n + diff) * dy,
895 		  0, y - diff * dy);
896 	XClearArea(disp, win, 0, 0, 0, y - diff * dy, False);
897 	redraw_items(w, pos_y, pos_y - diff - 1, False);
898     } else if (diff > 0) {
899 	XCopyArea(disp, win, win, w->scrlist.default_gc,
900 		  0, y + diff * dy,
901 		  w->core.width, (n - diff) * dy,
902 		  0, y);
903 	XClearArea(disp, win, 0, y + (n - diff) * dy, 0, 0, False);
904 	redraw_items(w, pos_y + n - diff, pos_y + n - 1, False);
905     }
906 }
907 
QueryGeometry(Widget gw,XtWidgetGeometry * intended,XtWidgetGeometry * preferred)908 static XtGeometryResult QueryGeometry(Widget gw,
909 				      XtWidgetGeometry *intended,
910 				      XtWidgetGeometry *preferred)
911 {
912     ScrListWidget	w = (ScrListWidget)gw;
913     Dimension		intended_width;
914     Dimension		intended_height;
915 
916     preferred->request_mode = CWHeight | CWWidth;
917     preferred->height = preferred_height(w);
918     preferred->width = preferred_width(w);
919 
920     if (intended->request_mode & CWWidth)
921 	intended_width = intended->width;
922     else
923 	intended_width = w->core.width;
924     if (intended->request_mode & CWHeight)
925 	intended_height = intended->height;
926     else
927 	intended_height = w->core.height;
928 
929     if (intended_width == preferred->width &&
930 	intended_height == preferred->height)
931 	return XtGeometryYes;
932     if (preferred->width == w->core.width &&
933 	preferred->height == w->core.height)
934 	return XtGeometryNo;
935     return XtGeometryAlmost;
936 }
937 
938 /*************************************************************************/
939 
ScrListClearLines(Widget gw)940 void ScrListClearLines(Widget gw)
941 {
942     ScrListWidget	w = (ScrListWidget)gw;
943     long		i;
944 
945     for (i = 0 ; i < LINES(w) ; i++) {
946 	XtFree(w->scrlist.strings[i]);
947 	w->scrlist.strings[i] = NULL;
948 	w->scrlist.selected[i] = False;
949 	if (w->scrlist.pixmaps)
950 	    w->scrlist.pixmaps[i] = None;
951     }
952 
953     XtFree((char *)w->scrlist.strings);
954     w->scrlist.strings = NULL;
955     XtFree((char *)w->scrlist.pixmaps);
956     w->scrlist.pixmaps = NULL;
957     XtFree((char *)w->scrlist.selected);
958     w->scrlist.selected = NULL;
959     w->scrlist.n_alloc = 0;
960 
961     LINES(w) = 0;
962     FIRST(w) = 0;
963     w->scrlist.dnd_start = -1;
964     w->scrlist.n_sel = 0;
965     w->scrlist.max_width = 0;
966 
967     if (XtIsRealized((Widget)w))
968 	XClearWindow(XtDisplay(w), XtWindow(w));
969 
970     ScrollableFitVBar((ScrollableWidget)w);
971 }
972 
ScrListAddLine(Widget gw,char * string,Pixmap pixmap)973 long ScrListAddLine(Widget gw, char *string, Pixmap pixmap)
974 {
975     ScrListWidget	w = (ScrListWidget)gw;
976     long		n = LINES(w)++;
977     int			len, width;
978 
979     if (n + 3 > w->scrlist.n_alloc)
980 	alloc_list(w, 2 * (w->scrlist.n_alloc + 1));
981     len = strlen(string);
982     w->scrlist.strings[n] = strcpy(XtMalloc(len + 1), string);
983     if (w->scrlist.use_pixmaps)
984 	w->scrlist.pixmaps[n] = pixmap;
985     width = XTextWidth(w->scrlist.font, string, len);
986     if (width > w->scrlist.max_width)
987 	w->scrlist.max_width = width;
988 
989     if (n == 0 && w->scrlist.at_least_one) {
990 	w->scrlist.selected[0] = True;
991 	w->scrlist.n_sel = 1;
992     }
993 
994     if (XtIsRealized((Widget)w) &&
995 	n >= FIRST(w) && n < FIRST(w) + SHOWN(w))
996 	redraw_items(w, n, n, False);
997 
998     if (!w->scrollable.suspended)
999 	ScrollableFitVBar((ScrollableWidget)w);
1000 
1001     return n;
1002 }
1003 
ScrListSetLine(Widget gw,long row,char * string,Pixmap pixmap)1004 void ScrListSetLine(Widget gw, long row, char *string, Pixmap pixmap)
1005 {
1006     ScrListWidget	w = (ScrListWidget)gw;
1007     int			len, width;
1008 
1009     if (row < 0 || row >= LINES(w))
1010 	return;
1011 
1012     XtFree(w->scrlist.strings[row]);
1013     len = strlen(string);
1014     w->scrlist.strings[row] = strcpy(XtMalloc(len + 1), string);
1015     if (w->scrlist.use_pixmaps)
1016 	w->scrlist.pixmaps[row] = pixmap;
1017     width = XTextWidth(w->scrlist.font, string, len);
1018     if (width > w->scrlist.max_width)
1019 	w->scrlist.max_width = width;
1020 
1021     if (XtIsRealized((Widget)w))
1022 	redraw_items(w, row, row, True);
1023 }
1024 
ScrListDeleteLine(Widget gw,long row)1025 void ScrListDeleteLine(Widget gw, long row)
1026 {
1027     ScrListWidget	w = (ScrListWidget)gw;
1028     int			use_pixmaps = w->scrlist.use_pixmaps;
1029     int			was_sel;
1030     long		i, n;
1031 
1032     if (row < 0 || row >= LINES(w))
1033 	return;
1034 
1035     was_sel = w->scrlist.selected[row];
1036     if (was_sel)
1037 	w->scrlist.n_sel--;
1038     XtFree(w->scrlist.strings[row]);
1039     n = --LINES(w);
1040     for (i = row ; i < n ; i++) {
1041 	w->scrlist.strings[i] = w->scrlist.strings[i + 1];
1042 	if (use_pixmaps)
1043 	    w->scrlist.pixmaps[i] = w->scrlist.pixmaps[i + 1];
1044 	w->scrlist.selected[i] = w->scrlist.selected[i + 1];
1045     }
1046     w->scrlist.strings[n] = NULL;
1047 
1048     if (was_sel && w->scrlist.at_least_one &&
1049 	w->scrlist.n_sel == 0 && LINES(w) > 0) {
1050 	if (row < LINES(w) - 1)
1051 	    w->scrlist.selected[row + 1] = True;
1052 	else
1053 	    w->scrlist.selected[LINES(w) - 1] = True;
1054 	w->scrlist.n_sel = 1;
1055     }
1056 
1057     if (FIRST(w) >= LINES(w) && LINES(w) > 0)
1058 	FIRST(w)--;
1059 
1060     if (XtIsRealized((Widget)w)) {
1061 	XClearWindow(XtDisplay(w), XtWindow(w));
1062 	Redisplay((Widget)w, NULL, NULL);
1063     }
1064 
1065     if (!w->scrollable.suspended)
1066 	ScrollableFitVBar((ScrollableWidget)w);
1067 }
1068 
ScrListSetSelected(Widget gw,long i,int select)1069 void ScrListSetSelected(Widget gw, long i, int select)
1070 {
1071     ScrListWidget	w = (ScrListWidget)gw;
1072 
1073     if (i < 0 || i >= LINES(w))
1074 	return;
1075 
1076     select = !!select;
1077     if (w->scrlist.selected[i] == select)
1078 	return;
1079 
1080     if (select) {
1081 	if (w->scrlist.at_most_one && w->scrlist.n_sel > 0) {
1082 	    long	j;
1083 	    Boolean	*sel = w->scrlist.selected;
1084 
1085 	    for (j = 0 ; j < LINES(w) ; j++)
1086 		if (sel[j]) {
1087 		    sel[j] = False;
1088 		    redraw_items(w, j, j, True);
1089 		}
1090 	    w->scrlist.n_sel = 0;
1091 	}
1092 	w->scrlist.n_sel++;
1093 	w->scrlist.selected[i] = True;
1094 	redraw_items(w, i, i, True);
1095     } else if (!w->scrlist.at_least_one || w->scrlist.n_sel > 1) {
1096 	w->scrlist.n_sel--;
1097 	w->scrlist.selected[i] = False;
1098 	redraw_items(w, i, i, True);
1099     }
1100 }
1101 
ScrListMakeVisible(Widget gw,long i)1102 void ScrListMakeVisible(Widget gw, long i)
1103 {
1104     make_visible((ScrListWidget)gw, i, True);
1105 }
1106 
ScrListGetSelected(Widget gw,long i)1107 int ScrListGetSelected(Widget gw, long i)
1108 {
1109     ScrListWidget w = (ScrListWidget)gw;
1110 
1111     return i >= 0 && i < LINES(w) && w->scrlist.selected[i];
1112 }
1113 
ScrListGetFirstSelected(Widget gw)1114 long ScrListGetFirstSelected(Widget gw)
1115 {
1116     return ScrListGetNextSelected(gw, -1);
1117 }
1118 
ScrListGetNextSelected(Widget gw,long i)1119 long ScrListGetNextSelected(Widget gw, long i)
1120 {
1121     ScrListWidget	w = (ScrListWidget)gw;
1122     Boolean		*loop = w->scrlist.selected;
1123     long		n = LINES(w);
1124 
1125     i++;
1126     if (i < 0)
1127 	return -1;
1128     loop += i;
1129     while (i < n) {
1130 	if (*loop)
1131 	    return i;
1132 	loop++;
1133 	i++;
1134     }
1135 
1136     return -1;
1137 }
1138 
ScrListGetString(Widget gw,long row)1139 char *ScrListGetString(Widget gw, long row)
1140 {
1141     ScrListWidget	w = (ScrListWidget)gw;
1142 
1143     if (row < 0 || row >= LINES(w))
1144 	return NULL;
1145 
1146     return w->scrlist.strings[row];
1147 }
1148 
ScrListGetPixmap(Widget gw,long row)1149 Pixmap ScrListGetPixmap(Widget gw, long row)
1150 {
1151     ScrListWidget	w = (ScrListWidget)gw;
1152 
1153     if (!w->scrlist.use_pixmaps ||
1154 	row < 0 || row >= LINES(w))
1155 	return None;
1156 
1157     return w->scrlist.pixmaps[row];
1158 }
1159 
ScrListPurgePixmap(Widget gw,Pixmap pixmap)1160 void ScrListPurgePixmap(Widget gw, Pixmap pixmap)
1161 {
1162     ScrListWidget	w = (ScrListWidget)gw;
1163     long		i;
1164 
1165     if (!w->scrlist.use_pixmaps)
1166 	return;
1167 
1168     for (i = 0 ; i < LINES(w) ; i++) {
1169 	if (w->scrlist.pixmaps[i] == pixmap) {
1170 	    w->scrlist.pixmaps[i] = None;
1171 	    redraw_items(w, i, i, True);
1172 	}
1173     }
1174 }
1175 
ScrListEventToIndex(Widget gw,XEvent * event)1176 long ScrListEventToIndex(Widget gw, XEvent *event)
1177 {
1178     ScrListWidget	w = (ScrListWidget)gw;
1179 
1180     return event_to_index(w, event);
1181 }
1182 
ScrListSetActive(Widget gw,int active)1183 void ScrListSetActive(Widget gw, int active)
1184 {
1185     ScrListWidget	w = (ScrListWidget)gw;
1186 
1187     w->scrlist.active = active;
1188 }
1189