1 /*
2    Copyright (c) 1991-1999 Thomas T. Wetmore IV
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 /* modified 05 Jan 2000 by Paul B. McBride (pmcbride@tiac.net) */
25 /*=============================================================
26  * show.c -- Curses version of display functions
27  * Copyright(c) 1992-94 by T.T. Wetmore IV; all rights reserved
28  * pre-SourceForge version information:
29  *   2.3.4 - 24 Jun 93    2.3.5 - 24 Aug 93
30  *   3.0.0 - 14 Sep 94    3.0.2 - 24 Dec 94
31  *   3.0.3 - 03 May 95
32  *===========================================================*/
33 
34 #include "llstdlib.h"
35 #include "table.h"
36 #include "translat.h"
37 #include "gedcom.h"
38 #include "indiseq.h"
39 #include "cache.h"
40 #include "liflines.h"
41 #include "lloptions.h"
42 #include "date.h"
43 #include "zstr.h"
44 
45 #include "llinesi.h"
46 #include "screen.h"
47 #include "cscurses.h"
48 
49 /*********************************************
50  * global/exported variables
51  *********************************************/
52 
53 INT Scroll1=0;
54 
55 /*********************************************
56  * external/imported variables
57  *********************************************/
58 
59 extern BOOLEAN opt_nocb;	/* TRUE to suppress display of cb. data */
60 extern INT listbadkeys;
61 extern char badkeylist[];
62 extern STRING qSmisskeys;
63 extern STRING qSdspl_indi,qSdspl_fath,qSdspl_moth,qSdspl_spouse,qSdspl_child;
64 extern STRING qSdspa_resi,qSdspa_div;
65 extern STRING qSdspa_mar,qSdspa_bir,qSdspa_chr,qSdspa_dea,qSdspa_bur,qSdspa_chbr;
66 extern STRING qSdspl_mar,qSdspl_bir,qSdspl_chr,qSdspl_dea,qSdspl_bur;
67 extern STRING qSdspa_eng,qSdspa_marc;
68 extern STRING qSdspl_eng,qSdspl_marc;
69 
70 /*********************************************
71  * local types
72  *********************************************/
73 
74 typedef char *LINESTRING;
75 struct tag_prefix {
76 	STRING tag;
77 	STRING prefix;
78 };
79 
80 /*********************************************
81  * local enums & defines
82  *********************************************/
83 
84 /* to handle large families, this needs to be made a regular
85 variable, and checked & resized at init_display_indi time */
86 #define MAXOTHERS 30
87 
88 /*********************************************
89  * local function prototypes
90  *********************************************/
91 
92 /* alphabetical */
93 static void add_child_line(INT, RECORD, INT width);
94 static void add_spouse_line(INT, NODE, NODE, INT width);
95 static BOOLEAN append_event(STRING * pstr, STRING evt, INT * plen, INT minlen);
96 static void disp_person_birthdeath(ZSTR zstr, RECORD irec, struct tag_prefix * tags, RFMT rfmt);
97 static void disp_person_name(ZSTR zstr, STRING prefix, RECORD irec, INT width);
98 static void indi_events(STRING outstr, NODE indi, INT len);
99 static void init_display_indi(RECORD irec, INT width);
100 static void init_display_fam(RECORD frec, INT width);
101 static void pedigree_line(CANVASDATA canvas, INT x, INT y, STRING string, INT overflow);
102 static STRING person_display(NODE, NODE, INT);
103 static void put_out_line(UIWINDOW uiwin, INT x, INT y, STRING string, INT maxcol, INT flag);
104 static STRING sh_fam_to_event_shrt(NODE node, STRING tag, STRING head
105 	, INT len);
106 static STRING sh_indi_to_event_long(NODE node, STRING tag
107 	, STRING head, INT len);
108 static STRING sh_indi_to_event_shrt(NODE node, STRING tag
109 	, STRING head, INT len);
110 
111 /*********************************************
112  * local variables
113  *********************************************/
114 
115 static LINESTRING Sfath, Smoth, Smarr;
116 static ZSTR Spers=0, Sbirt=0, Sdeat=0;
117 static ZSTR Shusb=0, Swife=0;
118 static ZSTR Shbirt=0, Shdeat=0, Swbirt=0, Swdeat=0;
119 static LINESTRING Sothers[MAXOTHERS];
120 static INT liwidth;
121 static INT Solen = 0;
122 static INT Scroll2 = 0;
123 static INT number_child_enable = 0;
124 static struct tag_prefix f_birth_tags[] = {
125 	{ "BIRT", N_("born") } /* GEDCOM BIRT tag, label to precede date on display */
126 	,{ "CHR", N_("bapt") } /* GEDCOM CHR tag, label to precede date on display */
127 	,{ "BAPM", N_("bapt") } /* GEDCOM BAPM tag, label to precede date on display */
128 	,{ "BARM", N_("barm") } /* GEDCOM BARM tag, label to precede date on display */
129 	,{ "BASM", N_("basm") } /* GEDCOM BASM tag, label to precede date on display */
130 	,{ "BLES", N_("bles") }
131 	,{ "ADOP", N_("adop") }
132 	,{ "RESI", N_("resi") }
133 	,{ NULL, NULL }
134 };
135 static struct tag_prefix f_death_tags[] = {
136 	{ "DEAT", N_("died") }
137 	,{ "BURI", N_("buri") }
138 	,{ "CREM", N_("crem") }
139 	,{ NULL, NULL }
140 };
141 
142 /*********************************************
143  * local function definitions
144  * body of module
145  *********************************************/
146 
147 /*===============================================
148  * init_show_module -- Initialize display variables
149  *=============================================*/
150 void
init_show_module(void)151 init_show_module (void)
152 {
153 	INT i;
154 	liwidth = ll_cols+1;
155 
156 	Spers = zs_new();
157 	Sbirt = zs_new();
158 	Sdeat = zs_new();
159 	Sfath = (LINESTRING)stdalloc(liwidth);
160 	Smoth = (LINESTRING)stdalloc(liwidth);
161 	Smarr = (LINESTRING)stdalloc(liwidth);
162 	Shusb = zs_new();
163 	Shbirt = zs_new();
164 	Shdeat = zs_new();
165 	Swife = zs_new();
166 	Swbirt = zs_new();
167 	Swdeat = zs_new();
168 	for (i=0; i<MAXOTHERS; i++)
169 		Sothers[i] = (LINESTRING)stdalloc(liwidth);
170 	init_disp_reformat();
171 }
172 /*===============================================
173  * term_show_module -- Free memory used by show module
174  *=============================================*/
175 void
term_show_module(void)176 term_show_module (void)
177 {
178 	INT i;
179 
180 	zs_free(&Spers);
181 	zs_free(&Sbirt);
182 	zs_free(&Sdeat);
183 	stdfree(Sfath);
184 	stdfree(Smoth);
185 	stdfree(Smarr);
186 	zs_free(&Shusb);
187 	zs_free(&Shbirt);
188 	zs_free(&Shdeat);
189 	zs_free(&Swife);
190 	zs_free(&Swbirt);
191 	zs_free(&Swdeat);
192 	for (i=0; i<MAXOTHERS; i++)
193 		stdfree(Sothers[i]);
194 }
195 /*===============================================
196  * disp_person_name -- Display person's name
197  *  append REFN or key, & include title if room
198  * Created: 2003-01-11 (Perry Rapp)
199  *=============================================*/
200 static void
disp_person_name(ZSTR zstr,STRING prefix,RECORD irec,INT width)201 disp_person_name (ZSTR zstr, STRING prefix, RECORD irec, INT width)
202 {
203 /* TODO: width handling is wrong, it should not be byte based */
204 	ZSTR zkey = zs_news(key_of_record(nztop(irec)));
205 	/* ": " between prefix and name, and " ()" for key */
206 	INT avail = width - strlen(prefix)-zs_len(zkey)-5;
207 	STRING name = indi_to_name(nztop(irec), avail);
208 	zs_clear(zstr);
209 	zs_setf(zstr, "%s: %s ", prefix, name);
210 	avail = width - zs_len(zstr)-zs_len(zkey)-2;
211 	if (avail > 10) {
212 		STRING t = indi_to_title(nztop(irec), avail-3);
213 		if (t) zs_appf(zstr, "[%s] ", t);
214 	}
215 /* TODO: add more names if room */
216 /* TODO: first implement new function namelist to get us all names */
217 	if(getlloptint("DisplayKeyTags", 0) > 0) {
218 		zs_appf(zstr, "(i%s)", zs_str(zkey));
219 	} else {
220 		zs_appf(zstr, "(%s)", zs_str(zkey));
221 	}
222 	zs_free(&zkey);
223 }
224 /*===============================================
225  * disp_person_birthdeath -- Print birth string
226  *  Try to find date & place info for birth (or approx)
227  * Created: 2003-01-12 (Perry Rapp)
228  *=============================================*/
229 static void
disp_person_birthdeath(ZSTR zstr,RECORD irec,struct tag_prefix * tags,RFMT rfmt)230 disp_person_birthdeath (ZSTR zstr, RECORD irec, struct tag_prefix * tags, RFMT rfmt)
231 {
232 	struct tag_prefix *tg;
233 	INT ct=0;
234 	ZSTR ztemp=zs_new();
235 	zs_clear(zstr);
236 	for (tg = tags; tg->tag; ++tg) {
237 		STRING date=NULL, place=NULL;
238 		zs_clear(ztemp);
239 		ct = 0;
240 		record_to_date_place(irec, tg->tag, &date, &place, &ct);
241 		if (!ct) continue;
242 		if (rfmt && rfmt->rfmt_date) /* format date */
243 			date = (*rfmt->rfmt_date)(date);
244 		zs_appf(ztemp, "%s: ", _(tg->prefix));
245 
246 		if (date) {
247 			zs_apps(ztemp, date);
248 		}
249 		if (place) {
250 			if (date)
251 				zs_appf(ztemp, ", ");
252 			if (rfmt && rfmt->rfmt_plac) /* format place */
253 				place = (*rfmt->rfmt_plac)(place);
254 			zs_apps(ztemp, place);
255 		}
256 		if (!date && !place) {
257 			// Git #308: INDI with BIRT/DEAT without DATE/PLAC displays "Y"
258 			// The 3.0.62 behaviour was to display nothing.
259 			// This sounds more appropriate so reverting to that behaviour.
260 			//zs_apps(ztemp, "Y");
261 		}
262 		if (ct>1) {
263 			zs_appf(ztemp, " (%d alt)", ct-1);
264 		}
265 		/* append current info to accumulated info */
266 		if (zs_len(zstr)>0) {
267 			zs_apps(zstr, ", ");
268 		}
269 		zs_appz(zstr, ztemp);
270 	}
271 	zs_free(&ztemp);
272 }
273 /*===============================================
274  * init_display_indi -- Initialize display person
275  *  Fill in all the local buffers for normal person
276  *  display mode (Spers, Sbirt, etc)
277  *=============================================*/
278 static void
init_display_indi(RECORD irec,INT width)279 init_display_indi (RECORD irec, INT width)
280 {
281 	NODE pers=nztop(irec);
282 	NODE this_fam = 0;
283 	INT nsp, nch, num, nm;
284 	STRING s;
285 	NODE fth;
286 	NODE mth;
287 	CACHEEL icel;
288 
289 	ASSERT(width < ll_cols+1); /* size of Spers etc */
290 
291 
292 	ASSERT(pers);
293 
294 	disp_person_name(Spers, _(qSdspl_indi), irec, width);
295 
296 	disp_person_birthdeath(Sbirt, irec, f_birth_tags, &disp_long_rfmt);
297 
298 	disp_person_birthdeath(Sdeat, irec, f_death_tags, &disp_long_rfmt);
299 
300 	fth = indi_to_fath(pers);
301 	s = person_display(fth, NULL, width-13);
302 	if (s) llstrncpyf(Sfath, liwidth, uu8, "  %s: %s", _(qSdspl_fath), s);
303 	else llstrncpyf(Sfath, liwidth, uu8, "  %s:", _(qSdspl_fath));
304 
305 	mth = indi_to_moth(pers);
306 	s = person_display(mth, NULL, width-13);
307 	if (s) llstrncpyf(Smoth, liwidth, uu8, "  %s: %s", _(qSdspl_moth), s);
308 	else llstrncpyf(Smoth, liwidth, uu8, "  %s:", _(qSdspl_moth));
309 
310 	Solen = 0;
311 	nsp = nch = 0;
312 	icel = indi_to_cacheel_old(pers);
313 	lock_cache(icel);
314 	FORFAMSS(pers, fam, sp, num)
315 		if (sp) add_spouse_line(++nsp, sp, fam, width);
316 	        if (this_fam != fam) {
317 		        this_fam = fam; /* only do each family once */
318 			FORCHILDREN(fam, chld, nm)
319 				if(chld) add_child_line(++nch, chld, width);
320 			ENDCHILDREN
321 		}
322 	ENDFAMSS
323 	unlock_cache(icel);
324 }
325 /*==============================
326  * show_indi_vitals -- Display person using
327  * the traditional LifeLines vitals format.
328  *  uiwin:  [IN] which curses window (usually MAIN_WIN)
329  *  pers:   [IN] whom to display
330  *  row:    [IN] starting row to draw upon
331  *  hgt:    [IN] how many rows to use
332  *  width:  [IN] how many columns to use
333  *  scroll: [IN] how many rows to skip over at top
334  *  reuse:  [IN] flag to avoid recomputing display strings
335  * Caller sets reuse flag if it knows that this is the same
336  * person displayed last.
337  *============================*/
338 void
show_indi_vitals(UIWINDOW uiwin,RECORD irec,LLRECT rect,INT * scroll,BOOLEAN reuse)339 show_indi_vitals (UIWINDOW uiwin, RECORD irec, LLRECT rect
340 	, INT *scroll, BOOLEAN reuse)
341 {
342 	INT i;
343 	INT localrow;
344 	INT overflow;
345 	WINDOW * win = uiw_win(uiwin);
346 	INT row = rect->top;
347 	INT width = rect->right - rect->left + 1;
348 	INT hgt = rect->bottom - rect->top + 1;
349 
350 	badkeylist[0] = '\0';
351 	listbadkeys = 1;
352 	if (hgt<=0) return;
353 	if (!reuse)
354 		init_display_indi(irec, width);
355 	wipe_window_rect(uiwin, rect);
356 	if (*scroll) {
357 		if (*scroll > Solen + 5 - hgt)
358 			*scroll = Solen + 5 - hgt;
359 		if (*scroll < 0)
360 			*scroll = 0;
361 	}
362 	/* we keep putting lines out til we run out or exhaust our alloted
363 	height */
364 	localrow = row - *scroll;
365 
366 	mvccwaddnstr(win, row+0, 1, zs_str(Spers), width-1);
367 	if (hgt==1) return;
368 	mvccwaddnstr(win, row+1, 1, zs_str(Sbirt), width-1);
369 	if (hgt==2) return;
370 	mvccwaddnstr(win, row+2, 1, zs_str(Sdeat), width-1);
371 	if (hgt==3) return;
372 	mvccwaddnstr(win, row+3, 1, Sfath, width-1);
373 	if (hgt==4) return;
374 	mvccwaddnstr(win, row+4, 1, Smoth, width-1);
375 	if (hgt==5) return;
376 	for (i = *scroll; i < Solen && i < hgt-5+ *scroll; i++)
377 	{
378 		/* the other lines scroll internally, and we
379 		mark the top one displayed if not the actual top one, and the
380 		bottom one displayed if not the actual bottom */
381 		overflow = ((i+1 == hgt-5+ *scroll)&&(i+1 != Solen));
382 		if (*scroll && (i == *scroll))
383 			overflow = 1;
384 		put_out_line(uiwin, localrow+5+i, rect->left, Sothers[i], rect->right, overflow);
385 	}
386 	listbadkeys = 0;
387 	if(badkeylist[0]) {
388 		char buf[132];
389 		llstrncpyf(buf, sizeof(buf), uu8, "%s: %.40s", _(qSmisskeys), badkeylist);
390 		message(buf);
391 	}
392 }
393 /*=============================================
394  * add_spouse_line -- Add spouse line to others
395  *===========================================*/
396 static void
add_spouse_line(INT num,NODE indi,NODE fam,INT width)397 add_spouse_line (INT num, NODE indi, NODE fam, INT width)
398 {
399 	STRING line, ptr=Sothers[Solen];
400 	INT mylen=liwidth;
401 	num=num; /* unused */
402 	if (Solen >= MAXOTHERS) return;
403 	if (mylen>width) mylen=width;
404 	llstrcatn(&ptr, " ", &mylen);
405 	llstrcatn(&ptr, _(qSdspl_spouse), &mylen);
406 	llstrcatn(&ptr, ": ", &mylen);
407 	line = person_display(indi, fam, mylen-1);
408 	llstrcatn(&ptr, line, &mylen);
409 	++Solen;
410 }
411 /*===========================================
412  * add_child_line -- Add child line to others
413  *=========================================*/
414 static void
add_child_line(INT num,RECORD irec,INT width)415 add_child_line (INT num, RECORD irec, INT width)
416 {
417 	STRING line;
418 	STRING child = _(qSdspl_child);
419 	if (Solen >= MAXOTHERS) return;
420 	line = person_display(nztop(irec), NULL, width-15);
421 	if (number_child_enable)
422 		llstrncpyf(Sothers[Solen], liwidth, uu8, "  %2d%s: %s", num, child, line);
423 	else
424 		llstrncpyf(Sothers[Solen], liwidth, uu8, "    %s: %s", child, line);
425 	Sothers[Solen++][width-2] = 0;
426 }
427 /*==============================================
428  * init_display_fam -- Initialize display family
429  *============================================*/
430 static void
init_display_fam(RECORD frec,INT width)431 init_display_fam (RECORD frec, INT width)
432 {
433 	NODE fam=nztop(frec);
434 	NODE husb=0, wife=0;
435 	STRING s=0;
436 	ZSTR famkey = zs_news(key_of_record(fam));
437 	INT nch, nm, wtemp;
438 	STRING father = _(qSdspl_fath);
439 	STRING mother = _(qSdspl_moth);
440 	RECORD ihusb=0, iwife=0;
441 	INT husbstatus = 0;
442 	INT wifestatus = 0;
443 	NODE fnode;
444 
445 	/* Get the first two spouses in the family and use them rather than
446 	 * displaying first husband and first mother
447 	 * This causes a more reasonable presentation of non-traditional
448 	 * familes.  Also it will display first hustband and first wife
449 	 * for traditional families (as there's only one) and the db routines
450 	 * insert HUSB records before WIFE records.
451 	 */
452 	if (fam) {
453 		fnode = nchild(fam);
454 		husbstatus = next_spouse(&fnode,&ihusb);
455 		husb = nztop(ihusb);
456 		if (fnode) {
457 			fnode = nsibling(fnode);
458 			wifestatus = next_spouse(&fnode,&iwife);
459 			wife = nztop(iwife);
460 		}
461 	}
462 	/* if the only spouse is female, list in second slot
463 	 * hiding the non-traditional behavior
464 	 */
465 	if (!wife && husb && SEX(husb) == SEX_FEMALE) {
466 		wife = husb;
467 		husb = 0;
468 		iwife = ihusb;
469 		ihusb = 0;
470 		wifestatus = husbstatus;
471 		husbstatus  = 0;
472 	}
473 
474 	if (husbstatus == 1) {
475 		INT avail = width - zs_len(famkey) - 3;
476 		disp_person_name(Shusb, SEX(husb)==SEX_MALE?father:mother, ihusb, avail);
477 	} else {
478 		zs_setf(Shusb, "%s:", father);
479 		if (husbstatus == -1)
480 			zs_apps(Shusb, "??");
481 	}
482 	if(getlloptint("DisplayKeyTags", 0) > 0) {
483 		zs_appf(Shusb, " (f%s)", zs_str(famkey));
484 	} else {
485 		zs_appf(Shusb, " (%s)", zs_str(famkey));
486 	}
487 	zs_free(&famkey);
488 
489 	disp_person_birthdeath(Shbirt, ihusb, f_birth_tags, &disp_long_rfmt);
490 	disp_person_birthdeath(Shdeat, ihusb, f_death_tags, &disp_long_rfmt);
491 
492 	if (wifestatus == 1) {
493 		INT avail = width;
494 		disp_person_name(Swife, SEX(wife)==SEX_MALE?father:mother, iwife, avail);
495 	} else {
496 		zs_setf(Swife, "%s:", mother);
497 		if (wifestatus == -1)
498 			zs_apps(Swife, "??");
499 	}
500 
501 	disp_person_birthdeath(Swbirt, iwife, f_birth_tags, &disp_long_rfmt);
502 	disp_person_birthdeath(Swdeat, iwife, f_death_tags, &disp_long_rfmt);
503 
504 	/* Find marriage (or marital contract, or engagement) */
505 	s = sh_indi_to_event_long(fam, "MARR", _(qSdspl_mar), width-3);
506 	if (!s) s = sh_indi_to_event_long(fam, "MARC", _(qSdspl_marc), width-3);
507 	if (!s) s = sh_indi_to_event_long(fam, "ENGA", _(qSdspl_eng), width-3);
508 	if (s) llstrncpyf(Smarr, liwidth, uu8, s);
509 	else llstrncpyf(Smarr, liwidth, uu8, _(qSdspl_mar));
510 
511 	/* append divorce to marriage line, if room */
512 	/* (Might be nicer to make it a separate, following line */
513 	wtemp = width-5 - strlen(Smarr);
514 	if (wtemp > 10) {
515 		s = sh_indi_to_event_long(fam, "DIV", _(qSdspa_div), wtemp);
516 		if (s)
517 			llstrncpyf(Smarr+strlen(Smarr), liwidth-strlen(Smarr), uu8, ", %s", s);
518 	}
519 
520 	Solen = 0;
521 	nch = 0;
522 	FORCHILDREN(fam, chld, nm)
523 		add_child_line(++nch, chld, width);
524 	ENDCHILDREN
525 	release_record(ihusb);
526 	release_record(iwife);
527 }
528 /*===================================
529  * show_fam_vitals -- Display family
530  * [in] fam:  whom to display
531  * [in] row:   starting row to use
532  * [in] hgt:   how many rows allowed
533  * [in] width: how many columns allowed
534  * [in] reuse: flag to save recalculating display strings
535  *=================================*/
536 void
show_fam_vitals(UIWINDOW uiwin,RECORD frec,INT row,INT hgt,INT width,INT * scroll,BOOLEAN reuse)537 show_fam_vitals (UIWINDOW uiwin, RECORD frec, INT row, INT hgt
538 	, INT width, INT *scroll, BOOLEAN reuse)
539 {
540 	INT i, len;
541 	INT localrow;
542 	INT overflow;
543 	char buf[132];
544 	INT maxcol = width-1;
545 	WINDOW * win = uiw_win(uiwin);
546 	struct tag_llrect rect;
547 
548 	rect.bottom = row+hgt-1;
549 	rect.top = row;
550 	rect.left = 1;
551 	rect.right = width-2;
552 
553 	badkeylist[0] = '\0';
554 	listbadkeys = 1;
555 	if (!reuse)
556 		init_display_fam(frec, width);
557 	wipe_window_rect(uiwin, &rect);
558 	if (*scroll) {
559 		if (*scroll > Solen + 7 - hgt)
560 			*scroll = Solen + 7 - hgt;
561 		if (*scroll < 0)
562 			*scroll = 0;
563 	}
564 	localrow = row - *scroll;
565 	mvccwaddnstr(win, row+0, 1, zs_str(Shusb), width-2);
566 	if (hgt==1) return;
567 	mvccwaddnstr(win, row+1, 1, zs_str(Shbirt), width-2);
568 	if (hgt==2) return;
569 	mvccwaddnstr(win, row+2, 1, zs_str(Shdeat), width-2);
570 	if (hgt==3) return;
571 	mvccwaddnstr(win, row+3, 1, zs_str(Swife), width-2);
572 	if (hgt==4) return;
573 	mvccwaddnstr(win, row+4, 1, zs_str(Swbirt), width-2);
574 	if (hgt==5) return;
575 	mvccwaddnstr(win, row+5, 1, zs_str(Swdeat), width-2);
576 	if (hgt==6) return;
577 	mvccwaddnstr(win, row+6, 1, Smarr, width-2);
578 	for (i = *scroll; i < Solen && i < hgt-7+*scroll; i++)
579 	{
580 		overflow = ((i+1 == hgt-7+*scroll)&&(i+1 != Solen));
581 		if (*scroll && (i == *scroll))
582 			overflow = 1;
583 		len = strlen(Sothers[i]+1);
584 		put_out_line(uiwin, localrow+7+i, 1, Sothers[i]+1, maxcol, overflow);
585 	}
586 	listbadkeys = 0;
587 	if(badkeylist[0]) {
588 		sprintf(buf, "WARNING: missing keys: %.40s", badkeylist);
589 		message(buf);
590 	}
591 }
592 /*================================================
593  * show_ancestors -- Show pedigree/ancestors
594  * Created: 2001/02/04, Perry Rapp
595  *==============================================*/
596 void
show_ancestors(UIWINDOW uiwin,RECORD irec,LLRECT rect,INT * scroll,BOOLEAN reuse)597 show_ancestors (UIWINDOW uiwin, RECORD irec, LLRECT rect
598 	, INT * scroll, BOOLEAN reuse)
599 {
600 	struct tag_canvasdata canvas;
601 		/* parameters for drawing tree */
602 	canvas.rect = rect;
603 	canvas.scroll = *scroll;
604 	canvas.param = (void *)uiwin;
605 	canvas.line = pedigree_line;
606 		/* clear & draw pedigree */
607 	wipe_window_rect(main_win, rect);
608 	pedigree_draw_ancestors(irec, &canvas, reuse);
609 	*scroll = canvas.scroll;
610 }
611 /*================================================
612  * show_descendants -- Show pedigree/descendants
613  * Created: 2001/02/04, Perry Rapp
614  *==============================================*/
615 void
show_descendants(UIWINDOW uiwin,RECORD rec,LLRECT rect,INT * scroll,BOOLEAN reuse)616 show_descendants (UIWINDOW uiwin, RECORD rec, LLRECT rect
617 	, INT * scroll, BOOLEAN reuse)
618 {
619 	struct tag_canvasdata canvas;
620 		/* parameters for drawing tree */
621 	canvas.rect = rect;
622 	canvas.scroll = *scroll;
623 	canvas.param = (void *)uiwin;
624 	canvas.line = pedigree_line;
625 		/* clear & draw pedigree */
626 	wipe_window_rect(main_win, rect);
627 	pedigree_draw_descendants(rec, &canvas, reuse);
628 	*scroll = canvas.scroll;
629 }
630 /*================================================
631  * show_gedcom -- Display record in raw gedcom format
632  * Created: 2001/01/27, Perry Rapp
633  *==============================================*/
634 void
show_gedcom(UIWINDOW uiwin,RECORD rec,INT gdvw,LLRECT rect,INT * scroll,BOOLEAN reuse)635 show_gedcom (UIWINDOW uiwin, RECORD rec, INT gdvw, LLRECT rect
636 	, INT * scroll, BOOLEAN reuse)
637 {
638 	struct tag_canvasdata canvas;
639 		/* parameters for drawing */
640 	canvas.rect = rect;
641 	canvas.scroll = *scroll;
642 	canvas.param = (void *)uiwin;
643 	canvas.line = pedigree_line;
644 		/* clear & draw pedigree */
645 	wipe_window_rect(uiwin, rect);
646 	pedigree_draw_gedcom(rec, gdvw, &canvas, reuse);
647 	*scroll = canvas.scroll;
648 }
649 /*================================================
650  * switch_scrolls -- Interchange scroll1 & scroll2
651  * This is how the tandem modes do their drawing of
652  * the lower part -- they swap in the second set of
653  * scroll briefly for displaying the lower part, then
654  * swap back to normal as soon as finishing lower part.
655  * Created: 2001/02/04, Perry Rapp
656  *==============================================*/
657 void
switch_scrolls(void)658 switch_scrolls (void)
659 {
660 	INT save = Scroll1;
661 	Scroll1 = Scroll2;
662 	Scroll2 = save;
663 }
664 /*===============================================================
665  * indi_to_ped_fix -- Construct person STRING for pedigree screen
666  * returns static buffer
667  *  indi: [in] person to display
668  *  len:  [in] max width
669  * Does internal-to-display translation
670  *=============================================================*/
671 STRING
indi_to_ped_fix(NODE indi,INT len)672 indi_to_ped_fix (NODE indi, INT len)
673 {
674 	STRING bevt, devt, name, key;
675 	static char scratch[100];
676 	char tmp1[100];
677 
678 /*	return person_display(indi, 0, len); */
679 
680 	if (!indi) return (STRING) "------------";
681 	bevt = event_to_date(BIRT(indi), TRUE);
682 	if (!bevt) bevt = event_to_date(BAPT(indi), TRUE);
683 	if (!bevt) bevt = (STRING) "";
684 	devt = event_to_date(DEAT(indi), TRUE);
685 	if (!devt) devt = event_to_date(BURI(indi), TRUE);
686 	if (!devt) devt = (STRING) "";
687 	if (keyflag) {
688 		key = key_of_record(indi);
689 		if(getlloptint("DisplayKeyTags", 0) > 0) {
690 			sprintf(tmp1, " [%s-%s] (i%s)", bevt, devt, key);
691 		} else {
692 			sprintf(tmp1, " [%s-%s] (%s)", bevt, devt, key);
693 		}
694 	}
695 	else
696 		sprintf(tmp1, " (%s-%s)", bevt, devt);
697 	name = indi_to_name(indi, len - strlen(tmp1));
698 	strcpy(scratch, name);
699 	strcat(scratch, tmp1);
700 	return scratch;
701 }
702 /*=============================================
703  * append_event -- Add an event if present to output string
704  *  pstr:   [I/O] end of printed event string
705  *  evt:    [IN]  event string (must be valid)
706  *  plen:   [I/O] max length of output
707  *  minlen: [IN]  threshold for caller to stop
708  * If event found, this prints it, advances *pstr, and reduces *plen
709  * returns FALSE if (*plen)<minlen after advancing
710  *  (signal to caller to stop)
711  * Created: 2001/07/04 (Perry Rapp)
712  *===========================================*/
713 static BOOLEAN
append_event(STRING * pstr,STRING evt,INT * plen,INT minlen)714 append_event (STRING * pstr, STRING evt, INT * plen, INT minlen)
715 {
716 	llstrcatn(pstr, ", ", plen);
717 	llstrcatn(pstr, evt, plen);
718 	return *plen >= minlen;
719 }
720 /*=============================================
721  * family_events -- Print string of events
722  *  outstr: [I/O] printed event string
723  *  indi:   [IN]  whom to display
724  *  fam:    [IN]  family record (used when displaying spouses)
725  *  len:    [IN]  max length of output
726  * If none are found, this will write a 0 to first char of outstr
727  * If anything written, starts with ", "
728  * Created: 2001/07/04 (Perry Rapp)
729  *===========================================*/
730 static void
family_events(STRING outstr,NODE indi,NODE fam,INT len)731 family_events (STRING outstr, NODE indi, NODE fam, INT len)
732 {
733 	STRING evt = NULL;
734 	STRING p = outstr;
735 	INT mylen = len;
736 	p[0] = 0;
737 
738 	/* find marriage (or marital contract, or engagement) */
739 	if (!evt) {
740 		evt = sh_fam_to_event_shrt(fam, "MARR", _(qSdspa_mar), mylen);
741 		if (evt && !append_event(&p, evt, &mylen, 10))
742 			return;
743 	}
744 	if (!evt) {
745 		evt = sh_fam_to_event_shrt(fam, "MARC", _(qSdspa_marc), mylen);
746 		if (evt && !append_event(&p, evt, &mylen, 10))
747 			return;
748 	}
749 	if (!evt) {
750 		evt = sh_fam_to_event_shrt(fam, "ENGA", _(qSdspa_eng), mylen);
751 		if (evt && !append_event(&p, evt, &mylen, 10))
752 			return;
753 	}
754 
755 /*
756 	mylen is up-to-date how many chars left we have
757 	(we keep passing mylen-2 because events are prefixed with ", ")
758 	if we ever have too few left (<10), append_event will return FALSE
759 */
760 	if (!opt_nocb) {
761 		NODE chld;
762 		/* Look for birth or christening of first child */
763 		if ((chld = fam_to_first_chil(fam))) {
764 			evt = sh_indi_to_event_shrt(chld, "BIRT", _(qSdspa_chbr), mylen-2);
765 			if (evt && !append_event(&p, evt, &mylen, 10))
766 				return;
767 			if (!evt) {
768 				evt = sh_indi_to_event_shrt(chld, "CHR", _(qSdspa_chbr), mylen-2);
769 				if (evt && !append_event(&p, evt, &mylen, 10))
770 					return;
771 			}
772 		}
773 	}
774 	evt = sh_indi_to_event_shrt(indi, "BIRT", _(qSdspa_bir), mylen-2);
775 	if (evt && !append_event(&p, evt, &mylen, 10))
776 		return;
777 	evt = sh_indi_to_event_shrt(indi, "CHR", _(qSdspa_chr), mylen-2);
778 	if (evt && !append_event(&p, evt, &mylen, 10))
779 		return;
780 	evt = sh_indi_to_event_shrt(indi, "DEAT", _(qSdspa_dea), mylen-2);
781 	if (evt && !append_event(&p, evt, &mylen, 10))
782 		return;
783 	evt = sh_indi_to_event_shrt(indi, "BURI", _(qSdspa_bur), mylen-2);
784 	if (evt && !append_event(&p, evt, &mylen, 10))
785 		return;
786 }
787 /*=============================================
788  * indi_events -- Print string of events
789  *  outstr: [I/O] printed event string
790  *  indi:   [IN]  whom to display
791  *  len:    [IN]  max length of output
792  * If none are found, this will write a 0 to first char of outstr
793  * If anything written, starts with ", "
794  * Created: 2001/07/04 (Perry Rapp)
795  *===========================================*/
796 static void
indi_events(STRING outstr,NODE indi,INT len)797 indi_events (STRING outstr, NODE indi, INT len)
798 {
799 	STRING evt = NULL;
800 	INT width = (len-2)/2;
801 	STRING p = outstr;
802 	INT mylen = len;
803 	p[0] = 0;
804 
805 	evt = sh_indi_to_event_shrt(indi, "BIRT", _(qSdspa_bir), width);
806 	if (!evt)
807 		evt = sh_indi_to_event_shrt(indi, "CHR", _(qSdspa_chr), width);
808 	if (evt) {
809 		llstrcatn(&p, ", ", &mylen);
810 		llstrcatn(&p, evt, &mylen);
811 	}
812 	if (p == outstr)
813 		width = len;
814 	evt = sh_indi_to_event_shrt(indi, "DEAT", _(qSdspa_dea), width);
815 	if (!evt) evt = sh_indi_to_event_shrt(indi, "BURI", _(qSdspa_bur), width);
816 	if (evt) {
817 		llstrcatn(&p, ", ", &mylen);
818 		llstrcatn(&p, evt, &mylen);
819 	}
820 }
821 /*==========================================================
822  * max_keywidth -- Figure the width of the widest extant key
823  *========================================================*/
824 static INT
max_keywidth(void)825 max_keywidth (void)
826 {
827 	INT maxkey = xref_max_any();
828 	if (maxkey>9999) {
829 		if (maxkey>999999)
830 			return 7;
831 		if (maxkey>99999)
832 			return 6;
833 		return 5;
834 	}
835 	if (maxkey>999)
836 		return 4;
837 	if (maxkey>99)
838 		return 3;
839 	return 2;
840 }
841 /*=============================================
842  * person_display -- Create person display line
843  *  indi:  [in] whom to display
844  *  fam:   [in] family record (used when displaying spouses)
845  *  len:   max length of output
846  *===========================================*/
847 static STRING
person_display(NODE indi,NODE fam,INT len)848 person_display (NODE indi, NODE fam, INT len)
849 {
850 	static char scratch1[120];
851 	static char scratch2[100];
852 	STRING p;
853 	/* parentheses & leading space & possible "i" */
854 	INT keyspace = max_keywidth() + 4;
855 	INT evlen, namelen, temp;
856 	/* don't overflow scratch1, into which we catenate name & events */
857 	if (len > ARRSIZE(scratch1)-1)
858 		len = ARRSIZE(scratch1)-1;
859 
860 	/* keywidth for key, 2 for comma space, and split between name & events */
861 	evlen = (len-2-keyspace)/2;
862 	namelen = evlen;
863 
864 	if (!indi) return NULL;
865 
866 	/* test to see if name is short */
867 	p = indi_to_name(indi, 100);
868 	if ((temp = strlen(p)) < evlen) {
869 		/* name is short, give extra to events */
870 		evlen += (namelen - temp);
871 		namelen -= (namelen - temp);
872 	}
873 
874 	if (evlen > ARRSIZE(scratch2)-1) /* don't overflow name buffer */
875 		evlen = ARRSIZE(scratch2)-1;
876 	if (fam) {
877 		family_events(scratch2, indi, fam, evlen);
878 	} else {
879 		indi_events(scratch2, indi, evlen);
880 	}
881 
882 	/* give name any unused space events left */
883 	if ((INT)strlen(scratch2)<evlen)
884 		namelen += evlen-(INT)strlen(scratch2);
885 	p = scratch1;
886 	strcpy(p, indi_to_name(indi, namelen));
887 	p += strlen(p);
888 	if (scratch2[0]) {
889 		strcpy(p, scratch2);
890 		p += strlen(p);
891 	}
892 	if(getlloptint("DisplayKeyTags", 0) > 0) {
893 		snprintf(p, scratch1+len-p, " (i%s)", key_of_record(indi));
894 
895 	} else {
896 		snprintf(p, scratch1+len-p, " (%s)", key_of_record(indi));
897 	}
898 	return scratch1;
899 }
900 /*========================================================
901  * show_aux -- Show source, event or other record
902  *======================================================*/
903 void
show_aux(UIWINDOW uiwin,RECORD rec,INT mode,LLRECT rect,INT * scroll,BOOLEAN reuse)904 show_aux (UIWINDOW uiwin, RECORD rec, INT mode, LLRECT rect
905 	, INT * scroll, BOOLEAN reuse)
906 {
907 	if (mode == 'g')
908 		show_gedcom(uiwin, rec, GDVW_NORMAL, rect, scroll, reuse);
909 	else if (mode == 't')
910 		show_gedcom(uiwin, rec, GDVW_TEXT, rect, scroll, reuse);
911 	else
912 		show_gedcom(uiwin, rec, GDVW_EXPANDED, rect, scroll, reuse);
913 }
914 /*===============================================
915  * show_scroll - vertically scroll person display
916  *=============================================*/
917 void
show_scroll(INT delta)918 show_scroll (INT delta)
919 {
920 	Scroll1 += delta;
921 	if (Scroll1 < 0)
922 		Scroll1 = 0;
923 }
924 /*===================================
925  * show_scroll2 - scroll lower window
926  *  (in tandem mode)
927  *=================================*/
928 void
show_scroll2(INT delta)929 show_scroll2 (INT delta)
930 {
931 	Scroll2 += delta;
932 	if (Scroll2 < 0)
933 		Scroll2 = 0;
934 }
935 /*=================================
936  * show_reset_scroll - clear scroll
937  *===============================*/
938 void
show_reset_scroll(void)939 show_reset_scroll (void)
940 {
941 	Scroll1 = 0;
942 	Scroll2 = 0;
943 }
944 /*=====================================
945  * pedigree_line - callback from pedigree code
946  *  to put out each line of pedigree
947  *====================================*/
948 static void
pedigree_line(CANVASDATA canvas,INT y,INT x,STRING string,INT overflow)949 pedigree_line (CANVASDATA canvas, INT y, INT x, STRING string, INT overflow)
950 {
951 	if (!string || !string[0]) return;
952 	/* vertical clip to rect */
953 	if (y < canvas->rect->top || y > canvas->rect->bottom) return;
954 	/* horizontal clip to rect */
955 	if (x < canvas->rect->left) {
956 		INT delta = canvas->rect->left - x;
957 		if ((INT)strlen(string) <= delta) return;
958 		string += delta;
959 		x = canvas->rect->left;
960 	}
961 	put_out_line((UIWINDOW)canvas->param, y, x, string, canvas->rect->right, overflow);
962 }
963 /*=====================================
964  * put_out_line - move string to screen
965  * but also append ++ at end if flagged
966  * start string at x,y, and do not go beyond maxcol
967  * _disp version means string is already in display encoding
968  *====================================*/
969 static void
put_out_line(UIWINDOW uiwin,INT y,INT x,STRING string,INT maxcol,INT flag)970 put_out_line (UIWINDOW uiwin, INT y, INT x, STRING string, INT maxcol, INT flag)
971 {
972 	WINDOW * win = uiw_win(uiwin);
973 	static LINESTRING buffer=0; /* local buffer resized when needed */
974 	static INT buflen=0;
975 	INT maxlen = maxcol - x + 1;
976 	/* ensure enough room in buffer */
977 	if (!buflen || buflen < maxlen+1) {
978 		if (buffer) stdfree(buffer);
979 		buflen = maxlen+1;
980 		buffer = (LINESTRING)stdalloc(buflen);
981 	}
982 	/* TODO: Should convert to output codeset now, before limiting text */
983 
984 	/* copy into local buffer (here we enforce maxcol) */
985 	llstrncpy(buffer, string, buflen, uu8);
986 	if (flag) {
987 		/* put ++ against right, padding if needed */
988 		INT i = strlen(buffer);
989 		INT pos = maxcol-x-3;
990 		if (i>pos)
991 			i = pos;
992 		for (; i<pos; i++)
993 			buffer[i] = ' ';
994 		buffer[i++] = ' ';
995 		buffer[i++] = '+';
996 		buffer[i++] = '+';
997 		buffer[i++] = '\0';
998 	}
999 	mvccwaddstr(win, y, x, buffer);
1000 }
1001 /*==================================================================
1002  * show_childnumbers - toggle display of numbers for children
1003  *================================================================*/
1004 void
show_childnumbers(void)1005 show_childnumbers (void)
1006 {
1007 	number_child_enable = !number_child_enable;
1008 }
1009 /*================================
1010  * cache_stats -- Show cache stats
1011  *==============================*/
1012 void
display_cache_stats(void)1013 display_cache_stats (void)
1014 {
1015 	ZSTR zstr = zs_new();
1016 	ZSTR zstr_ind = get_cache_stats_indi();
1017 	ZSTR zstr_fam = get_cache_stats_fam();
1018 	zs_appf(zstr, _("Cached: I:%s; F:%s")
1019 		, zs_str(zstr_ind), zs_str(zstr_fam));
1020 	msg_info(zs_str(zstr));
1021 	zs_free(&zstr);
1022 	zs_free(&zstr_ind);
1023 	zs_free(&zstr_fam);
1024 }
1025 /*================================================
1026  * sh_indi_to_event -- Pass-thru to indi_to_event
1027  *  using long display reformatting
1028  *==============================================*/
1029 static STRING
sh_indi_to_event_long(NODE node,STRING tag,STRING head,INT len)1030 sh_indi_to_event_long (NODE node, STRING tag, STRING head, INT len)
1031 {
1032 	return indi_to_event(node, tag, head, len, &disp_long_rfmt);
1033 }
1034 /*================================================
1035  * sh_indi_to_event_shrt -- Pass-thru to indi_to_event, short display
1036  *  using short display reformatting
1037  *==============================================*/
1038 static STRING
sh_indi_to_event_shrt(NODE node,STRING tag,STRING head,INT len)1039 sh_indi_to_event_shrt (NODE node, STRING tag, STRING head, INT len)
1040 {
1041 	return indi_to_event(node, tag, head, len, &disp_shrt_rfmt);
1042 }
1043 /*==================================================
1044  * sh_fam_to_event_shrt -- Pass-thru to fam_to_event
1045  *  using display reformatting
1046  *================================================*/
1047 static STRING
sh_fam_to_event_shrt(NODE node,STRING tag,STRING head,INT len)1048 sh_fam_to_event_shrt (NODE node, STRING tag, STRING head
1049 	, INT len)
1050 {
1051 	return fam_to_event(node, tag, head, len, &disp_shrt_rfmt);
1052 }
1053