1 /* 2 * "GEDKeeper", the personal genealogical database editor. 3 * Copyright (C) 2009-2021 by Sergey V. Zhdanovskih. 4 * 5 * This file is part of "GEDKeeper". 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 using System.Collections.Generic; 22 using BSLib; 23 using BSLib.Calendar; 24 using BSLib.Design.Graphics; 25 using GDModel; 26 using GDModel.Providers.GEDCOM; 27 using GKCore.Interfaces; 28 using GKCore.Options; 29 using GKCore.Types; 30 31 namespace GKCore.Export 32 { 33 /// <summary> 34 /// 35 /// </summary> 36 public sealed class PedigreeExporter : ReportExporter 37 { 38 private class PedigreePerson 39 { 40 public PedigreePerson Parent; 41 public string Id; 42 public GDMIndividualRecord IRec; 43 public int Level; 44 public readonly List<string> Sources; 45 public int FamilyOrder; 46 public int ChildIdx; 47 PedigreePerson()48 public PedigreePerson() 49 { 50 Sources = new List<string>(); 51 } 52 GetOrderStr()53 private string GetOrderStr() 54 { 55 string order = ConvertHelper.AdjustNumber(FamilyOrder, 2); 56 string result = ((Parent == null) ? order : Parent.GetOrderStr() + order); 57 return result; 58 } 59 GetInternalStr()60 public string GetInternalStr() 61 { 62 return ConvertHelper.AdjustNumber(Level, 2) + GetOrderStr(); 63 } 64 } 65 66 private class PedigreeEvent 67 { 68 public readonly GDMIndividualRecord IRec; 69 public readonly GDMCustomEvent Event; 70 public readonly UDN Date; 71 PedigreeEvent(GDMIndividualRecord iRec, GDMCustomEvent evt)72 public PedigreeEvent(GDMIndividualRecord iRec, GDMCustomEvent evt) 73 { 74 IRec = iRec; 75 Event = evt; 76 Date = (evt == null) ? UDN.CreateEmpty() : evt.Date.GetUDN(); 77 } 78 } 79 80 public enum PedigreeKind 81 { 82 Ascend, 83 Descend_dAboville, 84 Descend_Konovalov 85 } 86 87 private PedigreeFormat fFormat; 88 private PedigreeKind fKind; 89 private ExtList<PedigreePerson> fPersonList; 90 private readonly GDMIndividualRecord fRoot; 91 private readonly ShieldState fShieldState; 92 private StringList fSourceList; 93 94 private IFont fTitleFont; 95 private IFont fChapFont; 96 private IFont fPersonFont; 97 private IFont fLinkFont; 98 private IFont fTextFont, fSupText; 99 100 public PedigreeKind Kind 101 { 102 get { return fKind; } 103 set { fKind = value; } 104 } 105 106 public GDMIndividualRecord Root 107 { 108 get { return fRoot; } 109 } 110 111 public ShieldState ShieldState 112 { 113 get { return fShieldState; } 114 } 115 PedigreeExporter(IBaseWindow baseWin, GDMIndividualRecord root)116 public PedigreeExporter(IBaseWindow baseWin, GDMIndividualRecord root) : base(baseWin, false) 117 { 118 fRoot = root; 119 fTitle = LangMan.LS(LSID.LSID_ExpPedigree) + ": " + GKUtils.GetNameString(fRoot, true, false); 120 fShieldState = baseWin.Context.ShieldState; 121 } 122 FindPerson(GDMIndividualRecord iRec)123 private PedigreePerson FindPerson(GDMIndividualRecord iRec) 124 { 125 if (iRec == null) return null; 126 127 PedigreePerson res = null; 128 129 int num = fPersonList.Count; 130 for (int i = 0; i < num; i++) { 131 PedigreePerson item = fPersonList[i]; 132 133 if (item.IRec == iRec) { 134 res = item; 135 break; 136 } 137 } 138 139 return res; 140 } 141 WritePerson(PedigreePerson person)142 private void WritePerson(PedigreePerson person) 143 { 144 fWriter.BeginParagraph(TextAlignment.taJustify, 6f, 6f); 145 fWriter.AddParagraphChunkAnchor(GetIdStr(person) + ". " + GKUtils.GetNameString(person.IRec, true, false), fPersonFont, person.Id); 146 fWriter.AddParagraphChunk(GKUtils.GetPedigreeLifeStr(person.IRec, fOptions.PedigreeOptions.Format), fTextFont); 147 148 if (fOptions.PedigreeOptions.IncludeSources && person.Sources.Count > 0) { 149 fWriter.AddParagraphChunk(" ", fTextFont); 150 151 int num = person.Sources.Count; 152 for (int i = 0; i < num; i++) { 153 string lnk = person.Sources[i]; 154 155 if (i > 0) { 156 fWriter.AddParagraphChunkLink(", ", fTextFont, "", true); 157 } 158 159 fWriter.AddParagraphChunkLink(lnk, fSupText, "src_" + lnk, true); 160 } 161 } 162 163 fWriter.EndParagraph(); 164 165 switch (fFormat) { 166 case PedigreeFormat.Excess: 167 WriteExcessFmt(person); 168 break; 169 170 case PedigreeFormat.Compact: 171 WriteCompactFmt(person); 172 break; 173 } 174 } 175 idLink(GDMIndividualRecord iRec)176 private string idLink(GDMIndividualRecord iRec) 177 { 178 PedigreePerson person = FindPerson(iRec); 179 return (person == null) ? "" : person.Id; 180 } 181 GetIdStr(PedigreePerson person)182 private string GetIdStr(PedigreePerson person) 183 { 184 string result = person.Id; 185 186 if (fKind == PedigreeKind.Descend_Konovalov && person.Parent != null) { 187 GDMFamilyRecord family = fTree.GetPtrValue(person.IRec.ChildToFamilyLinks[0]); 188 string spStr = ""; 189 int idx = person.Parent.IRec.IndexOfSpouse(family); 190 if (person.Parent.IRec.SpouseToFamilyLinks.Count > 1) { 191 spStr = "/" + (idx + 1).ToString(); 192 } 193 result += spStr; 194 } 195 return result; 196 } 197 WriteExcessFmt(PedigreePerson person)198 private void WriteExcessFmt(PedigreePerson person) 199 { 200 fWriter.AddParagraph(LangMan.LS(LSID.LSID_Sex) + ": " + GKUtils.SexStr(person.IRec.Sex), fTextFont); 201 202 string st = GKUtils.GetLifeExpectancyStr(person.IRec); 203 if (st != "?" && st != "") { 204 fWriter.AddParagraph(LangMan.LS(LSID.LSID_LifeExpectancy) + ": " + st, fTextFont); 205 } 206 207 GDMIndividualRecord father, mother; 208 fTree.GetParents(person.IRec, out father, out mother); 209 210 if (father != null) { 211 fWriter.AddParagraphLink(LangMan.LS(LSID.LSID_Father) + ": " + GKUtils.GetNameString(father, true, false) + " ", fTextFont, idLink(father), fLinkFont); 212 } 213 214 if (mother != null) { 215 fWriter.AddParagraphLink(LangMan.LS(LSID.LSID_Mother) + ": " + GKUtils.GetNameString(mother, true, false) + " ", fTextFont, idLink(mother), fLinkFont); 216 } 217 218 var evList = new ExtList<PedigreeEvent>(true); 219 try { 220 int i; 221 if (person.IRec.HasEvents) { 222 fWriter.AddParagraph(LangMan.LS(LSID.LSID_Events) + ":", fTextFont); 223 224 int num = person.IRec.Events.Count; 225 for (i = 0; i < num; i++) { 226 GDMCustomEvent evt = person.IRec.Events[i]; 227 if (!(evt is GDMIndividualAttribute) || fOptions.PedigreeOptions.IncludeAttributes) { 228 evList.Add(new PedigreeEvent(person.IRec, evt)); 229 } 230 } 231 WriteEventList(person, evList); 232 } 233 234 int num2 = person.IRec.SpouseToFamilyLinks.Count; 235 for (i = 0; i < num2; i++) { 236 GDMFamilyRecord family = fTree.GetPtrValue(person.IRec.SpouseToFamilyLinks[i]); 237 if (!fBase.Context.IsRecordAccess(family.Restriction)) continue; 238 239 GDMIndividualRecord spRec; 240 string unk; 241 if (person.IRec.Sex == GDMSex.svMale) { 242 spRec = fTree.GetPtrValue(family.Wife); 243 st = LangMan.LS(LSID.LSID_Wife) + ": "; 244 unk = LangMan.LS(LSID.LSID_UnkFemale); 245 } else { 246 spRec = fTree.GetPtrValue(family.Husband); 247 st = LangMan.LS(LSID.LSID_Husband) + ": "; 248 unk = LangMan.LS(LSID.LSID_UnkMale); 249 } 250 251 string sps; 252 if (spRec != null) { 253 sps = st + GKUtils.GetNameString(spRec, true, false) + GKUtils.GetPedigreeLifeStr(spRec, fOptions.PedigreeOptions.Format)/* + this.idLink(this.FindPerson(irec))*/; 254 } else { 255 sps = st + unk; 256 } 257 258 fWriter.AddParagraph(sps, fTextFont); 259 260 evList.Clear(); 261 int childrenCount = family.Children.Count; 262 for (int j = 0; j < childrenCount; j++) { 263 GDMIndividualRecord child = fTree.GetPtrValue(family.Children[j]); 264 evList.Add(new PedigreeEvent(child, child.FindEvent(GEDCOMTagType.BIRT))); 265 } 266 WriteEventList(person, evList); 267 } 268 } finally { 269 evList.Dispose(); 270 } 271 272 if (fOptions.PedigreeOptions.IncludeNotes && person.IRec.HasNotes) { 273 fWriter.AddParagraph(LangMan.LS(LSID.LSID_RPNotes) + ":", fTextFont); 274 275 fWriter.BeginList(); 276 277 int notesCount = person.IRec.Notes.Count; 278 for (int i = 0; i < notesCount; i++) { 279 GDMLines noteLines = fTree.GetNoteLines(person.IRec.Notes[i]); 280 fWriter.AddListItem(" " + GKUtils.MergeStrings(noteLines), fTextFont); 281 } 282 283 fWriter.EndList(); 284 } 285 } 286 WriteCompactFmt(PedigreePerson person)287 private void WriteCompactFmt(PedigreePerson person) 288 { 289 if (fOptions.PedigreeOptions.IncludeNotes && person.IRec.HasNotes) { 290 int num = person.IRec.Notes.Count; 291 for (int i = 0; i < num; i++) { 292 GDMLines noteLines = fTree.GetNoteLines(person.IRec.Notes[i]); 293 fWriter.AddParagraph(GKUtils.MergeStrings(noteLines), fTextFont); 294 } 295 } 296 297 bool spIndex = person.IRec.SpouseToFamilyLinks.Count > 1; 298 299 int num2 = person.IRec.SpouseToFamilyLinks.Count; 300 for (int i = 0; i < num2; i++) { 301 GDMFamilyRecord family = fTree.GetPtrValue(person.IRec.SpouseToFamilyLinks[i]); 302 if (fBase.Context.IsRecordAccess(family.Restriction)) { 303 GDMIndividualRecord spRec; 304 string st; 305 string unk; 306 if (person.IRec.Sex == GDMSex.svMale) { 307 spRec = fTree.GetPtrValue(family.Wife); 308 st = LangMan.LS(LSID.LSID_WifeSign); 309 unk = LangMan.LS(LSID.LSID_UnkFemale); 310 } else { 311 spRec = fTree.GetPtrValue(family.Husband); 312 st = LangMan.LS(LSID.LSID_HusbSign); 313 unk = LangMan.LS(LSID.LSID_UnkMale); 314 } 315 316 if (spIndex) { 317 st += (i + 1).ToString(); 318 } 319 st += " - "; 320 321 if (spRec != null) { 322 st = st + GKUtils.GetNameString(spRec, true, false) + GKUtils.GetPedigreeLifeStr(spRec, fOptions.PedigreeOptions.Format)/* + this.idLink(this.FindPerson(irec))*/; 323 } else { 324 st += unk; 325 } 326 327 fWriter.AddParagraph(st, fTextFont); 328 } 329 } 330 } 331 EventsCompare(PedigreeEvent item1, PedigreeEvent item2)332 private static int EventsCompare(PedigreeEvent item1, PedigreeEvent item2) 333 { 334 return item1.Date.CompareTo(item2.Date); 335 } 336 WriteEventList(PedigreePerson person, ExtList<PedigreeEvent> evList)337 private void WriteEventList(PedigreePerson person, ExtList<PedigreeEvent> evList) 338 { 339 evList.QuickSort(EventsCompare); 340 341 int evtNum = evList.Count; 342 for (int i = 0; i < evtNum; i++) { 343 GDMCustomEvent evt = evList[i].Event; 344 if (evt != null && Equals(evList[i].IRec, person.IRec)) { 345 var evtType = evt.GetTagType(); 346 347 if (evtType == GEDCOMTagType.BIRT) { 348 evList.Exchange(i, 0); 349 } else if (evtType == GEDCOMTagType.DEAT) { 350 evList.Exchange(i, evtNum - 1); 351 } 352 } 353 } 354 355 fWriter.BeginList(); 356 357 var dateFormat = GlobalOptions.Instance.DefDateFormat; 358 for (int i = 0; i < evtNum; i++) { 359 PedigreeEvent evObj = evList[i]; 360 GDMCustomEvent evt = evObj.Event; 361 string li; 362 363 if (evObj.IRec == person.IRec) { 364 var evtName = evt.GetTagName(); 365 int ev = GKUtils.GetPersonEventIndex(evtName); 366 string st; 367 if (ev == 0) { 368 st = evt.Classification; 369 } else { 370 st = (ev > 0) ? LangMan.LS(GKData.PersonEvents[ev].Name) : evtName; 371 } 372 373 string dt = GKUtils.GEDCOMEventToDateStr(evt, dateFormat, false); 374 li = dt + ": " + st + "."; 375 if (evt.HasPlace && evt.Place.StringValue != "") { 376 li = li + " " + LangMan.LS(LSID.LSID_Place) + ": " + evt.Place.StringValue; 377 } 378 379 fWriter.AddListItem(" " + li, fTextFont); 380 } else { 381 string dt = (evt == null) ? "?" : GKUtils.GEDCOMEventToDateStr(evt, dateFormat, false); 382 383 string st = (evObj.IRec.Sex == GDMSex.svMale) ? LangMan.LS(LSID.LSID_HeWasBorn) : LangMan.LS(LSID.LSID_SheWasBorn); 384 385 li = string.Format("{0}: {1} {2}", dt, st, GKUtils.GetNameString(evObj.IRec, true, false)); 386 PedigreePerson prs = FindPerson(evObj.IRec); 387 string id = (prs != null) ? prs.Id : ""; 388 389 fWriter.AddListItemLink(" " + li + " ", fTextFont, id, fLinkFont); 390 } 391 } 392 393 fWriter.EndList(); 394 } 395 GenStep(PedigreePerson parent, GDMIndividualRecord iRec, int level, int familyOrder)396 private void GenStep(PedigreePerson parent, GDMIndividualRecord iRec, int level, int familyOrder) 397 { 398 if (iRec == null) return; 399 400 PedigreePerson res = new PedigreePerson(); 401 res.Parent = parent; 402 res.IRec = iRec; 403 res.Level = level; 404 res.ChildIdx = 0; 405 res.FamilyOrder = familyOrder; 406 fPersonList.Add(res); 407 408 if (fOptions.PedigreeOptions.IncludeSources && iRec.HasSourceCitations) { 409 int num = iRec.SourceCitations.Count; 410 for (int i = 0; i < num; i++) { 411 var sourceRec = fTree.GetPtrValue<GDMSourceRecord>(iRec.SourceCitations[i]); 412 if (sourceRec == null) continue; 413 414 int srcIndex = fSourceList.IndexOfObject(sourceRec); 415 if (srcIndex < 0) { 416 string srcName = sourceRec.ShortTitle; 417 string srcTitle = GKUtils.MergeStrings(sourceRec.Title.Lines); 418 if (!string.IsNullOrEmpty(srcName) && !string.IsNullOrEmpty(srcTitle)) { 419 srcName += "\n"; 420 } 421 srcName += srcTitle; 422 423 srcIndex = fSourceList.AddObject(srcName, sourceRec); 424 } 425 426 res.Sources.Add((srcIndex + 1).ToString()); 427 } 428 } 429 430 if (fKind == PedigreeKind.Ascend) { 431 if (iRec.ChildToFamilyLinks.Count > 0) { 432 GDMFamilyRecord family = fTree.GetPtrValue(iRec.ChildToFamilyLinks[0]); 433 if (fBase.Context.IsRecordAccess(family.Restriction)) { 434 GDMIndividualRecord father, mother; 435 fBase.Context.Tree.GetSpouses(family, out father, out mother); 436 437 GenStep(res, father, level + 1, 1); 438 GenStep(res, mother, level + 1, 1); 439 } 440 } 441 } else { 442 int num2 = iRec.SpouseToFamilyLinks.Count; 443 for (int j = 0; j < num2; j++) { 444 GDMFamilyRecord family = fTree.GetPtrValue(iRec.SpouseToFamilyLinks[j]); 445 if (!fBase.Context.IsRecordAccess(family.Restriction)) continue; 446 447 fBase.Context.ProcessFamily(family); 448 449 int num3 = family.Children.Count; 450 for (int i = 0; i < num3; i++) { 451 GDMIndividualRecord child = fTree.GetPtrValue(family.Children[i]); 452 GenStep(res, child, level + 1, i + 1); 453 } 454 } 455 } 456 } 457 PersonsCompare(PedigreePerson item1, PedigreePerson item2)458 private static int PersonsCompare(PedigreePerson item1, PedigreePerson item2) 459 { 460 return string.CompareOrdinal(item1.GetInternalStr(), item2.GetInternalStr()); 461 } 462 ReIndex()463 private void ReIndex() 464 { 465 fPersonList.QuickSort(PersonsCompare); 466 467 int num3 = fPersonList.Count; 468 for (int i = 0; i < num3; i++) { 469 PedigreePerson obj = fPersonList[i]; 470 471 switch (fKind) { 472 case PedigreeKind.Descend_dAboville: 473 if (obj.Parent == null) { 474 obj.Id = "1"; 475 } else { 476 obj.Parent.ChildIdx++; 477 obj.Id = obj.Parent.Id + "." + obj.Parent.ChildIdx.ToString(); 478 } 479 break; 480 481 case PedigreeKind.Ascend: 482 case PedigreeKind.Descend_Konovalov: 483 obj.Id = (i + 1).ToString(); 484 if (obj.Parent != null) { 485 string pid = obj.Parent.Id; 486 487 int p = pid.IndexOf("-"); 488 if (p >= 0) pid = pid.Substring(0, p); 489 490 obj.Id = obj.Id + "-" + pid; 491 } 492 break; 493 } 494 } 495 } 496 InternalGenerate()497 protected override void InternalGenerate() 498 { 499 bool isRtf = false; 500 #if !NETSTANDARD 501 isRtf = (fWriter is RTFWriter); 502 #endif 503 504 IColor clrBlack = AppHost.GfxProvider.CreateColor(0x000000); 505 IColor clrBlue = AppHost.GfxProvider.CreateColor(0x0000FF); 506 507 fTitleFont = fWriter.CreateFont("", 16f/*20f*/, true, false, clrBlack); 508 fChapFont = fWriter.CreateFont("", 14f/*16f*/, true, false, clrBlack); 509 fPersonFont = fWriter.CreateFont("", 12f/*10f*/, true, false, clrBlack); 510 fLinkFont = fWriter.CreateFont("", 10f/*8f*/, false, true, clrBlue); 511 fTextFont = fWriter.CreateFont("", 10f/*8f*/, false, false, clrBlack); 512 fSupText = fWriter.CreateFont("", (isRtf ? 12f : 5f) /*5f*/, false, false, clrBlue); 513 514 fFormat = fOptions.PedigreeOptions.Format; 515 516 bool includeGens = fOptions.PedigreeOptions.IncludeGenerations; 517 518 fWriter.AddParagraph(fTitle, fTitleFont, TextAlignment.taCenter); 519 520 fPersonList = new ExtList<PedigreePerson>(true); 521 fSourceList = new StringList(); 522 try { 523 GenStep(null, fRoot, 1, 1); 524 ReIndex(); 525 526 int curLevel = 0; 527 int num = fPersonList.Count; 528 for (int i = 0; i < num; i++) { 529 PedigreePerson person = fPersonList[i]; 530 531 if (includeGens && curLevel != person.Level) { 532 curLevel = person.Level; 533 string genTitle = LangMan.LS(LSID.LSID_Generation) + " " + ConvertHelper.GetRome(curLevel); 534 535 fWriter.BeginParagraph(TextAlignment.taLeft, 12f, 6f); 536 fWriter.AddParagraphChunk(genTitle, fChapFont); 537 fWriter.EndParagraph(); 538 } 539 540 WritePerson(person); 541 } 542 543 if (fSourceList.Count > 0) { 544 fWriter.BeginParagraph(TextAlignment.taCenter, 12f, 6f); 545 fWriter.AddParagraphChunk(LangMan.LS(LSID.LSID_RPSources), fChapFont); 546 fWriter.EndParagraph(); 547 548 int num2 = fSourceList.Count; 549 for (int j = 0; j < num2; j++) { 550 string sn = (j + 1).ToString(); 551 string sst = sn + ". " + fSourceList[j]; 552 string sanc = "src_" + sn; 553 554 fWriter.AddParagraphAnchor(sst, fTextFont, sanc); 555 } 556 } 557 } finally { 558 fSourceList.Dispose(); 559 fPersonList.Dispose(); 560 } 561 } 562 } 563 } 564