/* X11listitem - list items for X11 dialogs */ /* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney */ /* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz */ /* You may give out copies of this software; for conditions see the */ /* file COPYING included with this distribution. */ /***********************************************************************/ /** **/ /** General Includes and Definitions **/ /** **/ /***********************************************************************/ #include #include #include #include "dialogs.h" extern Display *StX11Display(); extern Point DialogStringSize(); extern LVAL StX11ItemObject(); extern char *checkstring(); typedef struct { unsigned long fore, back; } ColorPair; extern LVAL s_internals; extern LVAL coerce_to_vector(); /***********************************************************************/ /** **/ /** Global Variables **/ /** **/ /***********************************************************************/ /* configuration parameters - should be set using the defaults database */ extern XFontStruct *DialogFont; extern unsigned long DialogBorderColor; extern ColorPair DialogC; extern unsigned int list_border_width; extern int min_button_width; extern GC DialogGC, DialogRGC; extern XContext ObjectContext, ListFieldContext; /***********************************************************************/ /** **/ /** List Items **/ /** **/ /***********************************************************************/ # define LIST_TEXT_PATTERN "MMMMMMMMMMMMMMM" # define LIST_LEAD 5 # define LIST_PAD 15 # define MAX_LIST_ROWS 12 # define SCROLL_WIDTH 20 # define DOUBLE_CLICK_TIME 1000 # define DOUBLE_CLICK_MOVE 10 /***********************************************************************/ /** **/ /** Double Click Detection **/ /** **/ /***********************************************************************/ static int click_x, click_y, click_inited = FALSE; static Time click_time; Window click_win; static is_double_click(report) XEvent *report; { int result, x, y, del_x, del_y, del_time; Time time; Window win; x = report->xbutton.x; y = report->xbutton.y; time = report->xbutton.time; win = report->xbutton.window; if (click_inited) { del_x = (click_x < x) ? x - click_x : click_x - x; del_y = (click_y < y) ? y - click_y : click_y - y; del_time = (click_time < time) ? time - click_time : click_time - time; result = (click_win == win && del_x < DOUBLE_CLICK_MOVE && del_y < DOUBLE_CLICK_MOVE && del_time < DOUBLE_CLICK_TIME) ? TRUE : FALSE; } else result = FALSE; click_inited = ! result; click_x = x; click_y = y; click_time = time; click_win = win; return(result); } /***********************************************************************/ /** **/ /** Basic Size Calculations **/ /** **/ /***********************************************************************/ static Point data_size(data) LVAL data; { Point sz; if (listp(data)) { sz.v = llength(data); sz.h = 1; } else if (simplevectorp(data)) { sz.v = getsize(data); sz.h = 1; } else if (matrixp(data)) { sz.v = getfixnum(getelement(displacedarraydim(data), 0)); sz.h = getfixnum(getelement(displacedarraydim(data), 1)); } else xlerror("bad list item data", data); return(sz); } static Point cell_size() { Point cellsz; cellsz = DialogStringSize(LIST_TEXT_PATTERN); cellsz.v += LIST_LEAD; cellsz.h += LIST_PAD; return(cellsz); } static max_cols(item) LVAL item; { LVAL columns = slot_value(item, s_columns); return((fixp(columns)) ? getfixnum(columns) : 1); } /***********************************************************************/ /** **/ /** Index Conversion **/ /** **/ /***********************************************************************/ static field_index(dpy, win, vdims, ddims, offset) Display *dpy; Window win; Point vdims, ddims, offset; { int vis_index, index; Point vpoint, dpoint; if (XFindContext(dpy, win, ListFieldContext, &vis_index) != 0) xlfail("could not find field index"); vis_index--; /* needed to avoid storing a zero - confuses som X's */ vpoint.v = vis_index / vdims.h; vpoint.h = vis_index % vdims.h; dpoint.v = offset.v + vpoint.v; dpoint.h = offset.h + vpoint.h; index = dpoint.v * ddims.h + dpoint.h; return(index); } /***********************************************************************/ /** **/ /** Internal Data Represenation **/ /** **/ /***********************************************************************/ make_internals(item) LVAL item; { LVAL internals, data; int cols, num_fields; Point dsize, vsize; data = slot_value(item, s_list_data); cols = max_cols(item); dsize = data_size(data); vsize.h = (dsize.h > cols) ? cols : dsize.h; vsize.v = (dsize.v > MAX_LIST_ROWS) ? MAX_LIST_ROWS : dsize.v; num_fields = vsize.h * vsize.v; internals = newvector(8); set_slot_value(item, s_internals, internals); setelement(internals, 0, PointToList(vsize)); setelement(internals, 1, PointToList(dsize)); setelement(internals, 2, (listp(data)) ? coerce_to_vector(data) : data); setelement(internals, 3, newvector(num_fields)); setelement(internals, 4, integer_list_2(0, 0)); setelement(internals, 5, NIL); setelement(internals, 6, NIL); setelement(internals, 7, NIL); } static Point visible_dims(internals) LVAL internals; { return(ListToPoint(getelement(internals, 0))); } static Point data_dims(internals) LVAL internals; { return(ListToPoint(getelement(internals, 1))); } static LVAL get_data(internals) LVAL internals; { return(getelement(internals, 2)); } static LVAL get_fields(internals) LVAL internals; { return(getelement(internals, 3)); } static Point get_offset(internals) LVAL internals; { return(ListToPoint(getelement(internals, 4))); } set_offset(internals, offset) LVAL internals; Point offset; { setelement(internals, 4, PointToList(offset)); } static LVAL get_selection(internals) LVAL internals; { return(getelement(internals, 5)); } static selection_index(internals) LVAL internals; { LVAL sel = get_selection(internals); Point ddims, psel; if (sel == NIL) return(-1); else if (fixp(sel)) return(getfixnum(sel)); else { ddims = data_dims(internals); psel = ListToPoint(sel); return(ddims.h * psel.h + psel.v); } } static set_selection(item, val, index, use_val) LVAL item, val; int index, use_val; { Point ddims, p; LVAL internals; internals = slot_value(item, s_internals); if (use_val) setelement(internals, 5, val); else if (vectorp(get_data(internals))) setelement(internals, 5, cvfixnum((FIXTYPE) index)); else { ddims = data_dims(internals); p.h = index / ddims.h; p.v = index % ddims.h; setelement(internals, 5, PointToList(p)); } draw_fields(item); } static set_vscroll(internals, w, has) LVAL internals; Window w; int has; { setelement(internals, 6, (has) ? cvfixnum((FIXTYPE) w) : NIL); } static set_hscroll(internals, w, has) LVAL internals; Window w; int has; { setelement(internals, 7, (has) ? cvfixnum((FIXTYPE) w) : NIL); } static has_vscroll(internals) LVAL internals; { return(getelement(internals, 6) != NIL); } static has_hscroll(internals) LVAL internals; { return(getelement(internals, 7) != NIL); } static Window get_vscroll(internals) LVAL internals; { LVAL val = getelement(internals, 6); return(fixp(val) ? (Window) getfixnum(val) : None); } static Window get_hscroll(internals) LVAL internals; { LVAL val = getelement(internals, 7); return(fixp(val) ? (Window) getfixnum(val) : None); } /***********************************************************************/ /** **/ /** Drawing Routines **/ /** **/ /***********************************************************************/ static draw_field_content(dpy, win, text, reversed) Display *dpy; Window win; char *text; int reversed; { Point ssz; int x, y, len; GC gc; unsigned long color; gc = (reversed) ? DialogRGC : DialogGC; color = (reversed) ? DialogC.fore : DialogC.back; XSetWindowBackground(dpy, win, color); XClearWindow(dpy, win); ssz = DialogStringSize(text); x = LIST_PAD / 2; y = LIST_LEAD / 2 + DialogFont->max_bounds.ascent; len = strlen(text); XDrawString(dpy, win, gc, x, y, text, len); XSetWindowBackground(dpy, win, DialogC.back); } static draw_fields(item) LVAL item; { Display *dpy = StX11Display(); Window win; LVAL fields, internals, data; Point vdims, ddims, offset; char *text; int sel, i, j, k, index, num_fields; internals = slot_value(item, s_internals); data = arraydata(get_data(internals)); fields = get_fields(internals); vdims = visible_dims(internals); ddims = data_dims(internals); offset = get_offset(internals); sel = selection_index(internals); num_fields = getsize(fields); for (i = 0, k = 0; i < vdims.v; i++) { for (j = 0; j < vdims.h && k < num_fields; j++, k++) { win = getfixnum(getelement(fields, k)); index = (i + offset.v) * ddims.h + j + offset.h; text = checkstring(getelement(data, index)); draw_field_content(dpy, win, text, sel == index); } } } /***********************************************************************/ /** **/ /** Event Handlers **/ /** **/ /***********************************************************************/ static LVAL field_handler(report, modal) XEvent report; int modal; { Display *dpy = StX11Display(); Window win; LVAL item, internals, data; LVAL result = NIL; Point vdims, ddims, offset; char *text; int sel, index; win = report.xany.window; item = StX11ItemObject(dpy, win); internals = slot_value(item, s_internals); data = arraydata(get_data(internals)); vdims = visible_dims(internals); ddims = data_dims(internals); offset = get_offset(internals); sel = selection_index(internals); if (item != NIL) { switch (report.type) { case Expose: index = field_index(dpy, win, vdims, ddims, offset); text = checkstring(getelement(data, index)); draw_field_content(dpy, win, text, sel == index); break; case ButtonPress: index = field_index(dpy, win, vdims, ddims, offset); set_selection(item, NIL, index, FALSE); if (is_double_click(&report)) send_message_1L(item, sk_do_action, s_true); else send_message(item, sk_do_action); break; default: break; } } return(result); } static scroll_action(item, s, which, x, y) LVAL item; Window s; int which, x, y; { int is_h_scroll, val, max, page, pos, inc; Point size, offset, ddims, vdims, cellsz; LVAL internals; Window hscroll, vscroll; double side; internals = slot_value(item, s_internals); offset = get_offset(internals); ddims = data_dims(internals); vdims = visible_dims(internals); cellsz = cell_size(); size.v = vdims.v * cellsz.v; size.h = vdims.h * cellsz.h; hscroll = get_hscroll(internals); vscroll = get_vscroll(internals); is_h_scroll = (s == hscroll) ? TRUE : FALSE; val = (is_h_scroll) ? offset.h : offset.v; max = (is_h_scroll) ? ddims.h : ddims.v; page = (is_h_scroll) ? vdims.h : vdims.v; side = (is_h_scroll) ? size.h : size.v; pos = (is_h_scroll) ? x * (max / side) : y * (max / side); inc = 1; switch (which) { case 'M': val = pos; break; case 'L': val += inc; break; case 'R': val -= inc; break; } if (val + page > max) val = max - page; if (val < 0) val = 0; if (is_h_scroll) offset.h = val; else offset.v = val; set_offset(internals, offset); draw_fields(item); if (hscroll != None) AdjustScrollBar(hscroll, offset.h, vdims.h, ddims.h); if (vscroll != None) AdjustScrollBar(vscroll, offset.v, vdims.v, ddims.v); } /***********************************************************************/ /** **/ /** Public Routines **/ /** **/ /***********************************************************************/ DialogListGetDefaultSize(item, width, height) LVAL item; int *width, *height; { LVAL data = slot_value(item, s_list_data); Point sz, cellsz; int cols, m, n; cellsz = cell_size(); cols = max_cols(item); sz = data_size(data); m = sz.v; n = sz.h; *height = (m <= MAX_LIST_ROWS) ? m * cellsz.v : MAX_LIST_ROWS * cellsz.v; *width = (n <= cols) ? n * cellsz.h : cols * cellsz.h; if (m > MAX_LIST_ROWS) *width += SCROLL_WIDTH; if (n > cols) *height += SCROLL_WIDTH; } InstallListItem(win, item) Window win; LVAL item; { Display *dpy = StX11Display(); Point loc, vsize, size, cellsz, dsize; Window panel, newfield, scroll; LVAL s_window_id = xlenter("WINDOW-ID"), internals, fields; int num_fields, i, j, k; make_internals(item); internals = slot_value(item, s_internals); cellsz = cell_size(); loc = ListToPoint(slot_value(item, s_location)); vsize = visible_dims(internals); dsize = data_dims(internals); size.v = vsize.v * cellsz.v; size.h = vsize.h * cellsz.h; fields = get_fields(internals); num_fields = getsize(fields); panel = XCreateSimpleWindow(dpy, win, loc.h, loc.v, size.h, size.v, list_border_width, DialogBorderColor, DialogC.back); set_slot_value(item, s_window_id, cvfixnum((FIXTYPE) panel)); if (XSaveContext(dpy, panel, ObjectContext, (XContext) item) != 0) xlfail("could not install object in window"); if (dsize.h > vsize.h) { InstallScrollBar(win, item, loc.h, loc.v + size.v, size.h, SCROLL_WIDTH, &scroll, scroll_action); set_hscroll(internals, scroll, TRUE); AdjustScrollBar(scroll, 0, vsize.h, dsize.h); } if (dsize.v > vsize.v) { InstallScrollBar(win, item, loc.h + size.h, loc.v, SCROLL_WIDTH, size.v, &scroll, scroll_action); set_vscroll(internals, scroll, TRUE); AdjustScrollBar(scroll, 0, vsize.v, dsize.v); } for (i = 0, k = 0; i < vsize.v; i++) { for (j = 0; j < vsize.h && k < num_fields; j++, k++) { newfield = XCreateSimpleWindow(dpy, panel, cellsz.h * j, cellsz.v * i, cellsz.h, cellsz.v, 0, DialogBorderColor, DialogC.back); XSelectInput(dpy, newfield, ExposureMask | ButtonPressMask); install_dialog_item_handler(dpy, newfield, field_handler, item); if (XSaveContext(dpy, newfield, ObjectContext, (XContext) item) != 0) xlfail("could not install object in window"); /* add 1 to index to avoid confusing context manager with zeros */ if (XSaveContext(dpy, newfield, ListFieldContext, (k + 1)) != 0) xlfail("could not install field index in window"); setelement(fields, k, cvfixnum((FIXTYPE) newfield)); } } XMapSubwindows(dpy, panel); } DeleteListItem(win, item) Window win; LVAL item; { Display *dpy = StX11Display(); Window panel, thefield; LVAL s_window_id = xlenter("WINDOW-ID"), internals, fields; int k, num_fields; panel = (Window) getfixnum(slot_value(item, s_window_id)); internals = slot_value(item, s_internals); fields = get_fields(internals); num_fields = getsize(fields); for (k = 0; k < num_fields; k++) { thefield = getfixnum(getelement(fields, k)); delete_dialog_item_handler(dpy, thefield); if (XDeleteContext(dpy, thefield, ObjectContext) != 0) xlfail("cound not delete object context"); if (XDeleteContext(dpy, thefield, ListFieldContext) != 0) xlfail("cound not delete list field context"); setelement(fields, k, NIL); } if (has_hscroll(internals)) DeleteScrollBar(get_hscroll(internals)); if (has_vscroll(internals)) DeleteScrollBar(get_vscroll(internals)); if (XDeleteContext(dpy, panel, ObjectContext) != 0) xlfail("cound not delete object context"); set_slot_value(item, s_window_id, NIL); } DialogListItemSetText(item, index, text) LVAL item, index; char *text; { #ifdef DODO /* this is not needed since the matrix in the internals is eq to the one in the list-data slot already modified by the portable part of the code. Besides, this code is wrong since it permutes the intex (as on the Mac). */ LVAL internals, data; Point p, ddims; int i; internals = slot_value(item, s_internals); data = arraydata(get_data(internals)); if (fixp(index)) i = getfixnum(index); else { p = ListToPoint(index); ddims = data_dims(internals); i = p.v * ddims.h + p.h; } if (0 <= i && i < getsize(data)) setelement(data, i, cvstring(text)); else xlerror("index out of range", index); #endif draw_fields(item); } LVAL DialogListItemSelection(item, set, index) LVAL item, index; int set; { if (set) set_selection(item, index, 0, TRUE); return(get_selection(slot_value(item, s_internals))); }