1 /*
2 Copyright (c) 2006 Perry Rapp
3
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation
6 files (the "Software"), to deal in the Software without
7 restriction, including without limitation the rights to use, copy,
8 modify, merge, publish, distribute, sublicense, and/or sell copies
9 of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 /*=============================================================
25 * listui.c -- display code for popup list and list browse screen
26 * Copyright(c) 2006
27 *===========================================================*/
28
29 #include <time.h>
30 #include "llstdlib.h"
31 #ifdef HAVE_LOCALE_H
32 #include <locale.h>
33 #endif
34 #include "table.h"
35 #include "liflines.h"
36 #include "arch.h"
37 #include "lloptions.h"
38 #include "interp.h"
39 #include "llinesi.h"
40 #include "menuitem.h"
41 #include "screen.h"
42 #include "cscurses.h"
43 #include "zstr.h"
44 #include "cache.h"
45 #ifdef WIN32_ICONV_SHIM
46 #include "iconvshim.h"
47 #endif
48 #include "codesets.h"
49 #include "charprops.h"
50 #include "listui.h"
51
52
53
54 static INT LISTWIN_WIDTH=0;
55
56 /*********************************************
57 * external/imported variables
58 *********************************************/
59
60 extern STRING qSmn_quit;
61
62 /*********************************************
63 * local types
64 *********************************************/
65
66 /*
67 Data for lists display
68 Both popup & full-screen lists have 3 rectangles:
69 details, list items, and menu
70 But they are not in the same place
71 */
72 typedef struct listdisp_s
73 {
74 UIWINDOW uiwin;
75 struct tag_llrect rectList;
76 struct tag_llrect rectDetails;
77 struct tag_llrect rectMenu;
78 INT details; /* #rows of detail info */
79 INT details_minhgt;
80 INT details_beginhgt; /* increase from 0 goes to this */
81 INT details_maxhgt;
82 INT details_scroll; /* scroll offset in detail area */
83 INT cur; /* current item selected, 0-based */
84 INT listlen; /* #items total */
85 INT top; /* current item at top of display, 0-based */
86 INT mode; /* record display mode */
87 } listdisp;
88
89 /*********************************************
90 * local function prototypes
91 *********************************************/
92
93 /* alphabetical */
94 static void activate_popup_list_uiwin (listdisp * ld);
95 static void display_string(UIWINDOW uiwin, LLRECT rect, STRING text);
96 static INT handle_list_cmds(listdisp * ld, INT code);
97 static BOOLEAN handle_popup_list_resize(listdisp * ld, INT code);
98 static void print_list_title(char * buffer, INT len, const listdisp * ld, STRING ttl);
99 static void shw_array_of_strings(STRING *strings, listdisp *ld
100 , DETAILFNC detfnc, void * param);
101 static void shw_popup_list(INDISEQ seq, listdisp * ld);
102 static void shw_recordlist_details(INDISEQ seq, listdisp * ld);
103 static void shw_recordlist_list(INDISEQ seq, listdisp * ld);
104
105 /*********************************************
106 * local variables
107 *********************************************/
108
109 /* the following values are default (larger screens get more) */
110 static int LIST_LINES_DEF = 6; /* number of lines of person info in list */
111 static int POPUP_LINES_DEF = 17; /* max lines in popup list */
112 /* working values */
113 static int LIST_LINES=0;
114 static int POPUP_LINES=0;
115
116 /*==============================================
117 * listui_init_windows -- Initialize anything dependent on screen size
118 *============================================*/
119 void
listui_init_windows(INT extralines)120 listui_init_windows (INT extralines)
121 {
122 /* initialize list window heights to default */
123 LIST_LINES = LIST_LINES_DEF;
124 POPUP_LINES = POPUP_LINES_DEF;
125 /* increase for larger screens */
126 if(extralines > 0) {
127 LIST_LINES = LIST_LINES_DEF + extralines;
128 POPUP_LINES = POPUP_LINES_DEF + extralines;
129 }
130
131 LISTWIN_WIDTH = ll_cols-7;
132 }
133 /*==============================================
134 * array_interact -- Interact with user over list
135 * ttl: [IN] title
136 * len: [IN] number of choices
137 * strings: [IN] array of choices
138 * selectable: [IN] FALSE for view-only
139 * detfnc: [IN] callback for details about items
140 * param: [IN] opaque type for callback
141 * returns 0-based index chosen, or -1 if cancelled
142 *============================================*/
143 INT
array_interact(STRING ttl,INT len,STRING * strings,BOOLEAN selectable,DETAILFNC detfnc,void * param)144 array_interact (STRING ttl, INT len, STRING *strings
145 , BOOLEAN selectable, DETAILFNC detfnc, void * param)
146 {
147 WINDOW *win=0;
148 INT row, done;
149 char fulltitle[128];
150 STRING responses = len<10 ? "jkiq123456789[]()$^" : "jkiq[]()$^";
151 STRING promptline=0;
152 listdisp ld; /* structure used in resizable list displays */
153
154 if (selectable)
155 promptline = _("Commands: j Move down k Move up i Select q Quit");
156 else
157 promptline = _("Commands: j Move down k Move up q Quit");
158
159 memset(&ld, 0, sizeof(ld));
160 ld.listlen = len;
161 ld.mode = 'n'; /* irrelevant for array list */
162
163 resize_win: /* we come back here if we resize the window */
164 activate_popup_list_uiwin(&ld);
165 win = uiw_win(ld.uiwin);
166 uierase(ld.uiwin);
167 draw_win_box(win);
168 row = ld.rectMenu.top-1;
169 show_horz_line(ld.uiwin, row++, 0, uiw_cols(ld.uiwin));
170 mvccwaddstr(win, row, ld.rectMenu.left, promptline);
171 done = FALSE;
172 while (!done) {
173 INT code=0, ret=0;
174 print_list_title(fulltitle, sizeof(fulltitle), &ld, _(ttl));
175 mvccwaddstr(win, 1, 1, fulltitle);
176 shw_array_of_strings(strings, &ld, detfnc, param);
177 wrefresh(win);
178 code = interact_popup(ld.uiwin, responses);
179 if (handle_list_cmds(&ld, code))
180 continue;
181 if (handle_popup_list_resize(&ld, code)) {
182 deactivate_uiwin_and_touch_all();
183 /* we're going to repick window & activate */
184 goto resize_win;
185 }
186 if (ret == 0) { /* not handled yet */
187 switch(code) {
188 case 'i': /* select current item */
189 case CMD_KY_ENTER:
190 if (selectable) {
191 done=TRUE;
192 }
193 break;
194 case '1':
195 case '2':
196 case '3':
197 case '4':
198 case '5':
199 case '6':
200 case '7':
201 case '8':
202 case '9':
203 if (len < 10 && selectable && code - '1' < len) {
204 done=TRUE;
205 ld.cur = code - '1';
206 }
207 break;
208 case 'q':
209 done=TRUE;
210 ld.cur = -1; /* ld.cur == -1 means cancelled */
211 }
212 }
213 }
214 deactivate_uiwin_and_touch_all();
215 return ld.cur;
216 }
217 /*=============================================================
218 * choose_one_or_list_from_indiseq --
219 * Implements the two choose_xxx_from_indiseq
220 * @ttl: [IN] title/caption for choice list
221 * @seq: [IN] list from which to choose
222 * @multi: [IN] if true, selecting a sublist
223 * returns index of selected (or -1 for quit)
224 * Rewritten to allow dynamic resizing (so user can
225 * resize detail area, ie, the [] functions), 2000/12, Perry Rapp
226 * Localizes ttl
227 *===========================================================*/
228 INT
choose_one_or_list_from_indiseq(STRING ttl,INDISEQ seq,BOOLEAN multi)229 choose_one_or_list_from_indiseq (STRING ttl, INDISEQ seq, BOOLEAN multi)
230 {
231 WINDOW *win=0;
232 INT row, done;
233 char fulltitle[128];
234 INT elemwidth;
235 listdisp ld; /* structure used in resizable list displays */
236 STRING menu, choices;
237 BOOLEAN first=TRUE;
238
239 ASSERT(seq);
240
241 calc_indiseq_names(seq); /* we certainly need the names */
242
243 memset(&ld, 0, sizeof(ld));
244 ld.listlen = length_indiseq(seq);
245 ld.mode = 'n';
246
247 /* TO DO: connect this to menuitem system */
248 if (multi) {
249 menu = _("Commands: j Move down k Move up d Delete i Select q Quit");
250 choices = "jkriq123456789()[]$^udUD";
251 } else {
252 menu = _("Commands: j Move down k Move up i Select q Quit");
253 choices = "jkiq123456789()[]$^udUD";
254 }
255
256 resize_win: /* we come back here if we resize the window */
257 activate_popup_list_uiwin(&ld);
258 win = uiw_win(ld.uiwin);
259 if (first) {
260 elemwidth = ld.rectDetails.right - ld.rectDetails.left + 1;
261 if (length_indiseq(seq)<50)
262 preprint_indiseq(seq, elemwidth, &disp_shrt_rfmt);
263 first=FALSE;
264 }
265 uierase(ld.uiwin);
266 draw_win_box(win);
267 row = ld.rectMenu.top-1;
268 show_horz_line(ld.uiwin, row++, 0, uiw_cols(ld.uiwin));
269 mvccwaddstr(win, row, ld.rectMenu.left, menu);
270 done = FALSE;
271 while (!done) {
272 INT code=0, ret=0;
273 print_list_title(fulltitle, sizeof(fulltitle), &ld, ttl);
274 mvccwaddstr(win, 1, 1, fulltitle);
275 shw_popup_list(seq, &ld);
276 wmove(win, row, 11);
277 wrefresh(win);
278 code = interact_popup(ld.uiwin, choices);
279 if (handle_list_cmds(&ld, code))
280 continue;
281 if (handle_popup_list_resize(&ld, code)) {
282 deactivate_uiwin_and_touch_all(); /* kills ld.uiwin */
283 ld.uiwin = NULL;
284 /* we're going to repick window & activate */
285 goto resize_win;
286 }
287 if (ret == 0) { /* not handled yet */
288 switch (code) {
289 case 'r':
290 if (!multi)
291 break;
292 delete_indiseq(seq, NULL, NULL, ld.cur);
293 if (!(ld.listlen = length_indiseq(seq))) {
294 done=TRUE;
295 ld.cur = -1;
296 }
297 if (ld.cur == ld.listlen) ld.cur--;
298 if (ld.cur < ld.top) ld.top = ld.cur;
299 break;
300 case 'i':
301 case CMD_KY_ENTER:
302 done=TRUE;
303 /* ld.cur points to currently selected */
304 break;
305 case '1':
306 case '2':
307 case '3':
308 case '4':
309 case '5':
310 case '6':
311 case '7':
312 case '8':
313 case '9':
314 if (ld.listlen < 10 && code - '1' < ld.listlen) {
315 done=TRUE;
316 ld.cur = code - '1';
317 }
318 break;
319 case 'q':
320 done=TRUE;
321 ld.cur = -1; /* ld.cur == -1 means cancelled */
322 break;
323 }
324 }
325 }
326 deactivate_uiwin_and_touch_all(); /* kills ld.uiwin */
327 ld.uiwin = NULL;
328
329 return ld.cur;
330 }
331 /*=============================================================
332 * handle_list_cmds -- Process choices from list display
333 * This handles moving up & down, adjusting size of detail,
334 * and scrolling detail.
335 * @listdisp: [I/O] array of info about list display
336 * @code: [IN] command to process
337 * Returns -1 if resized window, 1 if handled, 0 if unhandled.
338 *===========================================================*/
339 static INT
handle_list_cmds(listdisp * ld,INT code)340 handle_list_cmds (listdisp * ld, INT code)
341 {
342 INT rows = ld->rectList.bottom - ld->rectList.top + 1;
343 INT tmp;
344 switch(code) {
345 case 'j': /* next item */
346 case CMD_KY_DN:
347 if (ld->cur < ld->listlen - 1) {
348 ld->cur++;
349 if (ld->cur >= ld->top + rows)
350 ld->top = ld->cur + 1 - rows;
351 }
352 return TRUE; /* handled */
353 case 'd':
354 case CMD_KY_PGDN:
355 if (ld->top + rows < ld->listlen) {
356 ld->top += rows;
357 ld->cur += rows;
358 if (ld->cur > ld->listlen - 1)
359 ld->cur = ld->listlen - 1;
360 }
361 return TRUE; /* handled */
362 case 'D':
363 case CMD_KY_SHPGDN:
364 if (ld->top + rows < ld->listlen) {
365 tmp = (ld->listlen)/10;
366 if (tmp < rows*2) tmp = rows*2;
367 if (tmp > ld->listlen - rows - ld->top)
368 tmp = ld->listlen - rows - ld->top;
369 ld->top += tmp;
370 ld->cur += tmp;
371 if (ld->cur > ld->listlen - 1)
372 ld->cur = ld->listlen - 1;
373 }
374 return TRUE; /* handled */
375 case '$': /* jump to end of list */
376 case CMD_KY_END:
377 ld->top = ld->listlen - rows;
378 if (ld->top < 0)
379 ld->top = 0;
380 ld->cur = ld->listlen-1;
381 return TRUE; /* handled */
382 case 'k': /* previous item */
383 case CMD_KY_UP:
384 if (ld->cur > 0) {
385 ld->cur--;
386 if (ld->cur < ld->top)
387 ld->top = ld->cur;
388 }
389 return TRUE; /* handled */
390 case 'u':
391 case CMD_KY_PGUP:
392 tmp = rows;
393 if (tmp > ld->top) tmp = ld->top;
394 ld->top -= tmp;
395 ld->cur -= tmp;
396 return TRUE; /* handled */
397 case 'U':
398 case CMD_KY_SHPGUP:
399 tmp = (ld->listlen)/10;
400 if (tmp < rows*2) tmp = rows*2;
401 if (tmp > ld->top) tmp = ld->top;
402 ld->cur -= tmp;
403 ld->top -= tmp;
404 return TRUE; /* handled */
405 case '^': /* jump to top of list */
406 case CMD_KY_HOME:
407 ld->top = ld->cur = 0;
408 return TRUE; /* handled */
409 case '(': /* scroll detail area up */
410 if (ld->details_scroll)
411 ld->details_scroll--;
412 return TRUE; /* handled */
413 case ')': /* scroll detail area down */
414 if (ld->details_scroll<2)
415 ld->details_scroll++;
416 return 1; /* handled */
417 }
418 return FALSE; /* unhandled */
419 }
420 /*=====================================================
421 * shw_popup_list -- Draw list & details of popup list
422 *===================================================*/
423 static void
shw_popup_list(INDISEQ seq,listdisp * ld)424 shw_popup_list (INDISEQ seq, listdisp * ld)
425 {
426 WINDOW *win = uiw_win(ld->uiwin);
427 ASSERT(ld->listlen == length_indiseq(seq));
428 if (ld->details) {
429 INT row = ld->rectDetails.top-1;
430 clear_hseg(win, row, ld->rectDetails.left, ld->rectDetails.right);
431 mvccwaddstr(win, row, 2, _("--- CURRENT SELECTION ---"));
432 shw_recordlist_details(seq, ld);
433 row = ld->rectDetails.bottom+1;
434 mvccwaddstr(win, row, ld->rectDetails.left, _("--- LIST ---"));
435 }
436 shw_recordlist_list(seq, ld);
437 }
438 /*=====================================================
439 * shw_recordlist_details -- Draw record details for a list
440 * For either popup list or full-screen list (list browse)
441 *===================================================*/
442 static void
shw_recordlist_details(INDISEQ seq,listdisp * ld)443 shw_recordlist_details (INDISEQ seq, listdisp * ld)
444 {
445 WINDOW *win = uiw_win(ld->uiwin);
446 INT i;
447 STRING key, name;
448 BOOLEAN reuse=FALSE; /* don't reuse display strings in list */
449 for (i=ld->rectDetails.top; i<=ld->rectDetails.bottom; ++i) {
450 clear_hseg(win, i, ld->rectDetails.left, ld->rectDetails.right-10);
451 }
452 element_indiseq(seq, ld->cur, &key, &name);
453 if (!show_record(ld->uiwin, key, ld->mode, &ld->rectDetails
454 , &ld->details_scroll, reuse)) {
455 /* if couldn't find record, just display record key */
456 display_string(ld->uiwin, &ld->rectDetails, key);
457 }
458 }
459 /*=============================================================
460 * handle_popup_list_resize -- Process resizes of popup list
461 * In popup list, details & list compete, & menu is fixed
462 * Returns TRUE if handled, FALSE if not
463 *===========================================================*/
464 static BOOLEAN
handle_popup_list_resize(listdisp * ld,INT code)465 handle_popup_list_resize (listdisp * ld, INT code)
466 {
467 INT delta;
468 switch(code) {
469 case '[': /* shrink detail area */
470 if (ld->details) {
471 delta = (ld->details > ld->details_minhgt) ? 1 : ld->details;
472 ld->details -= delta;
473 ld->rectDetails.bottom -= delta;
474 ld->rectList.top -= delta;
475 return -1; /* handled & needs resize */
476 }
477 return 1; /* handled (nothing) */
478 case ']': /* enlarge detail area */
479 if (ld->details < ld->details_maxhgt) {
480 delta = ld->details ? 1 : ld->details_beginhgt;
481 ld->details += delta;
482 ld->rectDetails.bottom += delta;
483 ld->rectList.top += delta;
484 return TRUE; /* handled */
485 }
486 return TRUE; /* handled (nothing) */
487 }
488 return FALSE; /* unhandled */
489 }
490 /*=====================================================
491 * shw_recordlist_list -- Draw actual list items
492 * For either popup list or full-screen list (list browse)
493 *===================================================*/
494 static void
shw_recordlist_list(INDISEQ seq,listdisp * ld)495 shw_recordlist_list (INDISEQ seq, listdisp * ld)
496 {
497 WINDOW *win = uiw_win(ld->uiwin);
498 INT width = (ld->rectList.right - ld->rectList.left + 1) - 4;
499 INT rows = ld->rectList.bottom - ld->rectList.top + 1;
500 INT i, j, row;
501 INT offset=4;
502 char buffer[160];
503 BOOLEAN scrollable = (rows < ld->listlen);
504 /* for short lists, use leading numbers */
505 if (ld->listlen < 10) {
506 sprintf(buffer, "%ld: ", ld->listlen);
507 i = strlen(buffer);
508 width -= i; /* for "1: " */
509 offset += i;
510 }
511 if (width > (INT)sizeof(buffer)-1)
512 width = sizeof(buffer)-1;
513 for (j=0; j<rows; j++) {
514 /* j is zero-based iterator */
515 /* i is actual offset into indiseq */
516 i = ld->top + j;
517 /* row is row on screen */
518 row = ld->rectList.top + j;
519 clear_hseg(win, row, ld->rectList.left, ld->rectList.right);
520 if (i<ld->listlen) {
521 if (i == 0 && scrollable)
522 mvwaddch(win, row, ld->rectList.left, '^');
523 if (i == ld->listlen-1 && scrollable)
524 mvwaddch(win, row, ld->rectList.left, '$');
525 if (i == ld->cur) mvwaddch(win, row, ld->rectList.left+3, '>');
526 if (ld->listlen < 10) {
527 char numstr[12];
528 sprintf(numstr, "%d:", i+1);
529 mvccwaddstr(win, row, ld->rectList.left+4, numstr);
530 }
531 print_indiseq_element(seq, i, buffer, width, &disp_shrt_rfmt);
532 mvccwaddstr(win, row, ld->rectList.left+offset, buffer);
533 }
534 }
535 }
536 /*==================================
537 * print_list_title -- Print title line of an array list
538 * Adds suffix such as (5/11)
539 * Truncates title if necessary (leaving room for suffix)
540 * buffer: [OUT] output string
541 * len: [IN] size of buffer
542 * ld: [IN] list display structure
543 * ttl: [IN] title to print (localized)
544 *================================*/
545 static void
print_list_title(char * buffer,INT len,const listdisp * ld,STRING ttl)546 print_list_title (char * buffer, INT len, const listdisp * ld, STRING ttl)
547 {
548 STRING ptr = buffer;
549 char suffix[30];
550 if (len > uiw_cols(ld->uiwin)-2)
551 len = uiw_cols(ld->uiwin)-2;
552 sprintf(suffix, " (%ld/%ld)", ld->cur+1, ld->listlen);
553 len -= strlen(suffix)+1; /* reserve room for suffix */
554 ptr[0] = 0;
555 if ((INT)strlen(ttl)>len-1) {
556 len -= 4;
557 llstrcatn(&ptr, ttl, &len);
558 len += 4;
559 llstrcatn(&ptr, "...", &len);
560 } else {
561 llstrcatn(&ptr, ttl, &len);
562 }
563 len += strlen(suffix)+1; /* we reserved this room above */
564 llstrcatn(&ptr, suffix, &len);
565 }
566 /*================================================================
567 * shw_array_of_strings -- Show string list in list interact window
568 * strings: [IN] array (of choices) to be listed
569 * ld: [IN] structure of info for variable-sized list
570 * detfnc: [IN] callback for detail area
571 * param: [IN] opaque data for callback
572 *==============================================================*/
573 static void
shw_array_of_strings(STRING * strings,listdisp * ld,DETAILFNC detfnc,void * param)574 shw_array_of_strings (STRING *strings, listdisp * ld, DETAILFNC detfnc
575 , void * param)
576 {
577 /* 120 spaces */
578 STRING empstr120 = " ";
579 WINDOW *win = uiw_win(ld->uiwin);
580 INT i, j, row, lines;
581 INT rows = ld->rectList.bottom - ld->rectList.top + 1;
582 INT overflag=FALSE;
583 char buffer[120];
584 INT width = uiw_cols(ld->uiwin);
585 if (width > (INT)sizeof(buffer)-1)
586 width = sizeof(buffer)-1;
587 /* clear current lines */
588 lines = rows + (ld->details ? ld->details+2 : 0);
589 for (i = 0; i<lines; ++i) {
590 row = i+2;
591 llstrncpy(buffer, empstr120, width-1, uu8);
592 mvccwaddstr(win, row, 1, buffer);
593 }
594 row = 2;
595 if (ld->details) {
596 row = 3+ld->details;
597 mvccwaddstr(win, row++, 2, _("--- LIST ---"));
598 }
599 for (j=0; j<rows;++j) {
600 INT nlen=0,temp;
601 i = ld->top + j;
602 if (i>=ld->listlen)
603 break;
604 /* for short lists, we show leading numbers */
605 if (ld->listlen<10) {
606 char numstr[12]="";
607 llstrncpyf(numstr, sizeof(numstr), uu8, "%d: ", i+1);
608 if (i == ld->cur) mvwaddch(win, row, 3, '>');
609 mvccwaddstr(win, row, 4, numstr);
610 nlen = strlen(numstr);
611 } else {
612 if (i == ld->cur) mvwaddch(win, row, 3, '>');
613 }
614 temp = width-6-nlen;
615 llstrncpy(buffer, strings[i], temp, uu8);
616 if ((INT)strlen(buffer) > temp-2) {
617 if (i==ld->cur)
618 overflag=TRUE;
619 strcpy(&buffer[temp-3], "...");
620 }
621 mvccwaddstr(win, row, 4+nlen, buffer);
622 row++;
623 }
624 if (ld->details) {
625 STRING ptr = strings[ld->cur];
626 INT count;
627 row = 2;
628 mvccwaddstr(win, row++, 2, _("-- CURRENT SELECTION --"));
629 for (i=0; i<ld->details; ++i) {
630 /* TODO: scroll */
631 if (!ptr[0]) break;
632 llstrncpy(buffer, ptr, width-5, uu8);
633 mvccwaddstr(win, row++, 4, buffer);
634 ptr += strlen(buffer);
635 }
636 count = ld->details-1;
637 if (count && detfnc) {
638 /* caller gave us a detail callback, so we set up the
639 data needed & call it */
640 STRING * linestr = (STRING *)stdalloc(count * sizeof(STRING));
641 struct tag_array_details dets;
642 for (j=0; j<count; ++j) {
643 linestr[j] = stdalloc(width);
644 linestr[j][0] = 0;
645 }
646 memset(&dets, 0, sizeof(dets));
647 dets.list = strings;
648 dets.cur = ld->cur;
649 dets.lines = linestr;
650 dets.count = count;
651 dets.maxlen = width;
652 (*detfnc)(&dets, param);
653 for (j=0 ; j<count; ++j) {
654 mvccwaddstr(win, row++, 4, linestr[j]);
655 }
656 for (j=0; j<count; ++j)
657 stdfree(linestr[j]);
658 stdfree(linestr);
659 }
660 }
661 }
662 /*=============================================================
663 * activate_popup_list_uiwin --
664 * Choose list uiwin & activate
665 * @listdisp: [I/O] caller must have filled this in
666 * This routine sets the uiwin, height, rows members
667 *===========================================================*/
668 static void
activate_popup_list_uiwin(listdisp * ld)669 activate_popup_list_uiwin (listdisp * ld)
670 {
671 INT asklen, hgt, rows, waste;
672 /*
673 How many rows do we want ?
674 One for each item in list
675 +5: top line, title, bottom row, menu, bottom line
676 if details, need also line above & below details
677 */
678 asklen = ld->listlen;
679 if (ld->details)
680 asklen += ld->details+2;
681 hgt = asklen+5;
682
683 if (hgt>POPUP_LINES)
684 hgt = POPUP_LINES;
685 create_newwin2(&ld->uiwin, "list", hgt, LISTWIN_WIDTH);
686 uiw_dynamic(ld->uiwin) = TRUE; /* delete when finished */
687 /* list is below details to nearly bottom */
688 ld->rectList.left = 1;
689 ld->rectList.right = LISTWIN_WIDTH-2;
690 ld->rectList.top = 2;
691 if (ld->details) /* leave room for --DETAILS-- & --LIST-- lines */
692 ld->rectList.top += 2+ld->details;
693 ld->rectList.bottom = hgt-4;
694 /* details is from top down as far as #details */
695 ld->rectDetails.top = 2;
696 if (ld->details) /* leave room for --DETAILS-- line */
697 ++ld->rectDetails.top;
698 ld->rectDetails.bottom = ld->rectDetails.top + ld->details-1;
699 ld->rectDetails.left = 1;
700 ld->rectDetails.right = LISTWIN_WIDTH-2;
701 /* menu is at bottom, single-line */
702 ld->rectMenu.top = hgt-2;
703 ld->rectMenu.bottom = hgt-2;
704 ld->rectMenu.left = 1;
705 ld->rectMenu.right = LISTWIN_WIDTH-2;
706 ld->details_beginhgt = 4;
707 ld->details_maxhgt = POPUP_LINES-10;
708 ld->details_minhgt = 3;
709
710 activate_uiwin(ld->uiwin);
711 /* ensure cur is on-screen */
712 /* (growing detail area can push current off-screen) */
713 rows = ld->rectList.bottom + 1 - ld->rectList.top;
714 if (ld->cur < ld->top)
715 ld->top = ld->cur;
716 else if (ld->cur >= ld->top + rows)
717 ld->top = ld->cur + 1 - rows;
718 /* don't waste space by scrolling end up */
719 waste = ld->top + rows - ld->listlen;
720 if (waste>0 && ld->top) {
721 ld->top -= waste;
722 if (ld->top < 0)
723 ld->top = 0;
724 }
725 }
726 /*=====================================================
727 * display_string -- Draw string in rectangle
728 * handle embedded carriage returns
729 *===================================================*/
730 static void
display_string(UIWINDOW uiwin,LLRECT rect,STRING text)731 display_string (UIWINDOW uiwin, LLRECT rect, STRING text)
732 {
733 INT max = rect->right - rect->left + 2;
734 STRING str = stdalloc(max), p2;
735 WINDOW *win = uiw_win(uiwin);
736 INT row = rect->top;
737 wipe_window_rect(uiwin, rect);
738 for (row = rect->top; row <= rect->bottom; ++row) {
739 str[0] = 0;
740 p2 = str;
741 while (p2<str+max && text[0] && !islinebreak(text[0]))
742 *p2++ = *text++;
743 *p2 = 0;
744 mvccwaddstr(win, row, rect->left, str);
745 if (!text[0])
746 break;
747 else
748 ++text;
749 }
750 stdfree(str);
751 }
752 /*==========================================
753 * show_big_list - Show name list in list screen
754 *========================================*/
755 void
show_big_list(INDISEQ seq,INT top,INT cur,INT mark)756 show_big_list (INDISEQ seq,
757 INT top,
758 INT cur,
759 INT mark)
760 {
761 /*
762 TODO: To be resizable like popup list, need a listdisp structure,
763 and need to repaint that RHS menu, as its height will vary
764 just to be scrollable details doesn't require repainting the RHS menu
765 But in any case the real problem is that
766 show_big_list (screen.c) is called by list_browse (screen.c)
767 which is called by browse_list (lbrowse.c!), and it handles menus
768 and listdisp is local to screen.c right now, so browse_list can't have one
769 A solution would be to pass in what is known from browse_list, and then
770 manufacture a listdisp here
771 - Perry, 2002/01/01
772 */
773 static STRING empstr51 = (STRING) " ";
774 UIWINDOW uiwin = main_win;
775 WINDOW *win = uiw_win(uiwin);
776 INT i, j, row, len = length_indiseq(seq);
777 STRING key, name;
778 NODE recnode=0;
779 char scratch[200];
780 INT mode = 'n';
781 INT viewlines = 13;
782 BOOLEAN scrollable = (viewlines < len);
783
784 calc_indiseq_names(seq); /* we certainly need the names */
785
786 for (i = LIST_LINES+2; i < LIST_LINES+2+viewlines; i++)
787 mvccwaddstr(win, i, 1, empstr51);
788 row = LIST_LINES+2;
789 for (i = top, j = 0; j < viewlines && i < len; i++, j++) {
790 element_indiseq(seq, i, &key, &name);
791 recnode = key_to_type(key, 0);
792 if (i == 0 && scrollable) mvwaddch(win, row, 1, '^');
793 if (i == len-1 && scrollable) mvwaddch(win, row, 1, '$');
794 if (i == mark) mvwaddch(win, row, 2, 'x');
795 if (i == cur) {
796 INT drow=1;
797 INT scroll=0;
798 BOOLEAN reuse=FALSE;
799 struct tag_llrect rectList;
800 rectList.top = drow;
801 rectList.bottom = drow + LIST_LINES-1;
802 rectList.left = 1;
803 rectList.right = get_main_screen_width()-2;
804 mvwaddch(win, row, 3, '>');
805 show_record(main_win, key, mode, &rectList, &scroll, reuse);
806 }
807 scratch[0] =0;
808 if (name) {
809 SURCAPTYPE surcaptype = DOSURCAP;
810 if (!getlloptint("UppercaseSurnames", 1))
811 surcaptype = NOSURCAP;
812 name = manip_name(name, surcaptype, REGORDER, 40);
813 llstrapps(scratch, sizeof(scratch), uu8, name);
814 llstrapps(scratch, sizeof(scratch), uu8, " ");
815 }
816 if(getlloptint("DisplayKeyTags", 0) > 0) {
817 llstrappf(scratch, sizeof(scratch), uu8, "(i%s)", key_of_record(recnode));
818 } else {
819 llstrappf(scratch, sizeof(scratch), uu8, "(%s)", key_of_record(recnode));
820 }
821 mvccwaddstr(win, row, 4, scratch);
822 row++;
823 }
824 }
825 /*==============================================
826 * paint_list_screen -- Paint list browse screen
827 *============================================*/
828 void
paint_list_screen(void)829 paint_list_screen (void)
830 {
831 UIWINDOW uiwin = main_win;
832 WINDOW *win = uiw_win(uiwin);
833 INT row, col;
834 uierase(uiwin);
835 draw_win_box(win);
836 show_horz_line(uiwin, LIST_LINES+1, 0, ll_cols);
837 show_horz_line(uiwin, ll_lines-3, 0, ll_cols);
838 show_vert_line(uiwin, LIST_LINES+1, 52, 15);
839 mvwaddch(win, LIST_LINES+1, 52, get_gr_ttee());
840 mvccwaddstr(win, LIST_LINES+2, 54, _("Choose an operation:"));
841 row = LIST_LINES+3; col = 55;
842 mvccwaddstr(win, row++, col, _("j Move down list"));
843 mvccwaddstr(win, row++, col, _("k Move up list"));
844 mvccwaddstr(win, row++, col, _("e Edit this person"));
845 mvccwaddstr(win, row++, col, _("i Browse this person"));
846 mvccwaddstr(win, row++, col, _("m Mark this person"));
847 mvccwaddstr(win, row++, col, _("r Remove from list"));
848 mvccwaddstr(win, row++, col, _("t Enter tandem mode"));
849 mvccwaddstr(win, row++, col, _("n Name this list"));
850 mvccwaddstr(win, row++, col, _("b Browse new persons"));
851 mvccwaddstr(win, row++, col, _("a Add to this list"));
852 mvccwaddstr(win, row++, col, _("x Swap mark/current"));
853 mvccwaddstr(win, row++, col, _(qSmn_quit));
854 }
855 /*==============================================
856 * listui_placecursor_main -- Get location for cursor in the large list browse window
857 *============================================*/
858 void
listui_placecursor_main(INT * prow,INT * pcol)859 listui_placecursor_main (INT * prow, INT * pcol)
860 {
861 *prow = LIST_LINES+2;
862 *pcol = 75;
863 }
864