Initial commit, 3-52-19 alpha
[cls.git] / src / c / X11listitem.c
blob60613ea65dbd4ad59995e759b6618260055fb615
1 /* X11listitem - list items for X11 dialogs */
2 /* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney */
3 /* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz */
4 /* You may give out copies of this software; for conditions see the */
5 /* file COPYING included with this distribution. */
7 /***********************************************************************/
8 /** **/
9 /** General Includes and Definitions **/
10 /** **/
11 /***********************************************************************/
13 #include "dialogs.h"
15 extern Display *StX11Display();
16 extern Point DialogStringSize();
17 extern LVAL StX11ItemObject();
18 extern char *checkstring();
20 typedef struct {
21 unsigned long fore, back;
22 } ColorPair;
24 extern LVAL s_internals, s_window_id;
26 /* forward declarations */
27 LOCAL VOID draw_field_content _((Display *dpy, Window win,
28 char *text, int reversed));
29 LOCAL VOID draw_fields _((LVAL item));
31 /***********************************************************************/
32 /** **/
33 /** Global Variables **/
34 /** **/
35 /***********************************************************************/
37 /* configuration parameters - should be set using the defaults database */
38 extern XFontStruct *DialogFont;
39 extern unsigned long DialogBorderColor;
40 extern ColorPair DialogC;
41 extern unsigned int list_border_width;
42 extern int min_button_width;
44 extern GC DialogGC, DialogRGC;
46 extern XContext ObjectContext, ListFieldContext;
48 /***********************************************************************/
49 /** **/
50 /** List Items **/
51 /** **/
52 /***********************************************************************/
54 # define LIST_TEXT_PATTERN "MMMMMMMMMMMMMMM"
55 # define LIST_LEAD 5
56 # define LIST_PAD 15
57 # define MAX_LIST_ROWS 12
58 # define SCROLL_WIDTH 20
59 # define DOUBLE_CLICK_TIME 1000
60 # define DOUBLE_CLICK_MOVE 10
62 /***********************************************************************/
63 /** **/
64 /** Double Click Detection **/
65 /** **/
66 /***********************************************************************/
68 static int click_x, click_y, click_inited = FALSE;
69 static Time click_time;
70 Window click_win;
72 static int is_double_click(report)
73 XEvent *report;
75 int result, x, y, del_x, del_y, del_time;
76 Time time;
77 Window win;
79 x = report->xbutton.x;
80 y = report->xbutton.y;
81 time = report->xbutton.time;
82 win = report->xbutton.window;
83 if (click_inited) {
84 del_x = (click_x < x) ? x - click_x : click_x - x;
85 del_y = (click_y < y) ? y - click_y : click_y - y;
86 del_time = (click_time < time) ? time - click_time : click_time - time;
87 result = (click_win == win
88 && del_x < DOUBLE_CLICK_MOVE
89 && del_y < DOUBLE_CLICK_MOVE
90 && del_time < DOUBLE_CLICK_TIME) ? TRUE : FALSE;
92 else result = FALSE;
94 click_inited = ! result;
95 click_x = x;
96 click_y = y;
97 click_time = time;
98 click_win = win;
100 return(result);
103 /***********************************************************************/
104 /** **/
105 /** Basic Size Calculations **/
106 /** **/
107 /***********************************************************************/
109 static Point data_size(data)
110 LVAL data;
112 Point sz;
113 if (seqp(data)) {
114 sz.v = seqlen(data);
115 sz.h = 1;
117 else if (matrixp(data)) {
118 sz.v = numrows(data);
119 sz.h = numcols(data);
121 else xlerror("bad list item data", data);
122 return(sz);
125 static Point cell_size()
127 Point cellsz;
129 cellsz = DialogStringSize(LIST_TEXT_PATTERN);
130 cellsz.v += LIST_LEAD;
131 cellsz.h += LIST_PAD;
132 return(cellsz);
135 static int max_cols(item)
136 LVAL item;
138 LVAL columns = slot_value(item, s_columns);
140 return((fixp(columns)) ? getfixnum(columns) : 1);
143 /***********************************************************************/
144 /** **/
145 /** Index Conversion **/
146 /** **/
147 /***********************************************************************/
149 static int field_index(dpy, win, vdims, ddims, offset)
150 Display *dpy;
151 Window win;
152 Point vdims, ddims, offset;
154 int vis_index, index;
155 Point vpoint, dpoint;
157 if (XFindContext(dpy, win, ListFieldContext, (caddr_t *) &vis_index) != 0)
158 xlfail("could not find field index");
159 vis_index--; /* needed to avoid storing a zero - confuses som X's */
161 vpoint.v = vis_index / vdims.h;
162 vpoint.h = vis_index % vdims.h;
163 dpoint.v = offset.v + vpoint.v;
164 dpoint.h = offset.h + vpoint.h;
165 index = dpoint.v * ddims.h + dpoint.h;
167 return(index);
170 /***********************************************************************/
171 /** **/
172 /** Internal Data Represenation **/
173 /** **/
174 /***********************************************************************/
176 static VOID make_internals(item)
177 LVAL item;
179 LVAL internals, data;
180 int cols, num_fields;
181 Point dsize, vsize;
183 data = slot_value(item, s_list_data);
184 cols = max_cols(item);
185 dsize = data_size(data);
186 vsize.h = (dsize.h > cols) ? cols : dsize.h;
187 vsize.v = (dsize.v > MAX_LIST_ROWS) ? MAX_LIST_ROWS : dsize.v;
188 num_fields = vsize.h * vsize.v;
190 internals = newvector(8);
191 set_slot_value(item, s_internals, internals);
192 setelement(internals, 0, PointToList(vsize));
193 setelement(internals, 1, PointToList(dsize));
194 setelement(internals, 2,
195 matrixp(data) ? data : coerce_to_tvec(data, s_true));
196 setelement(internals, 3, newvector(num_fields));
197 setelement(internals, 4, integer_list_2(0, 0));
198 setelement(internals, 5, NIL);
199 setelement(internals, 6, NIL);
200 setelement(internals, 7, NIL);
203 static Point visible_dims(internals)
204 LVAL internals;
206 return(ListToPoint(getelement(internals, 0)));
209 static Point data_dims(internals)
210 LVAL internals;
212 return(ListToPoint(getelement(internals, 1)));
215 static LVAL get_data(internals)
216 LVAL internals;
218 return(getelement(internals, 2));
221 static LVAL get_fields(internals)
222 LVAL internals;
224 return(getelement(internals, 3));
227 static Point get_offset(internals)
228 LVAL internals;
230 return(ListToPoint(getelement(internals, 4)));
233 static VOID set_offset(internals, offset)
234 LVAL internals;
235 Point offset;
237 setelement(internals, 4, PointToList(offset));
240 static LVAL get_selection(internals)
241 LVAL internals;
243 return(getelement(internals, 5));
246 static int selection_index(internals)
247 LVAL internals;
249 LVAL sel = get_selection(internals);
250 Point ddims, psel;
252 if (sel == NIL) return(-1);
253 else if (fixp(sel)) return(getfixnum(sel));
254 else {
255 ddims = data_dims(internals);
256 psel = ListToPoint(sel);
257 return(ddims.h * psel.h + psel.v);
261 static VOID set_selection(item, val, index, use_val)
262 LVAL item, val;
263 int index, use_val;
265 Point ddims, p;
266 LVAL internals;
268 internals = slot_value(item, s_internals);
269 if (use_val) setelement(internals, 5, val);
270 else if (vectorp(get_data(internals)))
271 setelement(internals, 5, cvfixnum((FIXTYPE) index));
272 else {
273 ddims = data_dims(internals);
274 p.h = index / ddims.h;
275 p.v = index % ddims.h;
276 setelement(internals, 5, PointToList(p));
278 draw_fields(item);
281 static VOID set_vscroll(internals, w, has)
282 LVAL internals;
283 Window w;
284 int has;
286 setelement(internals, 6, (has) ? cvfixnum((FIXTYPE) w) : NIL);
289 static VOID set_hscroll(internals, w, has)
290 LVAL internals;
291 Window w;
292 int has;
294 setelement(internals, 7, (has) ? cvfixnum((FIXTYPE) w) : NIL);
297 static int has_vscroll(internals)
298 LVAL internals;
300 return(getelement(internals, 6) != NIL);
303 static int has_hscroll(internals)
304 LVAL internals;
306 return(getelement(internals, 7) != NIL);
309 static Window get_vscroll(internals)
310 LVAL internals;
312 LVAL val = getelement(internals, 6);
313 return(fixp(val) ? (Window) getfixnum(val) : None);
316 static Window get_hscroll(internals)
317 LVAL internals;
319 LVAL val = getelement(internals, 7);
320 return(fixp(val) ? (Window) getfixnum(val) : None);
323 /***********************************************************************/
324 /** **/
325 /** Drawing Routines **/
326 /** **/
327 /***********************************************************************/
329 LOCAL VOID draw_field_content(dpy, win, text, reversed)
330 Display *dpy;
331 Window win;
332 char *text;
333 int reversed;
335 Point ssz;
336 int x, y, len;
337 GC gc;
338 unsigned long color;
340 gc = (reversed) ? DialogRGC : DialogGC;
341 color = (reversed) ? DialogC.fore : DialogC.back;
343 XSetWindowBackground(dpy, win, color);
344 XClearWindow(dpy, win);
345 ssz = DialogStringSize(text);
346 x = LIST_PAD / 2;
347 y = LIST_LEAD / 2 + DialogFont->max_bounds.ascent;
348 len = strlen(text);
349 XDrawString(dpy, win, gc, x, y, text, len);
350 XSetWindowBackground(dpy, win, DialogC.back);
353 LOCAL VOID draw_fields(item)
354 LVAL item;
356 Display *dpy = StX11Display();
357 Window win;
358 LVAL fields, internals, data;
359 Point vdims, ddims, offset;
360 char *text;
361 int sel, i, j, k, index, num_fields;
363 internals = slot_value(item, s_internals);
364 data = get_data(internals);
365 if (darrayp(data)) data = getdarraydata(data);
366 fields = get_fields(internals);
367 vdims = visible_dims(internals);
368 ddims = data_dims(internals);
369 offset = get_offset(internals);
370 sel = selection_index(internals);
371 num_fields = getsize(fields);
373 for (i = 0, k = 0; i < vdims.v; i++) {
374 for (j = 0; j < vdims.h && k < num_fields; j++, k++) {
375 win = getfixnum(gettvecelement(fields, k));
376 index = (i + offset.v) * ddims.h + j + offset.h;
377 text = checkstring(gettvecelement(data, index));
378 draw_field_content(dpy, win, text, sel == index);
383 /***********************************************************************/
384 /** **/
385 /** Event Handlers **/
386 /** **/
387 /***********************************************************************/
389 static LVAL field_handler(report, modal)
390 XEvent report;
391 int modal;
393 Display *dpy = StX11Display();
394 Window win;
395 LVAL item, internals, data;
396 LVAL result = NIL;
397 Point vdims, ddims, offset;
398 char *text;
399 int sel, index;
401 win = report.xany.window;
402 item = StX11ItemObject(dpy, win);
403 internals = slot_value(item, s_internals);
404 data = get_data(internals);
405 if (darrayp(data)) data = getdarraydata(data);
406 vdims = visible_dims(internals);
407 ddims = data_dims(internals);
408 offset = get_offset(internals);
409 sel = selection_index(internals);
411 if (item != NIL) {
412 switch (report.type) {
413 case Expose:
414 index = field_index(dpy, win, vdims, ddims, offset);
415 text = checkstring(gettvecelement(data, index));
416 draw_field_content(dpy, win, text, sel == index);
417 break;
418 case ButtonPress:
419 index = field_index(dpy, win, vdims, ddims, offset);
420 set_selection(item, NIL, index, FALSE);
421 if (is_double_click(&report))
422 send_message_1L(item, sk_do_action, s_true);
423 else
424 send_message(item, sk_do_action);
425 break;
426 default:
427 break;
430 return(result);
433 static VOID scroll_action(item, s, which, x, y)
434 LVAL item;
435 Window s;
436 int which, x, y;
438 int is_h_scroll, val, max, page, pos, inc;
439 Point size, offset, ddims, vdims, cellsz;
440 LVAL internals;
441 Window hscroll, vscroll;
442 double side;
444 internals = slot_value(item, s_internals);
445 offset = get_offset(internals);
446 ddims = data_dims(internals);
447 vdims = visible_dims(internals);
448 cellsz = cell_size();
449 size.v = vdims.v * cellsz.v;
450 size.h = vdims.h * cellsz.h;
451 hscroll = get_hscroll(internals);
452 vscroll = get_vscroll(internals);
454 is_h_scroll = (s == hscroll) ? TRUE : FALSE;
455 val = (is_h_scroll) ? offset.h : offset.v;
456 max = (is_h_scroll) ? ddims.h : ddims.v;
457 page = (is_h_scroll) ? vdims.h : vdims.v;
458 side = (is_h_scroll) ? size.h : size.v;
459 pos = (is_h_scroll) ? x * (max / side) : y * (max / side);
460 inc = 1;
462 switch (which) {
463 case 'M': val = pos; break;
464 case 'L': val += inc; break;
465 case 'R': val -= inc; break;
467 if (val + page > max) val = max - page;
468 if (val < 0) val = 0;
470 if (is_h_scroll) offset.h = val;
471 else offset.v = val;
472 set_offset(internals, offset);
473 draw_fields(item);
474 if (hscroll != None) AdjustScrollBar(hscroll, offset.h, vdims.h, ddims.h);
475 if (vscroll != None) AdjustScrollBar(vscroll, offset.v, vdims.v, ddims.v);
478 /***********************************************************************/
479 /** **/
480 /** Public Routines **/
481 /** **/
482 /***********************************************************************/
484 VOID DialogListGetDefaultSize(item, width, height)
485 LVAL item;
486 int *width, *height;
488 LVAL data = slot_value(item, s_list_data);
489 Point sz, cellsz;
490 int cols, m, n;
492 cellsz = cell_size();
494 cols = max_cols(item);
495 sz = data_size(data);
496 m = sz.v;
497 n = sz.h;
499 *height = (m <= MAX_LIST_ROWS) ? m * cellsz.v : MAX_LIST_ROWS * cellsz.v;
500 *width = (n <= cols) ? n * cellsz.h : cols * cellsz.h;
501 if (m > MAX_LIST_ROWS) *width += SCROLL_WIDTH;
502 if (n > cols) *height += SCROLL_WIDTH;
505 VOID InstallListItem(win, item)
506 Window win;
507 LVAL item;
509 Display *dpy = StX11Display();
510 Point loc, vsize, size, cellsz, dsize;
511 Window panel, newfield, scroll;
512 LVAL internals, fields;
513 int num_fields, i, j, k;
515 make_internals(item);
517 internals = slot_value(item, s_internals);
518 cellsz = cell_size();
519 loc = ListToPoint(slot_value(item, s_location));
520 vsize = visible_dims(internals);
521 dsize = data_dims(internals);
522 size.v = vsize.v * cellsz.v;
523 size.h = vsize.h * cellsz.h;
524 fields = get_fields(internals);
525 num_fields = getsize(fields);
527 panel = XCreateSimpleWindow(dpy, win, loc.h, loc.v, size.h, size.v,
528 list_border_width,
529 DialogBorderColor, DialogC.back);
531 set_slot_value(item, s_window_id, cvfixnum((FIXTYPE) panel));
533 if (XSaveContext(dpy, panel, ObjectContext, (caddr_t) item) != 0)
534 xlfail("could not install object in window");
536 if (dsize.h > vsize.h) {
537 InstallScrollBar(win, item, loc.h, loc.v + size.v, size.h, SCROLL_WIDTH,
538 &scroll, scroll_action);
539 set_hscroll(internals, scroll, TRUE);
540 AdjustScrollBar(scroll, 0, vsize.h, dsize.h);
542 if (dsize.v > vsize.v) {
543 InstallScrollBar(win, item, loc.h + size.h, loc.v, SCROLL_WIDTH, size.v,
544 &scroll, scroll_action);
545 set_vscroll(internals, scroll, TRUE);
546 AdjustScrollBar(scroll, 0, vsize.v, dsize.v);
549 for (i = 0, k = 0; i < vsize.v; i++) {
550 for (j = 0; j < vsize.h && k < num_fields; j++, k++) {
551 newfield = XCreateSimpleWindow(dpy, panel, cellsz.h * j, cellsz.v * i,
552 cellsz.h, cellsz.v, 0,
553 DialogBorderColor, DialogC.back);
555 XSelectInput(dpy, newfield, ExposureMask | ButtonPressMask);
556 install_dialog_item_handler(dpy, newfield, field_handler, item);
557 if (XSaveContext(dpy, newfield, ObjectContext, (caddr_t) item) != 0)
558 xlfail("could not install object in window");
560 /* add 1 to index to avoid confusing context manager with zeros */
561 if (XSaveContext(dpy, newfield, ListFieldContext, (caddr_t) (k + 1)) != 0)
562 xlfail("could not install field index in window");
563 setelement(fields, k, cvfixnum((FIXTYPE) newfield));
566 XMapSubwindows(dpy, panel);
569 VOID DeleteListItem(win, item)
570 Window win;
571 LVAL item;
573 Display *dpy = StX11Display();
574 Window panel, thefield;
575 LVAL internals, fields;
576 int k, num_fields;
578 panel = (Window) getfixnum(slot_value(item, s_window_id));
579 internals = slot_value(item, s_internals);
580 fields = get_fields(internals);
582 num_fields = getsize(fields);
583 for (k = 0; k < num_fields; k++) {
584 thefield = getfixnum(getelement(fields, k));
585 delete_dialog_item_handler(dpy, thefield);
586 if (XDeleteContext(dpy, thefield, ObjectContext) != 0)
587 xlfail("could not delete object context");
588 if (XDeleteContext(dpy, thefield, ListFieldContext) != 0)
589 xlfail("could not delete list field context");
590 setelement(fields, k, NIL);
593 if (has_hscroll(internals)) DeleteScrollBar(get_hscroll(internals));
594 if (has_vscroll(internals)) DeleteScrollBar(get_vscroll(internals));
596 if (XDeleteContext(dpy, panel, ObjectContext) != 0)
597 xlfail("could not delete object context");
598 set_slot_value(item, s_window_id, NIL);
601 VOID DialogListItemSetText(item, index, text)
602 LVAL item, index;
603 char *text;
605 #ifdef DODO
608 this is not needed since the matrix in the internals is eq to the
609 one in the list-data slot already modified by the portable part of
610 the code. Besides, this code is wrong since it permutes the intex
611 (as on the Mac).
614 LVAL internals, data;
615 Point p, ddims;
616 int i;
618 internals = slot_value(item, s_internals);
619 data = get_data(internals);
620 if (darrayp(data)) data = getdarraydata(data);
621 if (fixp(index)) i = getfixnum(index);
622 else {
623 p = ListToPoint(index);
624 ddims = data_dims(internals);
625 i = p.v * ddims.h + p.h;
627 if (0 <= i && i < getsize(data))
628 settvecelement(data, i, cvstring(text));
629 else xlerror("index out of range", index);
630 #endif
631 draw_fields(item);
634 LVAL DialogListItemSelection(item, set, index)
635 LVAL item, index;
636 int set;
638 if (set) set_selection(item, index, 0, TRUE);
639 return(get_selection(slot_value(item, s_internals)));