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