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  * browse.c -- Implements the browse command
27  * NB: Part of curses GUI version
28  * Copyright(c) 1992-95 by T.T. Wetmore IV; all rights reserved
29  *   2.3.4 - 24 Jun 93    2.3.5 - 25 Aug 93
30  *   2.3.6 - 01 Nov 93    3.0.0 - 24 Sep 94
31  *   3.0.2 - 16 Oct 94    3.0.2 - 30 Dec 94
32  *   3.0.3 - 04 May 95
33  *===========================================================*/
34 
35 #include "llstdlib.h"
36 #include "table.h"
37 #include "translat.h"
38 #include "gedcom.h"
39 #include "indiseq.h"
40 #include "liflines.h"
41 #include "feedback.h"
42 #include "menuitem.h"
43 #include "cache.h"
44 #include "lloptions.h"
45 
46 #include "llinesi.h"
47 #include "screen.h"
48 
49 /*********************************************
50  * global/exported variables
51  *********************************************/
52 
53 RECORD jumpnode; /* used by Ethel for direct navigation */
54 
55 /*********************************************
56  * external/imported variables
57  *********************************************/
58 
59 extern BOOLEAN traditional;
60 extern STRING qSnochil, qSnopers, qSnofam, qSnosour, qSidsour, qSnorec;
61 extern STRING qSnoeven, qSideven, qSnoothe, qSidothe;
62 extern STRING qSnonote, qSidnote, qSnoptr, qSidptr;
63 extern STRING qSidsbrs, qSidsrmv, qSidfbrs, qSidcbrs, qSidcrmv, qSiscnew, qSissnew;
64 extern STRING qSidfcop, qSntprnt, qSnofath, qSnomoth, qSnospse, qSnoysib, qSnoosib;
65 extern STRING qSnoprnt, qSnohusb, qSnowife, qShasbth, qShasnei, qSnocinf, qSnocofp;
66 extern STRING qSidpnxt, qSidnxt;
67 extern STRING qSids2fm, qSidc2fm, qSidplst, qSidp2br, qScrtcfm, qScrtsfm;
68 extern STRING qSronlye, qSronlya, qSidhbrs, qSidwbrs;
69 extern STRING qSid1sbr, qSid2sbr, qSid1fbr, qSid2fbr, qSid1cbr, qSid2cbr;
70 extern STRING qSid1hbr, qSid2hbr, qSid1wbr, qSid2wbr;
71 extern STRING qSspover, qSidfamk, qSnohist, qSidhist, qShistclr;
72 extern STRING qStag2lng2cnc,qSnewrecis,qSautoxref,qSeditcur,qSgotonew,qSstaycur;
73 extern STRING qSbadhistcnt,qSbadhistcnt2,qSbadhistlen;
74 
75 /*********************************************
76  * local enums & defines
77  *********************************************/
78 
79 #define MAX_SPOUSES 30
80 struct hist;
81 
82 /*********************************************
83  * local function prototypes
84  *********************************************/
85 
86 /* alphabetical */
87 static RECORD add_new_rec_maybe_ref(RECORD current, char ntype);
88 static void ask_clear_history(struct hist * histp);
89 static void autoadd_xref(RECORD rec, NODE newnode);
90 static INT browse_aux(RECORD *prec1, RECORD *prec2, INDISEQ *pseq);
91 static INT browse_indi(RECORD *prec1, RECORD *prec2, INDISEQ *pseq);
92 static INT browse_fam(RECORD *prec1, RECORD *prec2, INDISEQ *pseq);
93 static INT browse_indi_modes(RECORD *prec1, RECORD *prec2, INDISEQ *pseq
94 	, INT indimode);
95 static INT browse_pedigree(RECORD *prec1, RECORD *prec2, INDISEQ *pseq);
96 static RECORD disp_chistory_list(void);
97 static RECORD disp_vhistory_list(void);
98 static INT display_aux(RECORD rec, INT mode, BOOLEAN reuse);
99 static INT get_hist_count(struct hist * histp);
100 static INDISEQ get_history_list(struct hist * histp);
101 static RECORD goto_fam_child(RECORD frec, int childno);
102 static RECORD goto_indi_child(RECORD irec, int childno);
103 static BOOLEAN handle_aux_mode_cmds(INT c, INT * mode);
104 static INT handle_history_cmds(INT c, RECORD *prec1);
105 static RECORD history_back(struct hist * histp);
106 static RECORD do_disp_history_list(struct hist * histp);
107 static void history_record(RECORD rec, struct hist * histp);
108 static RECORD history_fwd(struct hist * histp);
109 static void init_hist(struct hist * histp, INT count);
110 static void load_hist_lists(void);
111 static void load_nkey_list(STRING key, struct hist * histp);
112 static void prompt_add_spouse_with_candidate(RECORD fam, RECORD save);
113 static RECORD pick_create_new_family(RECORD current, RECORD save, STRING * addstrings);
114 static void pick_remove_spouse_from_family(RECORD frec);
115 static void save_hist_lists(void);
116 static void save_nkey_list(STRING key, struct hist * histp);
117 static void setrecord(RECORD * dest, RECORD * src);
118 
119 /*********************************************
120  * local variables
121  *********************************************/
122 
123 /*
124 	A history structure is a circular buffer holding
125 	a list. start is the earliest entry, and the
126 	past_end points just beyond the latest entry.
127 	If start==past_end the the buffer is full.
128 	(If start==-1, then there are no entries.)
129 	list is a dynamically allocated array, with #entries==size.
130 	-1 <= start < size
131 	0 <= past_end < size
132 	(NB: CMD_HISTORY_FWD will go ahead to unused entries.)
133 */
134 struct hist {
135 	INT start;
136 	INT past_end;
137 	INT size;
138 	NKEY * list;
139 };
140 static struct hist vhist; /* records visited */
141 static struct hist chist; /* records changed */
142 
143 
144 /*********************************************
145  * local function definitions
146  * body of module
147  *********************************************/
148 
149 /*=========================================
150  * prompt_for_browse -- prompt for browse target
151  *  when only type of browse is known
152  *  prec:  [OUT]  current record
153  *  code:  [I/O]  current browse type
154  *  pseq:  [OUT]  current sequence
155  * Sets either *prec or *pseq & sets *code to appropriate browse type
156  *  returns addref'd record
157  *=======================================*/
158 static void
prompt_for_browse(RECORD * prec,INT * code,INDISEQ * pseq)159 prompt_for_browse (RECORD * prec, INT * code, INDISEQ * pseq)
160 {
161 	INT len, rc;
162 	STRING key, name;
163 
164 	/* verify & clear the output arguments */
165 	ASSERT(prec);
166 	ASSERT(pseq);
167 	*prec = 0;
168 	*pseq =0;
169 
170 	if (*code == BROWSE_INDI) {
171 		/* ctype of 'B' means any type but check persons first */
172 		*pseq = ask_for_indiseq(_(qSidplst), 'B', &rc);
173 		if (!*pseq) return;
174 		if ((len = length_indiseq(*pseq)) < 1) return;
175 		if (len == 1) {
176 			element_indiseq(*pseq, 0, &key, &name);
177 			*prec = qkey_to_record(key);
178 			/* leaking sequence here, Perry, 2005-09-25 */
179 			*pseq = NULL;
180 			*code = BROWSE_UNK; /* not sure what we got above */
181 		} else {
182 			*code = BROWSE_LIST;
183 		}
184 		return;
185 	}
186 	if (*code == BROWSE_EVEN) {
187 		*prec = choose_any_event();
188 		return;
189 	}
190 	if (*code == BROWSE_SOUR) {
191 		*prec = choose_any_source();
192 		return;
193 	}
194 	*prec = choose_any_other();
195 	return;
196 }
197 /*=========================================
198  * browse -- Main loop of browse operation.
199  *  rec may be NULL (then prompt)
200  *=======================================*/
201 void
main_browse(RECORD rec1,INT code)202 main_browse (RECORD rec1, INT code)
203 {
204 	RECORD rec2=0;
205 	INDISEQ seq = NULL;
206 
207 	if (!rec1)
208 		prompt_for_browse(&rec1, &code, &seq);
209 
210 	if (!rec1) {
211 		if (!seq) return;
212 		if (!length_indiseq(seq)) {
213 			remove_indiseq(seq);
214 			return;
215 		}
216 	}
217 
218 
219 
220 	/*
221 	loop here handle user browsing around through
222 	persons, families, references, etc, without returning
223 	to main menu
224 	*/
225 
226 	while (code != BROWSE_QUIT) {
227 		switch (code) {
228 		case BROWSE_INDI:
229 			code = browse_indi(&rec1, &rec2, &seq); break;
230 		case BROWSE_FAM:
231 			code = browse_fam(&rec1, &rec2, &seq); break;
232 		case BROWSE_PED:
233 			code = browse_pedigree(&rec1, &rec2, &seq); break;
234 		case BROWSE_TAND:
235 			code = browse_tandem(&rec1, &rec2, &seq); break;
236 		case BROWSE_2FAM:
237 			code = browse_2fam(&rec1, &rec2, &seq); break;
238 		case BROWSE_LIST:
239 			code = browse_list(&rec1, &rec2, &seq); break;
240 		case BROWSE_SOUR:
241 		case BROWSE_EVEN:
242 		case BROWSE_AUX:
243 			code = browse_aux(&rec1, &rec2, &seq); break;
244 		case BROWSE_UNK:
245 			ASSERT(rec1);
246 			switch(nztype(rec1)) {
247 			case 'I': code=BROWSE_INDI; break;
248 			case 'F': code=BROWSE_FAM; break;
249 			case 'S': code=BROWSE_SOUR; break;
250 			case 'E': code=BROWSE_EVEN; break;
251 			default: code=BROWSE_AUX; break;
252 			}
253 		}
254 	}
255 	setrecord(&rec1, NULL);
256 	setrecord(&rec2, NULL);
257 }
258 /*================================================
259  * goto_indi_child - jump to child by number
260  * returns addref'd record
261  *==============================================*/
262 static RECORD
goto_indi_child(RECORD irec,int childno)263 goto_indi_child (RECORD irec, int childno)
264 {
265 	INT num1, num2, i = 0;
266 	RECORD answer = 0;
267 	INT akeynum=0; /* answer key */
268 	NODE indi = nztop(irec);
269 	if (!irec) return NULL;
270 	FORFAMS(indi, fam, num1)
271 		FORCHILDREN(fam, chil, num2)
272 			i++;
273 			if (i == childno)
274 				akeynum = nzkeynum(chil);
275 		ENDCHILDREN
276 	ENDFAMS
277 	if (akeynum) {
278 		answer = keynum_to_irecord(akeynum);
279 		addref_record(answer);
280 	}
281 	return answer;
282 }
283 /*================================================
284  * goto_fam_child - jump to child by number
285  * returns addref'd record
286  *==============================================*/
287 static RECORD
goto_fam_child(RECORD frec,int childno)288 goto_fam_child (RECORD frec, int childno)
289 {
290 	INT num, i = 0;
291 	RECORD answer = 0;
292 	INT akeynum=0;
293 	NODE fam = nztop(frec);
294 	if (!frec) return NULL;
295 	FORCHILDREN(fam, chil, num)
296 		i++;
297 		if (i == childno)
298 			akeynum = nzkeynum(chil);
299 	ENDCHILDREN
300 	if (akeynum) {
301 		answer = keynum_to_irecord(akeynum);
302 		addref_record(answer);
303 	}
304 	return answer;
305 }
306 /*===============================================
307  * pick_create_new_family --
308  * returns addref'd record
309  *=============================================*/
310 static RECORD
pick_create_new_family(RECORD current,RECORD save,STRING * addstrings)311 pick_create_new_family (RECORD current, RECORD save, STRING * addstrings)
312 {
313 	INT i;
314 	RECORD rec=0;
315 
316 	if (readonly) {
317 		message(_(qSronlya));
318 		return NULL;
319 	}
320 	i = choose_from_array(_(qSidfcop), 2, addstrings);
321 	if (i == -1) return NULL;
322 	if (i == 0) {
323 		rec = add_family_by_edit(NULL, NULL, current, &disp_long_rfmt);
324 	} else if (save) {
325 		char scratch[100];
326 		STRING name = indi_to_name(nztop(save), 55);
327 		llstrncpyf(scratch, sizeof(scratch), uu8, "%s%s", _(qSissnew), name);
328 		if (keyflag) {
329 			STRING key = rmvat(nxref(nztop(save)))+1;
330 			llstrappf(scratch, sizeof(scratch), uu8, " (%s)", key);
331 		}
332 		if (ask_yes_or_no(scratch))
333 			rec = add_family_by_edit(current, save, NULL, &disp_long_rfmt);
334 		else
335 			rec = add_family_by_edit(current, NULL, NULL, &disp_long_rfmt);
336 	} else
337 		rec = add_family_by_edit(current, NULL, NULL, &disp_long_rfmt);
338 	return rec;
339 }
340 /*====================================================
341  * setrecord -- Move record in src to dest
342  *  Handles releasing old reference
343  *==================================================*/
344 static void
setrecord(RECORD * dest,RECORD * src)345 setrecord (RECORD * dest, RECORD * src)
346 {
347 	ASSERT(dest);
348 	if (*dest) {
349 		release_record(*dest);
350 	}
351 	if (src) {
352 		*dest = *src;
353 		*src = 0;
354 	} else {
355 		*dest = 0;
356 	}
357 }
358 /*====================================================
359  * browse_indi_modes -- Handle person/pedigree browse.
360  *  prec1 [I/O]  current record (or upper in tandem screens)
361  *  prec2 [I/O]  lower record in tandem screens
362  *  pseq  [I/O]  current sequence in list browse
363  *==================================================*/
364 static INT
browse_indi_modes(RECORD * prec1,RECORD * prec2,INDISEQ * pseq,INT indimode)365 browse_indi_modes (RECORD *prec1, RECORD *prec2, INDISEQ *pseq, INT indimode)
366 {
367 	RECORD current=0;
368 	STRING key, name;
369 	INT i, c, rc;
370 	BOOLEAN reuse=FALSE; /* flag to reuse same display strings */
371 	INT nkeyp, indimodep;
372 	RECORD save=0, tmp=0, tmp2=0;
373 	INDISEQ seq = NULL;
374 	INT rtn=0; /* return code */
375 
376 	ASSERT(prec1);
377 	ASSERT(*prec1);
378 	ASSERT(nztype(*prec1)=='I');
379 	ASSERT(!*prec2);
380 	ASSERT(!*pseq);
381 
382 	/* move working record into current */
383 	setrecord(&current, prec1);
384 	setrecord(prec1, 0);
385 	setrecord(prec2, 0);
386 
387 	show_reset_scroll();
388 	nkeyp = 0;
389 	indimodep = indimode;
390 
391 	while (TRUE) {
392 		setrecord(&tmp, NULL);
393 		if (nzkeynum(current) != nkeyp
394 			|| indimode != indimodep) {
395 			show_reset_scroll();
396 		}
397 		history_record(current, &vhist);
398 			/* display & get input, preserving INDI in cache */
399 		display_indi(current, indimode, reuse);
400 		c = interact_indi();
401 		/* last keynum & mode, so can tell if changed */
402 		nkeyp = nzkeynum(current);
403 		indimodep = indimode;
404 reprocess_indi_cmd: /* so one command can forward to another */
405 		reuse = FALSE; /* don't reuse display unless specifically set */
406 		if (c != CMD_NEWFAMILY) save = NULL;
407 		if (handle_menu_cmds(c, &reuse))
408 			continue;
409 		if (handle_scroll_cmds(c, &reuse))
410 			continue;
411 		if (handle_indi_mode_cmds(c, &indimode))
412 			continue;
413 		i = handle_history_cmds(c, prec1);
414 		if (i == 1)
415 			continue; /* history cmd handled, stay here */
416 		if (i == -1) {
417 			/* history cmd handled, leave page */
418 			rtn = BROWSE_UNK;
419 			goto exitbrowse;
420 		}
421 		switch (c)
422 		{
423 		case CMD_EDIT:	/* Edit this person */
424 			edit_indi(current, &disp_long_rfmt);
425 			break;
426 		case CMD_FAMILY: 	/* Browse to person's family */
427 			if ((tmp = choose_family(current, _(qSntprnt)
428 				,  _(qSidfbrs), TRUE))) {
429 				setrecord(prec1, &tmp);
430 				rtn = BROWSE_FAM;
431 				goto exitbrowse;
432 			}
433 			break;
434 		case CMD_TANDEM_FAMILIES:
435 			if ((tmp = choose_family(current, _(qSntprnt),
436 				_(qSid1fbr), TRUE)) != 0) {
437 				if ((tmp2 = choose_family(current, _(qSntprnt),
438 					_(qSid2fbr), TRUE)) != 0) {
439 					setrecord(prec1, &tmp);
440 					setrecord(prec2, &tmp2);
441 					rtn = BROWSE_2FAM;
442 					goto exitbrowse;
443 				}
444 				setrecord(&tmp, 0);
445 			}
446 			break;
447 		case CMD_FATHER: 	/* Browse to person's father */
448 			if ((tmp = choose_father(current, NULL, _(qSnofath),
449 				_(qSidhbrs), NOASK1)) != 0) {
450 				setrecord(&current, &tmp);
451 			}
452 			break;
453 		case CMD_TANDEM_FATHERS:	/* Tandem Browse to person's fathers */
454 			if ((tmp = choose_father(current, NULL, _(qSnofath),
455 				_(qSid1hbr), NOASK1)) != 0) {
456 				if ((tmp2 = choose_father(current, NULL, _(qSnofath),
457 					_(qSid2hbr), NOASK1)) != 0) {
458 					setrecord(prec1, &tmp);
459 					setrecord(prec2, &tmp2);
460 					rtn = BROWSE_TAND;
461 					goto exitbrowse;
462 				}
463 				setrecord(&tmp, 0);
464 			}
465 			break;
466 		case CMD_MOTHER:	/* Browse to person's mother */
467 			if ((tmp = choose_mother(current, NULL, _(qSnomoth),
468 				_(qSidwbrs), NOASK1)) != 0) {
469 				setrecord(&current, &tmp);
470 			}
471 			break;
472 		case CMD_TANDEM_MOTHERS:	/* Tandem Browse to person's mothers */
473 			if ((tmp = choose_mother(current, NULL, _(qSnomoth),
474 				_(qSid1wbr), NOASK1)) != 0) {
475 				if ((tmp2 = choose_mother(current, NULL, _(qSnomoth),
476 					_(qSid2wbr), NOASK1)) != 0) {
477 					setrecord(prec1, &tmp);
478 					setrecord(prec2, &tmp2);
479 					rtn = BROWSE_TAND;
480 					goto exitbrowse;
481 				}
482 				setrecord(&tmp, 0);
483 			}
484 			break;
485 		case CMD_BROWSE_ZIP_INDI:	/* Zip browse another person */
486 			if ((tmp = ask_for_indi(_(qSidpnxt), NOASK1)) != 0)
487 				setrecord(&current, &tmp);
488 			break;
489 		case CMD_BROWSE_ZIP_ANY:	/* Zip browse any record */
490 			if ((tmp = ask_for_any(_(qSidnxt), NOASK1)) != 0) {
491 				if (nztype(tmp) != 'I') {
492 					setrecord(prec1, &tmp);
493 					rtn = BROWSE_UNK;
494 					goto exitbrowse;
495 				} else {
496 					setrecord(&current, &tmp);
497 				}
498 			}
499 			break;
500 		case CMD_SPOUSE:	/* Browse to person's spouse */
501 			if ((tmp = choose_spouse(current, _(qSnospse), _(qSidsbrs))) != 0)
502 				setrecord(&current, &tmp);
503 			break;
504 		case CMD_TANDEM_SPOUSES:	/* browse to tandem spouses */
505 			if ((tmp = choose_spouse(current, _(qSnospse), _(qSid1sbr))) != 0) {
506 				if ((tmp2 = choose_spouse(current, _(qSnospse), _(qSid2sbr))) != 0) {
507 					setrecord(prec1, &tmp);
508 					setrecord(prec2, &tmp2);
509 					rtn = BROWSE_TAND;
510 					goto exitbrowse;
511 				}
512 				setrecord(&tmp, 0);
513 			}
514 			break;
515 		case CMD_CHILDREN:	/* Browse to person's child */
516 			if ((tmp = choose_child(current, NULL, _(qSnocofp),
517 				_(qSidcbrs), NOASK1)) != 0) {
518 				setrecord(&current, &tmp);
519 			}
520 			break;
521 		case CMD_TOGGLE_PEDTYPE:       /* toggle pedigree mode (ancestors/descendants) */
522 			pedigree_toggle_mode();
523 			break;
524 		case CMD_DEPTH_DOWN:       /* decrease pedigree depth */
525 			pedigree_increase_generations(-1);
526 			break;
527 		case CMD_DEPTH_UP:      /* increase pedigree depth */
528 			pedigree_increase_generations(+1);
529 			break;
530 		case CMD_TOGGLE_CHILDNUMS:       /* toggle children numbers */
531 			show_childnumbers();
532 			break;
533 		case CMD_CHILD_DIRECT0+1:	/* Go to children by number */
534 		case CMD_CHILD_DIRECT0+2:
535 		case CMD_CHILD_DIRECT0+3:
536 		case CMD_CHILD_DIRECT0+4:
537 		case CMD_CHILD_DIRECT0+5:
538 		case CMD_CHILD_DIRECT0+6:
539 		case CMD_CHILD_DIRECT0+7:
540 		case CMD_CHILD_DIRECT0+8:
541 		case CMD_CHILD_DIRECT0+9:
542 			if ((tmp = goto_indi_child(current, c-CMD_CHILD_DIRECT0)) != 0)
543 				setrecord(&current, &tmp);
544 			else
545 				message(_(qSnochil));
546 			break;
547 		case CMD_TANDEM_CHILDREN:	/* browse to tandem children */
548 			if ((tmp = choose_child(current, NULL, _(qSnocofp),
549 				_(qSid1cbr), NOASK1)) != 0) {
550 				if ((tmp2 = choose_child(current, NULL, _(qSnocofp),
551 					_(qSid2cbr), NOASK1)) != 0) {
552 					setrecord(prec1, &tmp);
553 					setrecord(prec2, &tmp2);
554 					rtn = BROWSE_TAND;
555 					goto exitbrowse;
556 				}
557 				setrecord(&tmp, 0);
558 			}
559 			break;
560 		case CMD_PEDIGREE:	/* Switch to pedigree mode */
561 			if (indimode == 'i')
562 				indimode = 'p';
563 			else
564 				indimode = 'i';
565 			break;
566 		case CMD_UPSIB:	/* Browse to older sib */
567 			if ((tmp = indi_to_prev_sib(current)) != 0)
568 				setrecord(&current, &tmp);
569 			else
570 				message(_(qSnoosib));
571 			break;
572 		case CMD_DOWNSIB:	/* Browse to younger sib */
573 			if ((tmp = indi_to_next_sib(current)) != 0)
574 				setrecord(&current, &tmp);
575 			else
576 				message(_(qSnoysib));
577 			break;
578 		case CMD_PARENTS:	/* Browse to parents' family */
579 			if ((tmp = choose_family(current, _(qSnoprnt),
580 				_(qSidfbrs), FALSE)) != 0) {
581 				setrecord(prec1, &tmp);
582 				rtn = BROWSE_FAM;
583 				goto exitbrowse;
584 			}
585 			break;
586 		case CMD_TANDEM_PARENTS:	/* tandem browse to two parents families*/
587 			if ((tmp = choose_family(current, _(qSnoprnt),
588 				_(qSid1fbr), FALSE)) != 0) {
589 				if ((tmp2 = choose_family(current, _(qSnoprnt),
590 					_(qSid2fbr), FALSE)) != 0) {
591 					setrecord(prec1, &tmp);
592 					setrecord(prec2, &tmp2);
593 					rtn = BROWSE_2FAM;
594 					goto exitbrowse;
595 				}
596 				setrecord(&tmp, 0);
597 			}
598 			break;
599 		case CMD_BROWSE: 	/* Browse new list of persons */
600 			seq = ask_for_indiseq(_(qSidplst), 'B', &rc);
601 			if (!seq) break;
602 			if (length_indiseq(seq) == 1) {
603 				element_indiseq(seq, 0, &key, &name);
604 				tmp = key_to_record(key);
605 				setrecord(&current, &tmp);
606 				remove_indiseq(seq);
607 				seq=NULL;
608 				if (nztype(current) != 'I') {
609 					setrecord(prec1, &current);
610 					rtn = BROWSE_UNK;
611 					goto exitbrowse;
612 				}
613 			} else {
614 				*pseq = seq;
615 				rtn = BROWSE_LIST;
616 				goto exitbrowse;
617 			}
618 			break;
619 		case CMD_NEWPERSON:	/* Add new person */
620 			if (!(tmp = add_indi_by_edit(&disp_long_rfmt)))
621 				break;
622 			setrecord(&save, &current);
623 			setrecord(&current, &tmp);
624 			break;
625 		case CMD_NEWFAMILY:	/* Add family for current person */
626 			{
627 				STRING addstrings[2];
628 				addstrings[0] = _(qScrtcfm);
629 				addstrings[1] = _(qScrtsfm);
630 				if ((tmp = pick_create_new_family(current, save, addstrings)) != 0) {
631 					setrecord(&save, NULL);
632 					setrecord(prec1, &tmp);
633 					rtn = BROWSE_FAM;
634 					goto exitbrowse;
635 				}
636 			}
637 			break;
638 		case CMD_ADD_SOUR: /* add source */
639 		case CMD_ADD_EVEN: /* add event */
640 		case CMD_ADD_OTHR: /* add other */
641 			{
642 				char c2 = (c==CMD_ADD_SOUR ? 'S' : (c==CMD_ADD_EVEN ? 'E' : 'X'));
643 				if ((tmp = add_new_rec_maybe_ref(current, c2)) != 0) {
644 					if (tmp == current) {
645 						c = CMD_EDIT;
646 						goto reprocess_indi_cmd; /* forward to edit */
647 					}
648 					setrecord(prec1, &tmp);
649 					rtn = BROWSE_UNK;
650 					goto exitbrowse;
651 				}
652 			}
653 			break;
654 		case CMD_TANDEM:	/* Switch to tandem browsing */
655 			if ((tmp = ask_for_indi(_(qSidp2br), NOASK1)) != 0) {
656 				setrecord(prec1, &current);
657 				setrecord(prec2, &tmp);
658 				rtn = BROWSE_TAND;
659 				goto exitbrowse;
660 			}
661 			break;
662 		case CMD_SWAPFAMILIES: 	/* Swap families of current person */
663 			swap_families(current);
664 			break;
665 		case CMD_ADDASSPOUSE:	/* Add person as spouse */
666 			prompt_add_spouse(current, NULL, TRUE);
667 			break;
668 		case CMD_ADDASCHILD:    /* Add person as child */
669 			my_prompt_add_child(nztop(current), NULL);
670 			break;
671 		case CMD_PERSON:   /* switch to person browse */
672 			indimode='i';
673 			break;
674 		case CMD_REMOVEASSPOUSE:	/* Remove person as spouse */
675 			choose_and_remove_spouse(current, NULL, FALSE);
676 			break;
677 		case CMD_REMOVEASCHILD:	/* Remove person as child */
678 			choose_and_remove_child(current, NULL, FALSE);
679 			break;
680 		case CMD_ADVANCED:	/* Advanced person edit */
681 			advanced_person_edit(nztop(current));
682 			break;
683 		case CMD_JUMP_HOOK:   /* GUI direct navigation */
684 			current = jumpnode;
685 			break;
686 		case CMD_NEXT:	/* Go to next indi in db */
687 			{
688 				i = xref_nexti(nkeyp);
689 				if (i) {
690 					tmp = keynum_to_irecord(i);
691 					setrecord(&current, &tmp);
692 				} else {
693 					message(_(qSnopers));
694 				}
695 			}
696 			break;
697 		case CMD_PREV:	/* Go to prev indi in db */
698 			{
699 				i = xref_previ(nkeyp);
700 				if (i) {
701 					tmp = keynum_to_irecord(i);
702 					setrecord(&current, &tmp);
703 				} else {
704 					message(_(qSnopers));
705 				}
706 			}
707 			break;
708 		case CMD_SOURCES:	/* Browse to sources */
709 			if ((tmp = choose_source(current, _(qSnosour), _(qSidsour))) != 0) {
710 				setrecord(prec1, &tmp);
711 				rtn = BROWSE_AUX;
712 				goto exitbrowse;
713 			}
714 			break;
715 		case CMD_NOTES:	/* Browse to notes */
716 			if ((tmp = choose_note(current, _(qSnonote), _(qSidnote))) != 0) {
717 				setrecord(prec1, &tmp);
718 				rtn = BROWSE_AUX;
719 				goto exitbrowse;
720 			}
721 			break;
722 		case CMD_POINTERS:	/* Browse to references */
723 			if ((tmp = choose_pointer(current, _(qSnoptr), _(qSidptr))) != 0) {
724 				setrecord(prec1, &tmp);
725 				rtn = BROWSE_UNK;
726 				goto exitbrowse;
727 			}
728 			break;
729 		case CMD_QUIT:
730 			rtn = BROWSE_QUIT;
731 			goto exitbrowse;
732 		}
733 	}
734 exitbrowse:
735 	setrecord(&tmp, NULL);
736 	setrecord(&current, NULL);
737 	return rtn;
738 }
739 /*==========================================
740  * display_aux -- Show aux node in current mode
741  * Created: 2001/01/27, Perry Rapp
742  *========================================*/
743 static INT
display_aux(RECORD rec,INT mode,BOOLEAN reuse)744 display_aux (RECORD rec, INT mode, BOOLEAN reuse)
745 {
746 	CACHEEL cel;
747 	INT c;
748 	cel = record_to_cacheel(rec);
749 	lock_cache(cel);
750 	c = aux_browse(rec, mode, reuse);
751 	unlock_cache(cel);
752 	return c;
753 }
754 /*====================================================
755  * browse_aux -- Handle aux node browse.
756  * Created: 2001/01/27, Perry Rapp
757  *==================================================*/
758 static INT
browse_aux(RECORD * prec1,RECORD * prec2,INDISEQ * pseq)759 browse_aux (RECORD *prec1, RECORD *prec2, INDISEQ *pseq)
760 {
761 	RECORD current=0;
762 	INT i, c;
763 	BOOLEAN reuse=FALSE; /* flag to reuse same display strings */
764 	INT nkeyp=0, auxmode=0, auxmodep=0;
765 	char ntype=0, ntypep=0;
766 	RECORD tmp=0;
767 	char c2;
768 	INT rtn=0; /* return code */
769 
770 	ASSERT(prec1);
771 	ASSERT(*prec1);
772 	ASSERT(!*prec2);
773 	ASSERT(!*pseq);
774 
775 	/* move working record into current */
776 	setrecord(&current, prec1);
777 	setrecord(prec1, 0);
778 	setrecord(prec2, 0);
779 
780 
781 	auxmode = 'x';
782 
783 	show_reset_scroll();
784 	nkeyp = 0;
785 	ntypep = 0;
786 	auxmodep = auxmode;
787 
788 	while (TRUE) {
789 		if (nzkeynum(current) != nkeyp
790 			|| nztype(current) != ntypep
791 			|| auxmode != auxmodep) {
792 			show_reset_scroll();
793 		}
794 		ntype = nztype(current);
795 		history_record(current, &vhist);
796 		c = display_aux(current, auxmode, reuse);
797 		/* last keynum & mode, so can tell if changed */
798 		nkeyp = nzkeynum(current);
799 		ntypep = nztype(current);
800 		auxmodep = auxmode;
801 reprocess_aux_cmd:
802 		reuse = FALSE; /* don't reuse display unless specifically set */
803 		if (handle_menu_cmds(c, &reuse))
804 			continue;
805 		if (handle_scroll_cmds(c, &reuse))
806 			continue;
807 		if (handle_aux_mode_cmds(c, &auxmode))
808 			continue;
809 		i = handle_history_cmds(c, prec1);
810 		if (i == 1)
811 			continue; /* history cmd handled, stay here */
812 		if (i == -1)
813 			return BROWSE_UNK; /* history cmd handled, leave page */
814 		switch (c)
815 		{
816 		case CMD_EDIT:
817 			switch(ntype) {
818 			case 'S': edit_source(current, &disp_long_rfmt); break;
819 			case 'E': edit_event(current, &disp_long_rfmt); break;
820 			case 'X': edit_other(current, &disp_long_rfmt); break;
821 			}
822 			break;
823 		case CMD_ADD_SOUR: /* add source */
824 		case CMD_ADD_EVEN: /* add event */
825 		case CMD_ADD_OTHR: /* add other */
826 			c2 = (c==CMD_ADD_SOUR ? 'S' : (c==CMD_ADD_EVEN ? 'E' : 'X'));
827 			if ((tmp = add_new_rec_maybe_ref(current, c2)) != 0) {
828 				if (tmp == current) {
829 					c = CMD_EDIT;
830 					goto reprocess_aux_cmd; /* forward to edit */
831 				} else {
832 					setrecord(prec1, &tmp);
833 					rtn = BROWSE_UNK;
834 					goto exitbrowse;
835 				}
836 			}
837 			break;
838 		case CMD_BROWSE_ZIP_INDI:	/* Zip browse to new person */
839 			if ((tmp = ask_for_indi(_(qSidpnxt), NOASK1)) != 0) {
840 				setrecord(prec1, &tmp);
841 				rtn = BROWSE_UNK;
842 				goto exitbrowse;
843 			}
844 			break;
845 		case CMD_BROWSE_ZIP_ANY:	/* Zip browse any record */
846 			if ((tmp = ask_for_any(_(qSidnxt), NOASK1)) != 0) {
847 				setrecord(prec1, &tmp);
848 				rtn = BROWSE_UNK;
849 				goto exitbrowse;
850 			}
851 			break;
852 		case CMD_NOTES:	/* Browse to notes */
853 			if ((tmp = choose_note(current, _(qSnonote), _(qSidnote))) != 0) {
854 				setrecord(&current, &tmp);
855 			}
856 			break;
857 		case CMD_POINTERS:	/* Browse to references */
858 			if ((tmp = choose_pointer(current, _(qSnoptr), _(qSidptr))) != 0) {
859 				setrecord(prec1, &tmp);
860 				rtn = BROWSE_UNK;
861 				goto exitbrowse;
862 			}
863 			break;
864 		case CMD_NEXT:	/* Go to next in db */
865 			{
866 				i = xref_next(ntype, nkeyp);
867 				if (i) {
868 					tmp = keynum_to_record(ntype, i);
869 					setrecord(&current, &tmp);
870 				} else {
871 					message(_(qSnorec));
872 				}
873 				break;
874 			}
875 		case CMD_PREV:	/* Go to prev in db */
876 			{
877 				i = xref_prev(ntype, nkeyp);
878 				if (i) {
879 					tmp = keynum_to_record(ntype, i);
880 					setrecord(&current, &tmp);
881 				} else {
882 					message(_(qSnorec));
883 				}
884 				break;
885 			}
886 		case CMD_QUIT:
887 			rtn = BROWSE_QUIT;
888 			goto exitbrowse;
889 		}
890 	}
891 exitbrowse:
892 	setrecord(&current, NULL);
893 	return rtn;
894 }
895 /*================================================
896  * browse_indi -- Handle person browse operations.
897  *==============================================*/
898 static INT
browse_indi(RECORD * prec1,RECORD * prec2,INDISEQ * pseq)899 browse_indi (RECORD *prec1, RECORD *prec2, INDISEQ *pseq)
900 {
901 	return browse_indi_modes(prec1, prec2, pseq, 'n');
902 }
903 /*===============================================
904  * pick_remove_spouse_from_family --
905  *  pulled out of browse_fam, 2001/02/03, Perry Rapp
906  *  construct list of spouses, prompt for choice, & remove
907  *=============================================*/
908 static void
pick_remove_spouse_from_family(RECORD frec)909 pick_remove_spouse_from_family (RECORD frec)
910 {
911 	NODE fam = nztop(frec);
912 	NODE fref, husb, wife, chil, rest;
913 	NODE root, node, spnodes[MAX_SPOUSES];
914 	STRING spstrings[MAX_SPOUSES];
915 	INT i;
916 	if (readonly) {
917 		message(_(qSronlye));
918 		return;
919 	}
920 	split_fam(fam, &fref, &husb, &wife, &chil, &rest);
921 	if (!husb && !wife) {
922 		message(_(qShasnei));
923 		return;
924 	}
925 	i = 0;
926 	for (node = husb; node; node = nsibling(node)) {
927 		root = key_to_indi(rmvat(nval(node)));
928 		spstrings[i] = indi_to_list_string(root,
929 			 NULL, 66, &disp_shrt_rfmt, TRUE);
930 		spnodes[i++] = root;
931 	}
932 	for (node = wife; node; node = nsibling(node)) {
933 		root = key_to_indi(rmvat(nval(node)));
934 		spstrings[i] = indi_to_list_string(root,
935 			 NULL, 66, &disp_shrt_rfmt, TRUE);
936 		spnodes[i++] = root;
937 		if (i == MAX_SPOUSES) {
938 			message(_(qSspover));
939 			break;
940 		}
941 	}
942 	join_fam(fam, fref, husb, wife, chil, rest);
943 	i = choose_from_array(_(qSidsrmv), i, spstrings);
944 	if (i == -1) return;
945 	choose_and_remove_spouse(node_to_record(spnodes[i]), frec, TRUE);
946 }
947 /*===============================================
948  * prompt_add_spouse_with_candidate --
949  *  fam:  [IN]  family to which to add (required arg)
950  *  save: [IN]  candidate spouse to add (optional arg)
951  * If candidate passed, asks user if that is desired spouse to add
952  * In either case, all work is delegated to prompt_add_spouse
953  *=============================================*/
954 static void
prompt_add_spouse_with_candidate(RECORD fam,RECORD candidate)955 prompt_add_spouse_with_candidate (RECORD fam, RECORD candidate)
956 {
957 	NODE fref, husb, wife, chil, rest;
958 	BOOLEAN confirm;
959 	char scratch[100];
960 	if (readonly) {
961 		message(_(qSronlye));
962 		return;
963 	}
964 	split_fam(nztop(fam), &fref, &husb, &wife, &chil, &rest);
965 	join_fam(nztop(fam), fref, husb, wife, chil, rest);
966 	if (traditional) {
967 		if (husb && wife) {
968 			message(_(qShasbth));
969 			return;
970 		}
971 	}
972 	if (candidate) {
973 		if (keyflag) {
974 			sprintf(scratch, "%s%s (%s)", _(qSissnew),
975 				 indi_to_name(nztop(candidate), 56),
976 				 rmvat(nxref(nztop(candidate)))+1);
977 		} else {
978 			sprintf(scratch, "%s%s", _(qSissnew),
979 				 indi_to_name(nztop(candidate), 56));
980 		}
981 		if (!ask_yes_or_no(scratch)) {
982 			candidate = NULL;
983 		}
984 	}
985 		/* don't confirm again if they just confirmed candidate */
986 	confirm = (candidate == NULL);
987 	prompt_add_spouse(candidate, fam, confirm);;
988 }
989 /*===============================================
990  * prompt_add_child_check_save --
991  *  fam:  [IN]  family to which to add (required arg)
992  *  save: [IN]  possible child to add (optional arg)
993  * If save is passed, this checks with user whether that is desired child
994  * In either case, all work is delegated to prompt_add_child
995  *=============================================*/
996 static void
prompt_add_child_check_save(NODE fam,NODE save)997 prompt_add_child_check_save (NODE fam, NODE save)
998 {
999 	char scratch[100];
1000 	if (readonly) {
1001 		message(_(qSronlye));
1002 		return;
1003 	}
1004 	if (save) {
1005 		if (keyflag)
1006 			if(getlloptint("DisplayKeyTags", 0) > 0) {
1007 				sprintf(scratch, "%s%s (i%s)", _(qSiscnew),
1008 				 	indi_to_name(save, 56),
1009 				 	rmvat(nxref(save))+1);
1010 			} else {
1011 				sprintf(scratch, "%s%s (%s)", _(qSiscnew),
1012 				 	indi_to_name(save, 56),
1013 				 	rmvat(nxref(save))+1);
1014 			}
1015 		else
1016 			sprintf(scratch, "%s%s", _(qSiscnew),
1017 				 indi_to_name(save, 56));
1018 		if (!ask_yes_or_no(scratch))
1019 			save = NULL;
1020 	}
1021 	my_prompt_add_child(save, fam);
1022 }
1023 /*===============================================
1024  * my_prompt_add_child -- call prompt_add_child with our reformatting info
1025  *=============================================*/
1026 NODE
my_prompt_add_child(NODE child,NODE fam)1027 my_prompt_add_child (NODE child, NODE fam)
1028 {
1029 	return prompt_add_child(child, fam, &disp_shrt_rfmt);
1030 }
1031 /*===============================================
1032  * browse_fam -- Handle family browse selections.
1033  *=============================================*/
1034 static INT
browse_fam(RECORD * prec1,RECORD * prec2,INDISEQ * pseq)1035 browse_fam (RECORD *prec1, RECORD *prec2, INDISEQ *pseq)
1036 {
1037 	RECORD current=0;
1038 	INT i, c, rc;
1039 	BOOLEAN reuse=FALSE; /* flag to reuse same display strings */
1040 	static INT fammode='n';
1041 	INT nkeyp, fammodep;
1042 	RECORD save=0, tmp=0, tmp2=0;
1043 	INDISEQ seq;
1044 	STRING key, name;
1045 	char c2;
1046 	INT rtn=0; /* return code */
1047 
1048 	ASSERT(prec1);
1049 	ASSERT(*prec1);
1050 	ASSERT(nztype(*prec1)=='F');
1051 	ASSERT(!*prec2);
1052 	ASSERT(!*pseq);
1053 
1054 	/* move working record into current */
1055 	setrecord(&current, prec1);
1056 	setrecord(prec1, 0);
1057 	setrecord(prec2, 0);
1058 
1059 	show_reset_scroll();
1060 	nkeyp = 0;
1061 	fammodep = fammode;
1062 
1063 	while (TRUE) {
1064 		setrecord(&tmp, NULL);
1065 		if (nzkeynum(current) != nkeyp
1066 			|| fammode != fammodep) {
1067 			show_reset_scroll();
1068 		}
1069 		history_record(current, &vhist);
1070 		display_fam(current, fammode, reuse);
1071 		c = interact_fam();
1072 		/* last keynum & mode, so can tell if changed */
1073 		nkeyp = nzkeynum(current);
1074 		fammodep = fammode;
1075 reprocess_fam_cmd: /* so one command can forward to another */
1076 		reuse = FALSE; /* don't reuse display unless specifically set */
1077 		if (c != CMD_ADDCHILD && c != CMD_ADDSPOUSE)
1078 			save = NULL;
1079 		if (handle_menu_cmds(c, &reuse))
1080 			continue;
1081 		if (handle_scroll_cmds(c, &reuse))
1082 			continue;
1083 		if (handle_fam_mode_cmds(c, &fammode))
1084 			continue;
1085 		i = handle_history_cmds(c, prec1);
1086 		if (i == 1)
1087 			continue; /* history cmd handled, stay here */
1088 		if (i == -1) {
1089 			/* history cmd handled, leave page */
1090 			rtn = BROWSE_UNK;
1091 			goto exitbrowse;
1092 		}
1093 		switch (c)
1094 		{
1095 		case CMD_ADVANCED:	/* Advanced family edit */
1096 			advanced_family_edit(nztop(current));
1097 			break;
1098 		case CMD_BROWSE_FAM:
1099 			if (ask_for_int(_(qSidfamk), &i) && (i>0)) {
1100 				if ((tmp = qkeynum_to_frecord(i)))
1101 					setrecord(&current, &tmp);
1102 				else
1103 					message(_(qSnofam));
1104 			}
1105 			break;
1106 		case CMD_EDIT:	/* Edit family's record */
1107 			edit_family(current, &disp_long_rfmt);
1108 			break;
1109 		case CMD_FATHER:	/* Browse to family's father */
1110 			if ((tmp = choose_father(NULL, current, _(qSnohusb),
1111 				_(qSidhbrs), NOASK1)) != 0) {
1112 				setrecord(prec1, &tmp);
1113 				rtn = BROWSE_INDI;
1114 				goto exitbrowse;
1115 			}
1116 			break;
1117 		case CMD_TANDEM_FATHERS:	/* Tandem Browse to family's fathers */
1118 			if ((tmp = choose_father(NULL, current, _(qSnohusb),
1119 				_(qSid1hbr), NOASK1)) != 0) {
1120 				if ((tmp2 = choose_father(NULL, current, _(qSnohusb),
1121 					_(qSid2hbr), NOASK1)) != 0) {
1122 					setrecord(prec1, &tmp);
1123 					setrecord(prec2, &tmp2);
1124 					rtn = BROWSE_TAND;
1125 					goto exitbrowse;
1126 				}
1127 				setrecord(&tmp, 0);
1128 			}
1129 			break;
1130 		case CMD_MOTHER:	/* Browse to family's mother */
1131 			if ((tmp = choose_mother(NULL, current, _(qSnowife),
1132 				_(qSidwbrs), NOASK1)) != 0) {
1133 				setrecord(prec1, &tmp);
1134 				rtn = BROWSE_INDI;
1135 				goto exitbrowse;
1136 			}
1137 			break;
1138 		case CMD_TANDEM_MOTHERS:	/* Tandem Browse to family's mother */
1139 			if ((tmp = choose_mother(NULL, current, _(qSnowife),
1140 				_(qSid1wbr), NOASK1)) != 0) {
1141 				if ((tmp2 = choose_mother(NULL, current, _(qSnowife),
1142 					_(qSid2wbr), NOASK1)) != 0) {
1143 					setrecord(prec1, &tmp);
1144 					setrecord(prec2, &tmp2);
1145 					rtn = BROWSE_TAND;
1146 					goto exitbrowse;
1147 				}
1148 				setrecord(&tmp, 0);
1149 			}
1150 			break;
1151 		case CMD_CHILDREN:	/* Browse to a child */
1152 			if ((tmp = choose_child(NULL, current, _(qSnocinf),
1153 				_(qSidcbrs), NOASK1)) != 0) {
1154 				setrecord(prec1, &tmp);
1155 				rtn = BROWSE_INDI;
1156 				goto exitbrowse;
1157 			}
1158 			break;
1159 		case CMD_TANDEM_CHILDREN:	/* browse to tandem children */
1160 			if ((tmp = choose_child(NULL, current, _(qSnocinf),
1161 				_(qSid1cbr), NOASK1)) != 0) {
1162 				if ((tmp2 = choose_child(NULL, current, _(qSnocinf),
1163 					_(qSid2cbr), NOASK1)) != 0) {
1164 					setrecord(prec1, &tmp);
1165 					setrecord(prec2, &tmp2);
1166 					rtn = BROWSE_TAND;
1167 					goto exitbrowse;
1168 				}
1169 				setrecord(&tmp, 0);
1170 			}
1171 			break;
1172 		case CMD_REMOVECHILD:	/* Remove a child */
1173 			if (readonly) {
1174 				message(_(qSronlye));
1175 				break;
1176 			}
1177 			if ((tmp = choose_child(NULL, current, _(qSnocinf),
1178 				_(qSidcrmv), DOASK1)) != 0) {
1179 				choose_and_remove_child(tmp, current, TRUE);
1180 				setrecord(&tmp, 0);
1181 			}
1182 			break;
1183 		case CMD_ADDSPOUSE:	/* Add spouse to family */
1184 			prompt_add_spouse_with_candidate(current, save);
1185 			setrecord(&save, 0);
1186 			break;
1187 		case CMD_REMOVESPOUSE:	/* Remove spouse from family */
1188 			pick_remove_spouse_from_family(current);
1189 			break;
1190 		case CMD_NEWPERSON:	/* Add person to database */
1191 			tmp = add_indi_by_edit(&disp_long_rfmt);
1192 			setrecord(&save, &tmp);
1193 			break;
1194 		case CMD_ADD_SOUR: /* add source */
1195 		case CMD_ADD_EVEN: /* add event */
1196 		case CMD_ADD_OTHR: /* add other */
1197 			c2 = (c==CMD_ADD_SOUR ? 'S' : (c==CMD_ADD_EVEN ? 'E' : 'X'));
1198 			if ((tmp = add_new_rec_maybe_ref(current, c2)) != 0) {
1199 				if (tmp == current) {
1200 					c = CMD_EDIT;
1201 					goto reprocess_fam_cmd; /* forward to edit */
1202 				} else {
1203 					setrecord(prec1, &tmp);
1204 					rtn = BROWSE_UNK;
1205 					goto exitbrowse;
1206 				}
1207 			}
1208 			break;
1209 		case CMD_ADDCHILD:	/* Add child to family */
1210 			prompt_add_child_check_save(nztop(current), nztop(save));
1211 			setrecord(&save, 0);
1212 			break;
1213 		case CMD_BROWSE: 	/* Browse to new list of persons */
1214 			seq = ask_for_indiseq(_(qSidplst), 'B', &rc);
1215 			if (!seq) break;
1216 			if (length_indiseq(seq) == 1) {
1217 				element_indiseq(seq, 0, &key, &name);
1218 				tmp = key_to_record(key);
1219 				setrecord(&current, &tmp);
1220 				remove_indiseq(seq);
1221 				seq=NULL;
1222 				if (nztype(current) != 'F') {
1223 					setrecord(prec1, &current);
1224 					rtn = BROWSE_UNK;
1225 					goto exitbrowse;
1226 				}
1227 				break;
1228 			}
1229 			*pseq = seq;
1230 			rtn = BROWSE_LIST;
1231 			goto exitbrowse;
1232 			break;
1233 		case CMD_BROWSE_ZIP_INDI:	/* Zip browse to new person */
1234 			if ((tmp = ask_for_indi(_(qSidpnxt), NOASK1)) != 0) {
1235 				setrecord(prec1, &tmp);
1236 				rtn = BROWSE_INDI;
1237 				goto exitbrowse;
1238 			}
1239 			break;
1240 		case CMD_BROWSE_ZIP_ANY:	/* Zip browse any record */
1241 			if ((tmp = ask_for_any(_(qSidnxt), NOASK1)) != 0) {
1242 				setrecord(prec1, &tmp);
1243 				rtn = BROWSE_UNK;
1244 				goto exitbrowse;
1245 			}
1246 			break;
1247 		case CMD_TANDEM:	/* Enter family tandem mode */
1248 			if ((tmp = ask_for_fam(_(qSids2fm), _(qSidc2fm))) != 0) {
1249 				setrecord(prec1, &current);
1250 				setrecord(prec2, &tmp);
1251 				rtn = BROWSE_2FAM;
1252 				goto exitbrowse;
1253 			}
1254 			break;
1255 		case CMD_SWAPCHILDREN:	/* Swap two children */
1256 			swap_children(NULL, current);
1257 			break;
1258 		case CMD_REORDERCHILD: /* Move a child in order */
1259 			reorder_child(NULL, current, &disp_shrt_rfmt);
1260 			break;
1261 		case CMD_TOGGLE_CHILDNUMS:       /* toggle children numbers */
1262 			show_childnumbers();
1263 			break;
1264 		case CMD_CHILD_DIRECT0+1:	/* Go to children by number */
1265 		case CMD_CHILD_DIRECT0+2:
1266 		case CMD_CHILD_DIRECT0+3:
1267 		case CMD_CHILD_DIRECT0+4:
1268 		case CMD_CHILD_DIRECT0+5:
1269 		case CMD_CHILD_DIRECT0+6:
1270 		case CMD_CHILD_DIRECT0+7:
1271 		case CMD_CHILD_DIRECT0+8:
1272 		case CMD_CHILD_DIRECT0+9:
1273 			if ((tmp = goto_fam_child(current, c-CMD_CHILD_DIRECT0)) != 0) {
1274 				setrecord(prec1, &tmp);
1275 				rtn = BROWSE_INDI;
1276 				goto exitbrowse;
1277 			}
1278 			message(_(qSnochil));
1279 			break;
1280 		case CMD_NEXT:	/* Go to next fam in db */
1281 			{
1282 				i = xref_nextf(nkeyp);
1283 				if (i && (tmp = qkeynum_to_frecord(i))) {
1284 					setrecord(&current, &tmp);
1285 				} else {
1286 					message(_(qSnofam));
1287 				}
1288 				break;
1289 			}
1290 		case CMD_PREV:	/* Go to prev fam in db */
1291 			{
1292 				i = xref_prevf(nkeyp);
1293 				if (i) {
1294 					tmp = keynum_to_frecord(i);
1295 					setrecord(&current, &tmp);
1296 				} else {
1297 					message(_(qSnofam));
1298 				}
1299 				break;
1300 			}
1301 		case CMD_SOURCES:	/* Browse to sources */
1302 			if ((tmp = choose_source(current, _(qSnosour), _(qSidsour))) != 0) {
1303 				setrecord(prec1, &tmp);
1304 				rtn = BROWSE_AUX;
1305 				goto exitbrowse;
1306 			}
1307 			break;
1308 		case CMD_NOTES:	/* Browse to notes */
1309 			if ((tmp = choose_note(current, _(qSnonote), _(qSidnote))) != 0) {
1310 				setrecord(prec1, &tmp);
1311 				rtn = BROWSE_AUX;
1312 				goto exitbrowse;
1313 			}
1314 			break;
1315 		case CMD_POINTERS:	/* Browse to references */
1316 			if ((tmp = choose_pointer(current, _(qSnoptr), _(qSidptr))) != 0) {
1317 				setrecord(prec1, &tmp);
1318 				rtn = BROWSE_UNK;
1319 				goto exitbrowse;
1320 			}
1321 			break;
1322 		case CMD_QUIT:
1323 			rtn = BROWSE_QUIT;
1324 			goto exitbrowse;
1325 		}
1326 	}
1327 exitbrowse:
1328 	setrecord(&current, NULL);
1329 	return rtn;
1330 }
1331 /*======================================================
1332  * handle_menu_cmds -- Handle menuing commands
1333  * That is, handle commands that are internal to the menuing
1334  * system (eg, paging, changing current menu size).
1335  * Created: 2001/01/31, Perry Rapp
1336  *====================================================*/
1337 BOOLEAN
handle_menu_cmds(INT c,BOOLEAN * reuse)1338 handle_menu_cmds (INT c, BOOLEAN * reuse)
1339 {
1340 	BOOLEAN old = *reuse;
1341 	/* if a menu command, then we CAN reuse the previous display strings */
1342 	*reuse = TRUE;
1343 	switch(c) {
1344 		case CMD_MENU_GROW: adjust_browse_menu_height(+1); return TRUE;
1345 		case CMD_MENU_SHRINK: adjust_browse_menu_height(-1); return TRUE;
1346 		case CMD_MENU_MORECOLS: adjust_browse_menu_cols(+1); return TRUE;
1347 		case CMD_MENU_LESSCOLS: adjust_browse_menu_cols(-1); return TRUE;
1348 		case CMD_MENU_MORE: cycle_browse_menu(); return TRUE;
1349 		case CMD_MENU_TOGGLE: toggle_browse_menu(); return TRUE;
1350 	}
1351 	*reuse = old;
1352 	return FALSE;
1353 }
1354 /*======================================================
1355  * handle_scroll_cmds -- Handle detail scrolling
1356  * Created: 2001/02/01, Perry Rapp
1357  *====================================================*/
1358 BOOLEAN
handle_scroll_cmds(INT c,BOOLEAN * reuse)1359 handle_scroll_cmds (INT c, BOOLEAN * reuse)
1360 {
1361 	BOOLEAN old = *reuse;
1362 	/* if a menu command, then we CAN reuse the previous display strings */
1363 	*reuse = TRUE;
1364 	switch(c) {
1365 		case CMD_SCROLL_UP: show_scroll(-1); return TRUE;
1366 		case CMD_SCROLL_DOWN: show_scroll(+1); return TRUE;
1367 	}
1368 	*reuse = old;
1369 	return FALSE;
1370 }
1371 /*======================================================
1372  * handle_indi_mode_cmds -- Handle indi modes
1373  * Created: 2001/02/04, Perry Rapp
1374  *====================================================*/
1375 BOOLEAN
handle_indi_mode_cmds(INT c,INT * mode)1376 handle_indi_mode_cmds (INT c, INT * mode)
1377 {
1378 	switch(c) {
1379 		case CMD_MODE_GEDCOM: *mode = 'g'; return TRUE;
1380 		case CMD_MODE_GEDCOMX: *mode = 'x'; return TRUE;
1381 		case CMD_MODE_GEDCOMT: *mode = 't'; return TRUE;
1382 		case CMD_MODE_PEDIGREE:
1383 			*mode = (*mode=='a')?'d':'a';
1384 			return TRUE;
1385 		case CMD_MODE_ANCESTORS: *mode = 'a'; return TRUE;
1386 		case CMD_MODE_DESCENDANTS: *mode = 'd'; return TRUE;
1387 		case CMD_MODE_NORMAL: *mode = 'n'; return TRUE;
1388 		case CMD_MODE_CYCLE:
1389 			switch(*mode) {
1390 			case 'n': *mode = 'a'; break;
1391 			case 'a': *mode = 'd'; break;
1392 			case 'd': *mode = 'g'; break;
1393 			case 'g': *mode = 'x'; break;
1394 			case 'x': *mode = 't'; break;
1395 			case 't': *mode = 'n'; break;
1396 			}
1397 			return TRUE;
1398 	}
1399 	return FALSE;
1400 }
1401 /*======================================================
1402  * handle_fam_mode_cmds -- Handle indi modes
1403  * Created: 2001/02/04, Perry Rapp
1404  *====================================================*/
1405 BOOLEAN
handle_fam_mode_cmds(INT c,INT * mode)1406 handle_fam_mode_cmds (INT c, INT * mode)
1407 {
1408 	switch(c) {
1409 		case CMD_MODE_GEDCOM: *mode = 'g'; return TRUE;
1410 		case CMD_MODE_GEDCOMX: *mode = 'x'; return TRUE;
1411 		case CMD_MODE_GEDCOMT: *mode = 't'; return TRUE;
1412 		case CMD_MODE_NORMAL: *mode = 'n'; return TRUE;
1413 		case CMD_MODE_CYCLE:
1414 			switch(*mode) {
1415 			case 'n': *mode = 'g'; break;
1416 			case 'g': *mode = 'x'; break;
1417 			case 'x': *mode = 't'; break;
1418 			case 't': *mode = 'n'; break;
1419 			}
1420 			return TRUE;
1421 	}
1422 	return FALSE;
1423 }
1424 /*======================================================
1425  * handle_aux_mode_cmds -- Handle aux modes
1426  * Created: 2001/02/11, Perry Rapp
1427  *====================================================*/
1428 BOOLEAN
handle_aux_mode_cmds(INT c,INT * mode)1429 handle_aux_mode_cmds (INT c, INT * mode)
1430 {
1431 	switch(c) {
1432 		case CMD_MODE_GEDCOM: *mode = 'g'; return TRUE;
1433 		case CMD_MODE_GEDCOMX: *mode = 'x'; return TRUE;
1434 		case CMD_MODE_GEDCOMT: *mode = 't'; return TRUE;
1435 		case CMD_MODE_CYCLE:
1436 			switch(*mode) {
1437 			case 'g': *mode = 'x'; break;
1438 			case 'x': *mode = 't'; break;
1439 			case 't': *mode = 'g'; break;
1440 			}
1441 			return TRUE;
1442 	}
1443 	return FALSE;
1444 }
1445 /*======================================================
1446  * browse_pedigree -- Handle pedigree browse selections.
1447  *====================================================*/
1448 static INT
browse_pedigree(RECORD * prec1,RECORD * prec2,INDISEQ * pseq)1449 browse_pedigree (RECORD *prec1, RECORD *prec2, INDISEQ *pseq)
1450 {
1451 	return browse_indi_modes(prec1, prec2, pseq, 'p');
1452 }
1453 /*==================================================
1454  * choose_any_source -- choose from list of all sources
1455  *================================================*/
1456 RECORD
choose_any_source(void)1457 choose_any_source (void)
1458 {
1459 	INDISEQ seq;
1460 	RECORD rec;
1461 	seq = get_all_sour();
1462 	if (!seq)
1463 	{
1464 		message(_(qSnosour));
1465 		return 0;
1466 	}
1467 	rec = choose_from_indiseq(seq, DOASK1, _(qSidsour), _(qSidsour));
1468 	remove_indiseq(seq);
1469 	return rec;
1470 }
1471 /*==================================================
1472  * choose_any_event -- choose from list of all events
1473  *================================================*/
1474 RECORD
choose_any_event(void)1475 choose_any_event (void)
1476 {
1477 	INDISEQ seq;
1478 	RECORD rec;
1479 	seq = get_all_even();
1480 	if (!seq)
1481 	{
1482 		message(_(qSnoeven));
1483 		return NULL;
1484 	}
1485 	rec = choose_from_indiseq(seq, DOASK1, _(qSideven), _(qSideven));
1486 	remove_indiseq(seq);
1487 	return rec;
1488 }
1489 /*==================================================
1490  * choose_any_other -- choose from list of all others
1491  *================================================*/
1492 RECORD
choose_any_other(void)1493 choose_any_other (void)
1494 {
1495 	INDISEQ seq;
1496 	RECORD rec;
1497 	seq = get_all_othe();
1498 	if (!seq)
1499 	{
1500 		message(_(qSnoothe));
1501 		return NULL;
1502 	}
1503 	rec = choose_from_indiseq(seq, DOASK1, _(qSidothe), _(qSidothe));
1504 	remove_indiseq(seq);
1505 	return rec;
1506 }
1507 /*==================================================
1508  * load_hist_lists -- Load previous history from database
1509  * Created: 2001/12/23, Perry Rapp
1510  *================================================*/
1511 static void
load_hist_lists(void)1512 load_hist_lists (void)
1513 {
1514 	/* V for visit history, planning to also have a change history */
1515 	INT count = getlloptint("HistorySize", 20);
1516 	if (count<0 || count > 9999)
1517 		count = 20;
1518 	init_hist(&vhist, count);
1519 	init_hist(&chist, count);
1520 	if (getlloptint("SaveHistory", 0)) {
1521 		load_nkey_list("HISTV", &vhist);
1522 		load_nkey_list("HISTC", &chist);
1523 	}
1524 }
1525 /*==================================================
1526  * save_hist_lists -- Save history into database
1527  * Created: 2001/12/23, Perry Rapp
1528  *================================================*/
1529 static void
save_hist_lists(void)1530 save_hist_lists (void)
1531 {
1532 	if (!getlloptint("SaveHistory", 0)) return;
1533 	if (readonly || immutable) return;
1534 	save_nkey_list("HISTV", &vhist);
1535 	save_nkey_list("HISTC", &chist);
1536 }
1537 /*==================================================
1538  * init_hist -- create & initialize a history list
1539  * Created: 2001/12/23, Perry Rapp
1540  *=================================================*/
1541 static void
init_hist(struct hist * histp,INT count)1542 init_hist (struct hist * histp, INT count)
1543 {
1544 	INT size = count * sizeof(histp->list[0]);
1545 	memset(histp, 0, sizeof(*histp));
1546 	histp->size = count;
1547 	histp->list = (NKEY *)stdalloc(size);
1548 	memset(histp->list, 0, size);
1549 	histp->start = -1;
1550 	histp->past_end = 0;
1551 }
1552 /*==================================================
1553  * load_nkey_list -- Load node list from record into NKEY array
1554  *  key:   [IN]  key used to store list in database
1555  *  histp: [IN]  history list to save
1556  * Fills in ndarray with data from database.
1557  * Created: 2001/12/23, Perry Rapp
1558  * TODO: should be moved to gedlib
1559  *================================================*/
1560 static void
load_nkey_list(STRING key,struct hist * histp)1561 load_nkey_list (STRING key, struct hist * histp)
1562 {
1563 	STRING rawrec;
1564 	INT * ptr;
1565 	INT count, len, i, temp;
1566 
1567 	count = 0;
1568 	if (!(rawrec = retrieve_raw_record(key, &len)))
1569 		return;
1570 	if (len < 8 || (len % 8) != 0)
1571 		return;
1572 	ptr = (INT *)rawrec;
1573 	temp = *ptr++;
1574 	if (temp<1 || temp > 9999) {
1575 		/* #records failed sanity check */
1576 		msg_error(_(qSbadhistcnt));
1577 		goto end;
1578 	}
1579 	if (temp != *ptr++) {
1580 		/* 2nd copy of #records failed to match */
1581 		msg_error(_(qSbadhistcnt2));
1582 		goto end;
1583 	}
1584 	if (len != (temp+1)*8) {
1585 		/* length should be 8 bytes per record + 8 byte header */
1586 		msg_error(_(qSbadhistlen));
1587 	}
1588 	count = temp;
1589 	if (count > histp->size) count = histp->size;
1590 	for (i=0,temp=0; i<count; ++i) {
1591 		char key[MAXKEYWIDTH+1];
1592 		char ntype = *ptr++;
1593 		INT keynum = *ptr++;
1594 		if (!ntype || !keynum)
1595 			continue;
1596 		if (keynum<1 || keynum>MAXKEYNUMBER)
1597 			continue;
1598 		snprintf(key, sizeof(key), "%c%ld", ntype, keynum);
1599 		strcpy(histp->list[temp].key, key);
1600 		histp->list[temp].ntype = ntype;
1601 		histp->list[temp].keynum = keynum;
1602 		++temp;
1603 	}
1604 	count = temp;
1605 	if (count) {
1606 		histp->start = 0;
1607 		histp->past_end = count % histp->size;
1608 	}
1609 
1610 end:
1611 	stdfree(rawrec);
1612 }
1613 /*==================================================
1614  * get_hist_count -- Calculate current size of history
1615  * Created: 2001/12/23, Perry Rapp
1616  *================================================*/
1617 static INT
get_hist_count(struct hist * histp)1618 get_hist_count (struct hist * histp)
1619 {
1620 	if (!histp->size || histp->start==-1)
1621 		return 0;
1622 	else if (histp->past_end > histp->start)
1623 		return histp->past_end - histp->start;
1624 	else
1625 		return histp->size - histp->start + histp->past_end;
1626 }
1627 /*==================================================
1628  * save_nkey_list -- Save nkey list from circular array
1629  *  key:     [IN]  key used to store list in database
1630  *  hist:    [IN]  history list to save
1631  * Created: 2001/12/23, Perry Rapp
1632  * TODO: should be moved to gedlib
1633  *================================================*/
1634 static void
save_nkey_list(STRING key,struct hist * histp)1635 save_nkey_list (STRING key, struct hist * histp)
1636 {
1637 	FILE * fp=0;
1638 	INT next, prev, count, temp;
1639 	size_t rtn;
1640 
1641 	count = get_hist_count(histp);
1642 
1643 	unlink(editfile);
1644 
1645 	fp = fopen(editfile, LLWRITETEXT);
1646 	if (!fp) return;
1647 
1648 	/* write count first -- twice just to take up 8 bytes same as records */
1649 	rtn = fwrite(&count, 4, 1, fp); ASSERT(rtn==1);
1650 	rtn = fwrite(&count, 4, 1, fp); ASSERT(rtn==1);
1651 
1652 	prev = -1;
1653 	next = histp->start;
1654 	while (1) {
1655 		/* write type & number, 4 bytes each */
1656 		temp = histp->list[next].ntype;
1657 		rtn = fwrite(&temp, 4, 1, fp); ASSERT(rtn==1);
1658 		temp = histp->list[next].keynum;
1659 		rtn = fwrite(&temp, 4, 1, fp); ASSERT(rtn==1);
1660 		prev = next;
1661 		next = (next+1) % histp->size;
1662 		if (next == histp->past_end)
1663 			break; /* finished them all */
1664 	}
1665 	fclose(fp);
1666 
1667 	store_text_file_to_db(key, editfile, 0);
1668 }
1669 /*==================================================
1670  * history_record_change -- add node to change history
1671  *  (if different from top of history)
1672  * Created: 2002/06/23, Perry Rapp
1673  *================================================*/
1674 void
history_record_change(RECORD rec)1675 history_record_change (RECORD rec)
1676 {
1677 	history_record(rec, &chist);
1678 }
1679 /*==================================================
1680  * history_record -- add node to history if different from top of history
1681  * Created: 2001/03?, Perry Rapp
1682  *================================================*/
1683 static void
history_record(RECORD rec,struct hist * histp)1684 history_record (RECORD rec, struct hist * histp)
1685 {
1686 	NKEY nkey = nkey_zero();
1687 	INT prev, next, i;
1688 	INT count = get_hist_count(histp);
1689 	INT protect = getlloptint("HistoryBounceSuppress", 0);
1690 	if (!histp->size) return;
1691 	if (histp->start==-1) {
1692 		histp->start = histp->past_end;
1693 		node_to_nkey(nztop(rec), &histp->list[histp->start]);
1694 		histp->past_end = (histp->start+1) % histp->size;
1695 		return;
1696 	}
1697 	/*
1698 	copy new node into nkey variable so we can check
1699 	if this is the same as our most recent (histp->list[last])
1700 	*/
1701 	node_to_nkey(nztop(rec), &nkey);
1702 	if (protect<1 || protect>99)
1703 		protect=1;
1704 	if (protect>count)
1705 		protect=count;
1706 	/* traverse from most recent back (bounce suppression) */
1707 	prev = -1;
1708 	next = (histp->past_end-1);
1709 	if (next < 0) next += histp->size;
1710 	for (i=0; i<protect; ++i) {
1711 		if (nkey_eq(&nkey, &histp->list[next]))
1712 			return;
1713 		prev = next;
1714 		if (--next < 0) next += histp->size;
1715 	}
1716 	/* it is a new one so add it to circular list */
1717 	nkey_copy(&nkey, &histp->list[histp->past_end]);
1718 	if (histp->start == histp->past_end) {
1719 		/* full buffer & we just overwrote the oldest */
1720 		histp->start = (histp->start+1) % histp->size;
1721 	}
1722 	/* advance pointer to account for what we just added */
1723 	histp->past_end = (histp->past_end+1) % histp->size;
1724 }
1725 /*==================================================
1726  * history_back -- return prev RECORD in history, if exists
1727  * Created: 2001/03?, Perry Rapp
1728  *================================================*/
1729 static RECORD
history_back(struct hist * histp)1730 history_back (struct hist * histp)
1731 {
1732 	INT last=0;
1733 	RECORD rec=0;
1734 	if (!histp->size || histp->start==-1)
1735 		return NULL;
1736 	/* back up from histp->past_end to current item */
1737 	last = histp->past_end-1;
1738 	if (last < 0) last += histp->size;
1739 	while (last != histp->start) {
1740 		/* loop is to keep going over deleted ones */
1741 		/* now back up before current item */
1742 		if (--last < 0) last += histp->size;
1743 		nkey_to_record(&histp->list[last], &rec);
1744 		if (rec) {
1745 			histp->past_end = (last+1) % histp->size;
1746 			return rec;
1747 		}
1748 	}
1749 	return NULL;
1750 }
1751 /*==================================================
1752  * history_fwd -- return later NODE in history, if exists
1753  * Created: 2001/03?, Perry Rapp
1754  *================================================*/
1755 static RECORD
history_fwd(struct hist * histp)1756 history_fwd (struct hist * histp)
1757 {
1758 	INT next;
1759 	RECORD rec;
1760 	if (!histp->size || histp->past_end == histp->start)
1761 		return NULL; /* at end of full history */
1762 	next = histp->past_end;
1763 	nkey_to_record(&histp->list[next], &rec);
1764 	return rec;
1765 }
1766 /*==================================================
1767  * disp_vhistory_list -- show user the visited history list
1768  *  returns NULL if no history or if user cancels
1769  * Created: 2002/06/23, Perry Rapp
1770  *================================================*/
1771 static RECORD
disp_vhistory_list(void)1772 disp_vhistory_list (void)
1773 {
1774 	return do_disp_history_list(&vhist);
1775 }
1776 /*==================================================
1777  * disp_chistory_list -- show user the changed history list
1778  *  returns NULL if no history or if user cancels
1779  * Created: 2002/06/23, Perry Rapp
1780  *================================================*/
1781 static RECORD
disp_chistory_list(void)1782 disp_chistory_list (void)
1783 {
1784 	return do_disp_history_list(&chist);
1785 }
1786 /*==================================================
1787  * get_chistory_list -- return indiseq of change history
1788  *  returns NULL if no history
1789  *================================================*/
1790 INDISEQ
get_chistory_list(void)1791 get_chistory_list (void)
1792 {
1793 	return get_history_list(&chist);
1794 }
1795 /*==================================================
1796  * get_vhistory_list -- Return indiseq of visit history
1797  *  returns NULL if no history
1798  *================================================*/
1799 INDISEQ
get_vhistory_list(void)1800 get_vhistory_list (void)
1801 {
1802 	return get_history_list(&vhist);
1803 }
1804 /*==================================================
1805  * do_disp_history_list -- let user choose from history list
1806  *  calls message(nohist) if none found
1807  *  returns NULL if no history or if user cancels
1808  * Created: 2001/04/12, Perry Rapp
1809  *================================================*/
1810 static RECORD
do_disp_history_list(struct hist * histp)1811 do_disp_history_list (struct hist * histp)
1812 {
1813 	INDISEQ seq = get_history_list(histp);
1814 	RECORD rec=0;
1815 
1816 	if (!seq) {
1817 		message(_(qSnohist));
1818 		return NULL;
1819 	}
1820 	rec = choose_from_indiseq(seq, DOASK1, _(qSidhist), _(qSidhist));
1821 	remove_indiseq(seq);
1822 	return rec;
1823 }
1824 /*==================================================
1825  * get_history_list -- return specified history list as indiseq
1826  *================================================*/
1827 static INDISEQ
get_history_list(struct hist * histp)1828 get_history_list (struct hist * histp)
1829 {
1830 	INDISEQ seq=0;
1831 	INT next, prev;
1832 	if (!histp->size || histp->start==-1) {
1833 		return NULL;
1834 	}
1835 	/* add all items of history to seq */
1836 	seq = create_indiseq_null();
1837 	prev = -1;
1838 	next = histp->start;
1839 	while (1) {
1840 		NODE node=0;
1841 		nkey_to_node(&histp->list[next], &node);
1842 		if (node) {
1843 			STRING key = node_to_key(node);
1844 			append_indiseq_null(seq, key, NULL, TRUE, FALSE);
1845 		}
1846 		prev = next;
1847 		next = (next+1) % histp->size;
1848 		if (next == histp->past_end)
1849 			break; /* finished them all */
1850 	}
1851 	return seq;
1852 }
1853 /*==================================================
1854  * ask_clear_history -- delete vist history
1855  *  (first verify via y/n prompt)
1856  * Created: 2001/12/23, Perry Rapp
1857  *================================================*/
1858 static void
ask_clear_history(struct hist * histp)1859 ask_clear_history (struct hist * histp)
1860 {
1861 	char buffer[120];
1862 	INT count;
1863 
1864 	if (!histp->size || histp->start==-1) {
1865 		message(_(qSnohist));
1866 		return;
1867 	}
1868 	count = get_hist_count(histp);
1869 	sprintf(buffer, _(qShistclr), count);
1870 	if (ask_yes_or_no(buffer))
1871 		histp->start = -1;
1872 }
1873 /*==================================================
1874  * handle_history_cmds -- handle the history commands
1875  *  returns 0 for not a history command
1876  *  returns 1 if handled but continue on same page
1877  *  returns -1 if handled & set *pindi1 for switching pages
1878  * Created: 2001/04/12, Perry Rapp
1879  *================================================*/
1880 static INT
handle_history_cmds(INT c,RECORD * prec1)1881 handle_history_cmds (INT c, RECORD * prec1)
1882 {
1883 	RECORD rec=0;
1884 	if (c == CMD_VHISTORY_BACK) {
1885 		rec = history_back(&vhist);
1886 		if (rec) {
1887 			*prec1 = rec;
1888 			return -1; /* handled, change pages */
1889 		}
1890 		message(_(qSnohist));
1891 		return 1; /* handled, stay here */
1892 	}
1893 	if (c == CMD_CHISTORY_BACK) {
1894 		rec = history_back(&chist);
1895 		if (rec) {
1896 			*prec1 = rec;
1897 			return -1; /* handled, change pages */
1898 		}
1899 		message(_(qSnohist));
1900 		return 1; /* handled, stay here */
1901 	}
1902 	if (c == CMD_VHISTORY_FWD) {
1903 		rec = history_fwd(&vhist);
1904 		if (rec) {
1905 			*prec1 = rec;
1906 			return -1; /* handled, change pages */
1907 		}
1908 		message(_(qSnohist));
1909 		return 1; /* handled, stay here */
1910 	}
1911 	if (c == CMD_CHISTORY_FWD) {
1912 		rec = history_fwd(&chist);
1913 		if (rec) {
1914 			*prec1 = rec;
1915 			return -1; /* handled, change pages */
1916 		}
1917 		message(_(qSnohist));
1918 		return 1; /* handled, stay here */
1919 	}
1920 	if (c == CMD_VHISTORY_LIST) {
1921 		rec = disp_vhistory_list();
1922 		if (rec) {
1923 			*prec1 = rec;
1924 			return -1; /* handled, change pages */
1925 		}
1926 		return 1;
1927 	}
1928 	if (c == CMD_CHISTORY_LIST) {
1929 		rec = disp_chistory_list();
1930 		if (rec) {
1931 			*prec1 = rec;
1932 			return -1; /* handled, change pages */
1933 		}
1934 		return 1;
1935 	}
1936 	if (c == CMD_VHISTORY_CLEAR) {
1937 		ask_clear_history(&vhist);
1938 		return 1;
1939 	}
1940 	if (c == CMD_CHISTORY_CLEAR) {
1941 		ask_clear_history(&chist);
1942 		return 1;
1943 	}
1944 	return 0; /* unhandled */
1945 }
1946 /*==================================================
1947  * add_new_rec_maybe_ref -- add a new record
1948  *  and optionally create a reference to it from
1949  *  current record
1950  * rtns: returns new node if user wants to browse to it
1951  *       current node to edit it (current node)
1952  *       NULL to just stay where we are
1953  * Created: 2001/04/06, Perry Rapp
1954  *================================================*/
1955 static RECORD
add_new_rec_maybe_ref(RECORD current,char ntype)1956 add_new_rec_maybe_ref (RECORD current, char ntype)
1957 {
1958 	RECORD newrec=0;
1959 	NODE newnode;
1960 	STRING choices[4];
1961 	char title[60];
1962 	INT rtn;
1963 
1964 	/* create new node of requested type */
1965 	if (ntype=='E')
1966 		newrec=edit_add_event();
1967 	else if (ntype=='S')
1968 		newrec=edit_add_source();
1969 	else
1970 		newrec=edit_add_other();
1971 	/* bail if user cancelled creation */
1972 	if (!newrec)
1973 		return NULL;
1974 	newnode = nztop(newrec);
1975 	/* sanity check for long tags in others */
1976 	if (strlen(ntag(newnode))>40) {
1977 		msg_info(_(qStag2lng2cnc));
1978 		return newrec;
1979 	}
1980 	/* now ask the user how to connect the new node */
1981 	sprintf(title, _(qSnewrecis), nxref(newnode));
1982 	msg_info(title);
1983 	/* keep new node # in status so it will be visible during edit */
1984 	lock_status_msg(TRUE);
1985 	choices[0] = _(qSautoxref);
1986 	choices[1] = _(qSeditcur);
1987 	choices[2] = _(qSgotonew);
1988 	choices[3] = _(qSstaycur);
1989 	rtn = choose_from_array(NULL, 4, choices);
1990 	lock_status_msg(FALSE);
1991 	switch(rtn) {
1992 	case 0:
1993 		autoadd_xref(current, newnode);
1994 		return 0;
1995 	case 1:
1996 		return current; /* convention - return current node for edit */
1997 	case 2:
1998 		return newrec;
1999 	default:
2000 		return 0;
2001 	}
2002 }
2003 /*==================================================
2004  * autoadd_xref -- add trailing xref from existing node
2005  *  to new node
2006  * Created: 2001/11/11, Perry Rapp
2007  *================================================*/
2008 static void
autoadd_xref(RECORD rec,NODE newnode)2009 autoadd_xref (RECORD rec, NODE newnode)
2010 {
2011 	NODE xref; /* new xref added to end of node */
2012 	NODE find, prev; /* used finding last child of node */
2013 	NODE node = nztop(rec);
2014 	xref = create_node(NULL, ntag(newnode), nxref(newnode), node);
2015 	if (!(find = nchild(node))) {
2016 		nchild(node) = xref;
2017 	} else {
2018 		/* find last child of node */
2019 		while(find) {
2020 			prev = find;
2021 			find = nsibling(find);
2022 		}
2023 		nsibling(prev) = xref;
2024 	}
2025 
2026 	normalize_rec(rec);
2027 
2028 	unknown_node_to_dbase(node);
2029 }
2030 /*==================================================
2031  * get_vhist_len -- how many records currently in visit history list ?
2032  * Created: 2002/06/23, Perry Rapp
2033  *================================================*/
2034 INT
get_vhist_len(void)2035 get_vhist_len (void)
2036 {
2037 	return get_hist_count(&vhist);
2038 }
2039 /*==================================================
2040  * get_vhist_len -- how many records currently in change history list ?
2041  * Created: 2002/06/23, Perry Rapp
2042  *================================================*/
2043 INT
get_chist_len(void)2044 get_chist_len (void)
2045 {
2046 	return get_hist_count(&chist);
2047 }
2048 /*==================================================
2049  * init_browse_module -- Do any initialization
2050  *  This is after database is determined, and before
2051  *  main_menu begins processing.
2052  * Created: 2001/12/23, Perry Rapp
2053  *================================================*/
2054 void
init_browse_module(void)2055 init_browse_module (void)
2056 {
2057 	load_hist_lists();
2058 }
2059 /*==================================================
2060  * term_browse_module -- Cleanup for browse module
2061  *  Primarily to persist history
2062  * Created: 2001/12/23, Perry Rapp
2063  *================================================*/
2064 void
term_browse_module(void)2065 term_browse_module (void)
2066 {
2067 	save_hist_lists();
2068 }
2069