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