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(¤t, 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(¤t, &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(¤t, &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(¤t, &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(¤t, &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(¤t, &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(¤t, &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(¤t, &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(¤t, &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(¤t, &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(¤t, &tmp);
606 remove_indiseq(seq);
607 seq=NULL;
608 if (nztype(current) != 'I') {
609 setrecord(prec1, ¤t);
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, ¤t);
623 setrecord(¤t, &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, ¤t);
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(¤t, &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(¤t, &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(¤t, 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(¤t, 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(¤t, &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(¤t, &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(¤t, &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(¤t, 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(¤t, 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(¤t, &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(¤t, &tmp);
1220 remove_indiseq(seq);
1221 seq=NULL;
1222 if (nztype(current) != 'F') {
1223 setrecord(prec1, ¤t);
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, ¤t);
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(¤t, &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(¤t, &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(¤t, 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