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