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;
22 using System.Collections.Generic;
23 using System.Diagnostics;
24 using System.Globalization;
25 using System.IO;
26 using System.Net;
27 using System.Reflection;
28 using System.Runtime.CompilerServices;
29 using System.Text;
30 using System.Text.RegularExpressions;
31 using BSLib;
32 using GDModel;
33 using GDModel.Providers.GEDCOM;
34 using GKCore.Cultures;
35 using GKCore.Import;
36 using GKCore.Interfaces;
37 using GKCore.Options;
38 using GKCore.Types;
39 using Ude;
40 
41 namespace GKCore
42 {
43     /// <summary>
44     ///
45     /// </summary>
46     public static class GKUtils
47     {
48         #region Aux functions
49 
50         /// <summary>
51         /// Forced call of GEDCOMProvider static constructor.
52         /// This is important for a number of tests that require initialization of the GEDCOM tag table.
53         /// And at the start of the application, before loading any GEDCOM files.
54         /// </summary>
InitGEDCOM()55         public static void InitGEDCOM()
56         {
57             RuntimeHelpers.RunClassConstructor(typeof(GEDCOMProvider).TypeHandle);
58         }
59 
GetCountries()60         public static List<string> GetCountries()
61         {
62             var countries = new List<string>();
63             foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) {
64                 RegionInfo regionInfo = new RegionInfo(culture.LCID);
65                 string ctry = regionInfo.TwoLetterISORegionName; // DisplayName
66                 if (!countries.Contains(ctry))
67                     countries.Add(ctry);
68             }
69             countries.Sort();
70             return countries;
71         }
72 
LoadExtFile(string fileName, string args = R)73         public static void LoadExtFile(string fileName, string args = "")
74         {
75             #if !CI_MODE
76             if (File.Exists(fileName)) {
77                 Process.Start(new ProcessStartInfo("file://"+fileName) { UseShellExecute = true, Arguments = args });
78             } else {
79                 Process.Start(fileName);
80             }
81             #endif
82         }
83 
SexChar(GDMSex sex)84         public static string SexChar(GDMSex sex)
85         {
86             string ss = SexStr(sex);
87             return string.IsNullOrEmpty(ss) ? "?" : new string(ss[0], 1);
88         }
89 
SexStr(GDMSex sex)90         public static string SexStr(GDMSex sex)
91         {
92             return LangMan.LS(GKData.SexData[(int)sex].NameId);
93         }
94 
GetSexBySign(char sexSign)95         public static GDMSex GetSexBySign(char sexSign)
96         {
97             GDMSex result = GDMSex.svUnknown;
98             switch (sexSign) {
99                 case 'F':
100                     result = GDMSex.svFemale;
101                     break;
102                 case 'M':
103                     result = GDMSex.svMale;
104                     break;
105                 case 'U':
106                     result = GDMSex.svUnknown;
107                     break;
108                 case 'X':
109                     result = GDMSex.svIntersex;
110                     break;
111             }
112             return result;
113         }
114 
MergeStrings(GDMLines strings)115         public static string MergeStrings(GDMLines strings)
116         {
117             if (strings == null)
118                 throw new ArgumentNullException("strings");
119 
120             StringBuilder result = new StringBuilder();
121 
122             int num = strings.Count;
123             for (int i = 0; i < num; i++) {
124                 if (result.Length != 0) result.Append(" ");
125                 result.Append(strings[i].Trim());
126             }
127 
128             return result.ToString();
129         }
130 
TruncateStrings(GDMLines value, int size)131         public static string TruncateStrings(GDMLines value, int size)
132         {
133             string s = string.Empty;
134 
135             if (value != null && value.Count != 0) {
136                 if (size < value[0].Length) {
137                     s = value[0].Substring(0, size) + "...";
138                 } else {
139                     s = value[0];
140                 }
141             }
142 
143             return s;
144         }
145 
GetLocationLinks(GDMTree tree, GDMLocationRecord locRec)146         public static StringList GetLocationLinks(GDMTree tree, GDMLocationRecord locRec)
147         {
148             var linksList = new StringList();
149             if (locRec == null) return linksList;
150 
151             int num = tree.RecordsCount;
152             for (int i = 0; i < num; i++) {
153                 var evsRec = tree[i] as GDMRecordWithEvents;
154                 if (evsRec == null || !evsRec.HasEvents) continue;
155 
156                 int num2 = evsRec.Events.Count;
157                 for (int j = 0; j < num2; j++) {
158                     GDMCustomEvent evt = evsRec.Events[j];
159 
160                     if (evt.HasPlace && evt.Place.Location.XRef == locRec.XRef) {
161                         linksList.AddObject(GetRecordName(tree, evsRec, true) + ", " + GetEventName(evt).ToLower(), evsRec);
162                     }
163                 }
164             }
165 
166             return linksList;
167         }
168 
HyperLink(string xref, string text, int num)169         public static string HyperLink(string xref, string text, int num)
170         {
171             string result = "";
172 
173             if (!string.IsNullOrEmpty(xref) && string.IsNullOrEmpty(text)) {
174                 text = "???";
175             }
176 
177             if (!string.IsNullOrEmpty(xref) && !string.IsNullOrEmpty(text)) {
178                 result = "[url=" + xref + "]" + text + "[/url]";
179             }
180 
181             return result;
182         }
183 
184         // TODO: greedy function, move to BaseContext
GetRecordName(GDMTree tree, GDMRecord record, bool signed)185         public static string GetRecordName(GDMTree tree, GDMRecord record, bool signed)
186         {
187             string result = "";
188 
189             if (record != null) {
190                 string sign = "";
191 
192                 if (signed) {
193                     GDMRecordType recordType = record.RecordType;
194                     if (recordType != GDMRecordType.rtIndividual) {
195                         if (recordType == GDMRecordType.rtFamily || (byte)recordType - (byte)GDMRecordType.rtMultimedia < (byte)GDMRecordType.rtResearch)
196                         {
197                             sign = LangMan.LS(GKData.RecordTypes[(int)record.RecordType]) + ": ";
198                         }
199                     } else {
200                         sign = "";
201                     }
202                 }
203 
204                 string st;
205                 switch (record.RecordType) {
206                     case GDMRecordType.rtIndividual:
207                         st = GetNameString(((GDMIndividualRecord)record), true, false);
208                         break;
209                     case GDMRecordType.rtFamily:
210                         st = GetFamilyString(tree, (GDMFamilyRecord)record);
211                         break;
212                     case GDMRecordType.rtNote:
213                         st = ((GDMNoteRecord)record).Lines[0]; // TODO: bad solution?!
214                         break;
215                     case GDMRecordType.rtMultimedia:
216                         st = ((GDMMultimediaRecord)record).FileReferences[0].Title;
217                         break;
218                     case GDMRecordType.rtSource:
219                         st = ((GDMSourceRecord)record).ShortTitle;
220                         break;
221                     case GDMRecordType.rtRepository:
222                         st = ((GDMRepositoryRecord)record).RepositoryName;
223                         break;
224                     case GDMRecordType.rtGroup:
225                         st = ((GDMGroupRecord)record).GroupName;
226                         break;
227                     case GDMRecordType.rtResearch:
228                         st = ((GDMResearchRecord)record).ResearchName;
229                         break;
230                     case GDMRecordType.rtTask:
231                         st = GetTaskGoalStr(tree, (GDMTaskRecord)record);
232                         break;
233                     case GDMRecordType.rtCommunication:
234                         st = ((GDMCommunicationRecord)record).CommName;
235                         break;
236                     case GDMRecordType.rtLocation:
237                         st = ((GDMLocationRecord)record).LocationName;
238                         break;
239                     default:
240                         st = record.XRef;
241                         break;
242                 }
243 
244                 result = sign + st;
245             }
246 
247             return result;
248         }
249 
GenRecordLink(GDMTree tree, GDMRecord record, bool signed)250         public static string GenRecordLink(GDMTree tree, GDMRecord record, bool signed)
251         {
252             string result = "";
253 
254             if (record != null) {
255                 result = HyperLink(record.XRef, GetRecordName(tree, record, signed), 0);
256             }
257 
258             return result;
259         }
260 
GenRecordLinkTuple(GDMTree tree, GDMRecord record, bool signed)261         public static Tuple<string, string> GenRecordLinkTuple(GDMTree tree, GDMRecord record, bool signed)
262         {
263             if (record != null) {
264                 string recName = GetRecordName(tree, record, signed);
265                 string recLink = HyperLink(record.XRef, recName, 0);
266                 return new Tuple<string, string>(recName, recLink);
267             } else {
268                 return new Tuple<string, string>(string.Empty, string.Empty);
269             }
270         }
271 
GetCorresponderStr(GDMTree tree, GDMCommunicationRecord commRec, bool aLink)272         public static string GetCorresponderStr(GDMTree tree, GDMCommunicationRecord commRec, bool aLink)
273         {
274             if (tree == null)
275                 throw new ArgumentNullException("tree");
276 
277             if (commRec == null)
278                 throw new ArgumentNullException("commRec");
279 
280             string result = "";
281             var corr = tree.GetPtrValue(commRec.Corresponder);
282 
283             if (corr != null) {
284                 string nm = GetNameString(corr, true, false);
285                 if (aLink) {
286                     nm = HyperLink(corr.XRef, nm, 0);
287                 }
288                 result = "[ " + LangMan.LS(GKData.CommunicationDirs[(int)commRec.CommDirection]) + " ] " + nm;
289             }
290             return result;
291         }
292 
293         public sealed class TaskGoalRet
294         {
295             public readonly GDMGoalType GoalType;
296             public readonly string GoalXRef;
297             public readonly GDMRecord GoalRec;
298 
TaskGoalRet(GDMGoalType goalType, string goalXRef, GDMRecord goalRec)299             public TaskGoalRet(GDMGoalType goalType, string goalXRef, GDMRecord goalRec)
300             {
301                 GoalType = goalType;
302                 GoalXRef = goalXRef;
303                 GoalRec = goalRec;
304             }
305         }
306 
307         // TODO: greedy function, move to BaseContext
GetTaskGoal(GDMTree tree, GDMTaskRecord taskRec)308         public static TaskGoalRet GetTaskGoal(GDMTree tree, GDMTaskRecord taskRec)
309         {
310             string goalXRef = string.Empty;
311             GDMRecord goalRec = tree.XRefIndex_Find(GEDCOMUtils.CleanXRef(taskRec.Goal));
312 
313             GDMGoalType goalType;
314             if (goalRec == null) {
315                 goalType = GDMGoalType.gtOther;
316             } else {
317                 switch (goalRec.RecordType) {
318                     case GDMRecordType.rtIndividual:
319                         goalType = GDMGoalType.gtIndividual;
320                         break;
321                     case GDMRecordType.rtFamily:
322                         goalType = GDMGoalType.gtFamily;
323                         break;
324                     case GDMRecordType.rtSource:
325                         goalType = GDMGoalType.gtSource;
326                         break;
327                     default:
328                         goalType = GDMGoalType.gtOther;
329                         break;
330                 }
331             }
332 
333             return new TaskGoalRet(goalType, goalXRef, goalRec);
334         }
335 
GetTaskGoalStr(GDMTree tree, GDMTaskRecord taskRec)336         public static string GetTaskGoalStr(GDMTree tree, GDMTaskRecord taskRec)
337         {
338             if (tree == null || taskRec == null) return string.Empty;
339 
340             string result = "";
341 
342             var goal = GetTaskGoal(tree, taskRec);
343 
344             switch (goal.GoalType) {
345                 case GDMGoalType.gtIndividual:
346                 case GDMGoalType.gtFamily:
347                 case GDMGoalType.gtSource:
348                     result = GetGoalStr(tree, goal.GoalType, goal.GoalRec);
349                     break;
350 
351                 case GDMGoalType.gtOther:
352                     result = taskRec.Goal;
353                     break;
354             }
355 
356             if (goal.GoalType != GDMGoalType.gtOther) {
357                 result = "[" + LangMan.LS(GKData.GoalNames[(int)goal.GoalType]) + "] " + result;
358             }
359 
360             return result;
361         }
362 
GetGoalStr(GDMTree tree, GDMGoalType gt, GDMRecord tempRec)363         public static string GetGoalStr(GDMTree tree, GDMGoalType gt, GDMRecord tempRec)
364         {
365             if (tempRec == null) return string.Empty;
366 
367             switch (gt)
368             {
369                 case GDMGoalType.gtIndividual:
370                     return GetNameString(((GDMIndividualRecord)tempRec), true, false);
371 
372                 case GDMGoalType.gtFamily:
373                     return GetFamilyString(tree, tempRec as GDMFamilyRecord);
374 
375                 case GDMGoalType.gtSource:
376                     return ((GDMSourceRecord)tempRec).ShortTitle;
377             }
378 
379             return string.Empty;
380         }
381 
GetGroups(GDMTree tree)382         public static List<GDMGroupRecord> GetGroups(GDMTree tree)
383         {
384             var result = new List<GDMGroupRecord>();
385 
386             int num = tree.RecordsCount;
387             for (int i = 0; i < num; i++) {
388                 var rec = tree[i] as GDMGroupRecord;
389                 if (rec != null) {
390                     result.Add(rec);
391                 }
392             }
393             result.Sort((a, b) => -b.GroupName.CompareTo(a.GroupName));
394 
395             return result;
396         }
397 
GetSources(GDMTree tree)398         public static List<GDMSourceRecord> GetSources(GDMTree tree)
399         {
400             var result = new List<GDMSourceRecord>();
401 
402             for (int i = 0; i < tree.RecordsCount; i++) {
403                 var rec = tree[i] as GDMSourceRecord;
404                 if (rec != null) {
405                     result.Add(rec);
406                 }
407             }
408             result.Sort((a, b) => -b.ShortTitle.CompareTo(a.ShortTitle));
409 
410             return result;
411         }
412 
413         #endregion
414 
415         #region Encoding
416 
GetRectUID(int x1, int y1, int x2, int y2)417         public static string GetRectUID(int x1, int y1, int x2, int y2)
418         {
419             byte[] bx1 = BitConverter.GetBytes((ushort)x1);
420             byte[] by1 = BitConverter.GetBytes((ushort)y1);
421             byte[] bx2 = BitConverter.GetBytes((ushort)x2);
422             byte[] by2 = BitConverter.GetBytes((ushort)y2);
423 
424             byte[] buffer = new byte[8];
425             Buffer.BlockCopy(bx1, 0, buffer, 0, 2);
426             Buffer.BlockCopy(by1, 0, buffer, 2, 2);
427             Buffer.BlockCopy(bx2, 0, buffer, 4, 2);
428             Buffer.BlockCopy(by2, 0, buffer, 6, 2);
429 
430             return GEDCOMUtils.EncodeUID(buffer);
431         }
432 
DetectCharset(Stream stream, int bufferSize = 32768)433         public static CharsetResult DetectCharset(Stream stream, int bufferSize = 32768)
434         {
435             var result = new CharsetResult();
436 
437             ICharsetDetector cdet = new CharsetDetector();
438             byte[] buffer = new byte[bufferSize];
439             int read = stream.Read(buffer, 0, buffer.Length);
440             if (read > 0) {
441                 cdet.Feed(buffer, 0, read);
442                 cdet.DataEnd();
443                 stream.Seek(0, SeekOrigin.Begin);
444 
445                 result.Charset = cdet.Charset;
446                 result.Confidence = cdet.Confidence;
447             } else {
448                 result.Charset = null;
449                 result.Confidence = 0.0f;
450             }
451 
452             return result;
453         }
454 
GetDetectedStreamReader(Stream stream)455         public static StreamReader GetDetectedStreamReader(Stream stream)
456         {
457             // TODO: total search and fix references to Encoding.GetEncoding(1251)
458             // TODO: implement detection of encoding
459             StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(1251));
460             return reader;
461         }
462 
463         #endregion
464 
465         #region Match functions
466 
467         private static readonly char[] MaskDelimiters = { '*', '?', '|' };
468 
PrepareMask(string mask)469         public static string PrepareMask(string mask)
470         {
471             string regexStr = "";
472 
473             if (!string.IsNullOrEmpty(mask)) {
474                 // double star evokes monstrous lags
475                 mask = mask.Replace("**", "*").Replace("??", "?");
476 
477                 int curPos = 0;
478                 int len = mask.Length;
479 
480                 while (curPos < len) {
481                     int I = mask.IndexOfAny(MaskDelimiters, curPos);
482                     if (I < curPos) break;
483                     if (I > curPos) {
484                         string part = mask.Substring(curPos, I - curPos);
485                         regexStr += Regex.Escape(part);
486                     }
487 
488                     switch (mask[I]) {
489                         case '*':
490                             regexStr += ".*";
491                             break;
492                         case '?':
493                             regexStr += ".";
494                             break;
495                         case '|':
496                             regexStr += "|";
497                             break;
498                     }
499 
500                     curPos = I + 1;
501                 }
502 
503                 if (curPos < len) {
504                     string part = mask.Substring(curPos, len - curPos);
505                     regexStr += Regex.Escape(part);
506                 }
507             }
508 
509             return regexStr;
510         }
511 
512         public const RegexOptions RegexOpts = RegexOptions.Compiled | RegexOptions.IgnoreCase;
513 
InitMaskRegex(string mask)514         public static Regex InitMaskRegex(string mask)
515         {
516             Regex result = null;
517 
518             string regexStr = PrepareMask(mask);
519             if (!string.IsNullOrEmpty(regexStr)) {
520                 result = new Regex(regexStr, RegexOpts);
521             }
522 
523             return result;
524         }
525 
MatchesRegex(string str, Regex regex)526         public static bool MatchesRegex(string str, Regex regex)
527         {
528             return (regex != null) && regex.IsMatch(str, 0);
529         }
530 
MatchesMask(string str, string mask)531         public static bool MatchesMask(string str, string mask)
532         {
533             if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(mask)) {
534                 return false;
535             }
536 
537             if (mask == "*") {
538                 return true;
539             }
540 
541             // Regex.IsMatch() has caching
542             return Regex.IsMatch(str, PrepareMask(mask), RegexOpts);
543         }
544 
545         #endregion
546 
547         #region Event Utils
548 
GetAttributeValue(GDMIndividualRecord iRec, string attrName)549         public static string GetAttributeValue(GDMIndividualRecord iRec, string attrName)
550         {
551             if (iRec == null) return string.Empty;
552 
553             GDMCustomEvent attr = iRec.FindEvent(attrName);
554             string result = ((attr == null) ? "" : attr.StringValue);
555             return result;
556         }
557 
GetPersonEventKindBySign(string sign)558         public static PersonEventKind GetPersonEventKindBySign(string sign)
559         {
560             int idx = GetPersonEventIndex(sign);
561             return (idx < 0) ? PersonEventKind.ekFact : GKData.PersonEvents[idx].Kind;
562         }
563 
GetPersonEventIndex(string sign)564         public static int GetPersonEventIndex(string sign)
565         {
566             int res = -1;
567 
568             for (int i = 0; i < GKData.PersonEvents.Length; i++)
569             {
570                 if (GKData.PersonEvents[i].Sign == sign)
571                 {
572                     res = i;
573                     break;
574                 }
575             }
576 
577             return res;
578         }
579 
GetFamilyEventIndex(string sign)580         public static int GetFamilyEventIndex(string sign)
581         {
582             int res = -1;
583 
584             for (int i = 0; i < GKData.FamilyEvents.Length; i++)
585             {
586                 if (GKData.FamilyEvents[i].Sign == sign)
587                 {
588                     res = i;
589                     break;
590                 }
591             }
592 
593             return res;
594         }
595 
GetEventName(GDMCustomEvent evt)596         public static string GetEventName(GDMCustomEvent evt)
597         {
598             if (evt == null)
599                 throw new ArgumentNullException("evt");
600 
601             string result = "";
602 
603             var evtName = evt.GetTagName();
604             if (evt is GDMIndividualEvent || evt is GDMIndividualAttribute) {
605                 int ev = GetPersonEventIndex(evtName);
606                 if (ev == 0) {
607                     result = !string.IsNullOrEmpty(evt.Classification) ? evt.Classification : LangMan.LS(GKData.PersonEvents[ev].Name);
608                 } else {
609                     result = (ev > 0) ? LangMan.LS(GKData.PersonEvents[ev].Name) : evtName;
610                 }
611             } else if (evt is GDMFamilyEvent) {
612                 int ev = GetFamilyEventIndex(evtName);
613                 if (ev == 0) {
614                     result = evt.Classification;
615                 } else {
616                     result = (ev > 0) ? LangMan.LS(GKData.FamilyEvents[ev].Name) : evtName;
617                 }
618             }
619 
620             return result;
621         }
622 
GetAttributeStr(GDMIndividualAttribute iAttr)623         public static string GetAttributeStr(GDMIndividualAttribute iAttr)
624         {
625             if (iAttr == null)
626                 throw new ArgumentNullException("iAttr");
627 
628             var attrName = iAttr.GetTagName();
629             int idx = GetPersonEventIndex(attrName);
630             string st;
631             if (idx == 0) {
632                 st = iAttr.Classification;
633             } else {
634                 st = (idx > 0) ? LangMan.LS(GKData.PersonEvents[idx].Name) : attrName;
635             }
636 
637             string place = string.Empty;
638             if (iAttr.HasPlace) {
639                 place = iAttr.Place.StringValue;
640                 if (place != "") {
641                     place = " [" + place + "]";
642                 }
643             }
644             return st + ": " + iAttr.StringValue + place;
645         }
646 
GetEventDesc(GDMTree tree, GDMCustomEvent evt, bool hyperLink = true)647         public static string GetEventDesc(GDMTree tree, GDMCustomEvent evt, bool hyperLink = true)
648         {
649             if (evt == null)
650                 throw new ArgumentNullException("evt");
651 
652             string dt = GEDCOMEventToDateStr(evt, GlobalOptions.Instance.DefDateFormat, false);
653 
654             string place = string.Empty;
655             if (evt.HasPlace) {
656                 place = evt.Place.StringValue;
657                 GDMLocationRecord location = tree.GetPtrValue<GDMLocationRecord>(evt.Place.Location);
658 
659                 if (place != "" && location != null && hyperLink) {
660                     place = HyperLink(location.XRef, place, 0);
661                 }
662             }
663 
664             string result;
665 
666             if (dt == "" && place == "") {
667                 result = "?";
668             } else {
669                 if (dt == "") {
670                     result = place;
671                 } else {
672                     if (place == "") {
673                         result = dt;
674                     } else {
675                         result = dt + ", " + place;
676                     }
677                 }
678             }
679 
680             return result;
681         }
682 
GetEventCause(GDMCustomEvent evt)683         public static string GetEventCause(GDMCustomEvent evt)
684         {
685             if (evt == null)
686                 throw new ArgumentNullException("evt");
687 
688             string result = evt.Cause;
689 
690             if (!string.IsNullOrEmpty(evt.Agency)) {
691                 if (result != "") {
692                     result += " ";
693                 }
694                 result = result + "[" + evt.Agency + "]";
695             }
696 
697             return result;
698         }
699 
700         #endregion
701 
702         #region Date functions
703 
704         /// <summary>
705         /// The result of the function is a "normalized date", delimited by '.' and fixed order of parts: "dd.mm.yyyy".
706         /// The pattern and regional date contain the delimiter '/'.
707         /// The pattern defines the position of the parts in a regional date format.
708         /// </summary>
709         /// <param name="regionalDate">date similar "01/20/1970"</param>
710         /// <param name="pattern">pattern similar "mm/dd/yyyy"</param>
711         /// <returns>normalized date as "dd.mm.yyyy"</returns>
GetNormalizeDate(string regionalDate, string pattern)712         public static string GetNormalizeDate(string regionalDate, string pattern)
713         {
714             if (string.IsNullOrEmpty(regionalDate)) return string.Empty;
715 
716             string[] regionalParts = regionalDate.Split('/');
717             string[] patternParts = pattern.Split('/');
718             string[] resultParts = new string[3];
719 
720             for (int i = 0; i < patternParts.Length; i++) {
721                 string part = patternParts[i];
722                 switch (part[0]) {
723                     case 'd':
724                         resultParts[0] = regionalParts[i];
725                         break;
726 
727                     case 'm':
728                         resultParts[1] = regionalParts[i];
729                         break;
730 
731                     case 'y':
732                         resultParts[2] = regionalParts[i];
733                         break;
734                 }
735             }
736 
737             string result = string.Join(".", resultParts);
738             return result;
739         }
740 
741         /// <summary>
742         /// The result of the function is a "regional date", delimited by '/' and regional order of parts: "mm/dd/yyyy"
743         /// or any other. The pattern and regional date contain the delimiter '/'.
744         /// The pattern defines the position of the parts in a regional date format.
745         /// </summary>
746         /// <param name="normalizeDate">date with format "dd.mm.yyyy"</param>
747         /// <param name="pattern">pattern similar "mm/dd/yyyy"</param>
748         /// <returns>regional date as "mm/dd/yyyy"</returns>
GetRegionalDate(string normalizeDate, string pattern)749         public static string GetRegionalDate(string normalizeDate, string pattern)
750         {
751             if (string.IsNullOrEmpty(normalizeDate)) return string.Empty;
752 
753             string[] normalizeParts = normalizeDate.Split('.');
754             string[] patternParts = pattern.Split('/');
755             string[] resultParts = new string[3];
756 
757             for (int i = 0; i < patternParts.Length; i++) {
758                 string part = patternParts[i];
759                 switch (part[0]) {
760                     case 'd':
761                         resultParts[i] = normalizeParts[0];
762                         break;
763 
764                     case 'm':
765                         resultParts[i] = normalizeParts[1];
766                         break;
767 
768                     case 'y':
769                         resultParts[i] = normalizeParts[2];
770                         break;
771                 }
772             }
773 
774             string result = string.Join("/", resultParts);
775             return result;
776         }
777 
GEDCOMEventToDateStr(GDMCustomEvent evt, DateFormat format, bool sign)778         public static string GEDCOMEventToDateStr(GDMCustomEvent evt, DateFormat format, bool sign)
779         {
780             return (evt == null) ? string.Empty : evt.Date.GetDisplayStringExt(format, sign, false);
781         }
782 
CompactDate(string date)783         public static string CompactDate(string date)
784         {
785             string result = date;
786             while (result.IndexOf("__.") == 0) result = result.Remove(0, 3);
787             return result;
788         }
789 
GetBirthDate(GDMIndividualRecord iRec)790         public static GDMCustomDate GetBirthDate(GDMIndividualRecord iRec)
791         {
792             if (iRec == null) return null;
793 
794             GDMCustomEvent evt = iRec.FindEvent(GEDCOMTagType.BIRT);
795             GDMCustomDate result = ((evt == null) ? null : evt.Date.Value);
796             return result;
797         }
798 
GetBirthDate(GDMIndividualRecord iRec, DateFormat dateFormat, bool compact)799         public static string GetBirthDate(GDMIndividualRecord iRec, DateFormat dateFormat, bool compact)
800         {
801             if (iRec == null) return string.Empty;
802 
803             GDMCustomEvent evt = iRec.FindEvent(GEDCOMTagType.BIRT);
804             string result = ((evt == null) ? "" : GEDCOMEventToDateStr(evt, dateFormat, false));
805             if (compact) result = CompactDate(result);
806             return result;
807         }
808 
GetDeathDate(GDMIndividualRecord iRec, DateFormat dateFormat, bool compact)809         public static string GetDeathDate(GDMIndividualRecord iRec, DateFormat dateFormat, bool compact)
810         {
811             if (iRec == null) return string.Empty;
812 
813             GDMCustomEvent evt = iRec.FindEvent(GEDCOMTagType.DEAT);
814             string result = ((evt == null) ? "" : GEDCOMEventToDateStr(evt, dateFormat, false));
815             if (compact) result = CompactDate(result);
816             return result;
817         }
818 
GetLifeStr(GDMIndividualRecord iRec)819         public static string GetLifeStr(GDMIndividualRecord iRec)
820         {
821             if (iRec == null) return string.Empty;
822 
823             string result = " (";
824 
825             string ds = GetBirthDate(iRec, GlobalOptions.Instance.DefDateFormat, false);
826             if (ds == "")
827             {
828                 ds = "?";
829             }
830             result += ds;
831 
832             ds = GetDeathDate(iRec, GlobalOptions.Instance.DefDateFormat, false);
833             if (ds == "")
834             {
835                 GDMCustomEvent ev = iRec.FindEvent(GEDCOMTagType.DEAT);
836                 if (ev != null)
837                 {
838                     ds = "?";
839                 }
840             }
841 
842             if (ds != "")
843             {
844                 result = result + " - " + ds;
845             }
846 
847             result += ")";
848             return result;
849         }
850 
GetEventDatePlace(GDMIndividualRecord iRec, string eventName, DateFormat dateFormat, bool compact, bool markUnkDate, out string dateStr, out string placeStr)851         public static void GetEventDatePlace(GDMIndividualRecord iRec, string eventName, DateFormat dateFormat,
852                                              bool compact, bool markUnkDate, out string dateStr, out string placeStr)
853         {
854             dateStr = string.Empty;
855             placeStr = string.Empty;
856 
857             if (iRec != null) {
858                 GDMCustomEvent evt = iRec.FindEvent(eventName);
859                 if (evt != null) {
860                     dateStr = GEDCOMEventToDateStr(evt, dateFormat, false);
861                     if (dateStr != "") {
862                         if (compact) dateStr = CompactDate(dateStr);
863                     }
864                     if (dateStr == "" && markUnkDate) {
865                         dateStr = "?";
866                     }
867 
868                     placeStr = GetPlaceStr(evt, false);
869                 }
870             }
871         }
872 
GetPedigreeLifeStr(GDMIndividualRecord iRec, PedigreeFormat fmt)873         public static string GetPedigreeLifeStr(GDMIndividualRecord iRec, PedigreeFormat fmt)
874         {
875             if (iRec == null)
876                 throw new ArgumentNullException("iRec");
877 
878             string result = "";
879 
880             switch (fmt) {
881                 case PedigreeFormat.Excess:
882                     {
883                         string ds = GetBirthDate(iRec, GlobalOptions.Instance.DefDateFormat, true);
884                         if (ds == "")
885                         {
886                             ds = "?";
887                         }
888                         result += ds;
889                         ds = GetDeathDate(iRec, GlobalOptions.Instance.DefDateFormat, true);
890                         if (ds == "")
891                         {
892                             GDMCustomEvent ev = iRec.FindEvent(GEDCOMTagType.DEAT);
893                             if (ev != null)
894                             {
895                                 ds = "?";
896                             }
897                         }
898                         if (ds != "")
899                         {
900                             result = result + " - " + ds;
901                         }
902                     }
903                     break;
904 
905                 case PedigreeFormat.Compact:
906                     {
907                         string ds, ps;
908 
909                         GetEventDatePlace(iRec, GEDCOMTagName.BIRT, GlobalOptions.Instance.DefDateFormat, true, true, out ds, out ps);
910                         if (ps != "") {
911                             if (ds != "") {
912                                 ds += ", ";
913                             }
914                             ds += ps;
915                         }
916                         if (ds != "") {
917                             ds = ImportUtils.STD_BIRTH_SIGN + ds;
918                         }
919                         result += ds;
920 
921                         GetEventDatePlace(iRec, GEDCOMTagName.DEAT, GlobalOptions.Instance.DefDateFormat, true, true, out ds, out ps);
922                         if (ps != "") {
923                             if (ds != "") {
924                                 ds += ", ";
925                             }
926                             ds += ps;
927                         }
928                         if (ds != "") {
929                             ds = ImportUtils.STD_DEATH_SIGN + ds;
930                             result = result + " " + ds;
931                         }
932                     }
933                     break;
934             }
935 
936             result = result.Trim();
937             result = (result == "") ? "" : " (" + result + ")";
938             return result;
939         }
940 
GetChronologicalYear(GDMCustomEvent evt)941         public static int GetChronologicalYear(GDMCustomEvent evt)
942         {
943             return (evt == null) ? 0 : evt.Date.GetChronologicalYear();
944         }
945 
GetEventsYearsDiff(GDMCustomEvent ev1, GDMCustomEvent ev2, bool currentEnd)946         public static int GetEventsYearsDiff(GDMCustomEvent ev1, GDMCustomEvent ev2, bool currentEnd)
947         {
948             int result = -1;
949 
950             try {
951                 int dt1 = GetChronologicalYear(ev1);
952                 int dt2 = GetChronologicalYear(ev2);
953 
954                 if (currentEnd && dt2 == 0) {
955                     dt2 = DateTime.Now.Year;
956                 }
957 
958                 if (dt1 != 0 && dt2 != 0) {
959                     result = Math.Abs(dt2 - dt1);
960                 }
961             } catch (Exception ex) {
962                 Logger.WriteError("GKUtils.GetEventsYearsDiff()", ex);
963             }
964 
965             return result;
966         }
967 
GetLifeExpectancyStr(GDMIndividualRecord iRec)968         public static string GetLifeExpectancyStr(GDMIndividualRecord iRec)
969         {
970             int result = GetLifeExpectancy(iRec);
971             return (result == -1) ? "" : result.ToString();
972         }
973 
GetLifeExpectancy(GDMIndividualRecord iRec)974         public static int GetLifeExpectancy(GDMIndividualRecord iRec)
975         {
976             int result = -1;
977             if (iRec == null) return result;
978 
979             try {
980                 var lifeDates = iRec.GetLifeDates();
981                 result = GetEventsYearsDiff(lifeDates.BirthEvent, lifeDates.DeathEvent, false);
982             } catch (Exception ex) {
983                 Logger.WriteError("GKUtils.GetLifeExpectancy()", ex);
984             }
985 
986             return result;
987         }
988 
GetAgeStr(GDMIndividualRecord iRec, int toYear)989         public static string GetAgeStr(GDMIndividualRecord iRec, int toYear)
990         {
991             int result = GetAge(iRec, toYear);
992             return (result == -1) ? "" : result.ToString();
993         }
994 
GetAge(GDMIndividualRecord iRec, int toYear)995         public static int GetAge(GDMIndividualRecord iRec, int toYear)
996         {
997             int result = -1;
998             if (iRec == null) return result;
999 
1000             try {
1001                 var lifeDates = iRec.GetLifeDates();
1002 
1003                 if (toYear == -1) {
1004                     result = GetEventsYearsDiff(lifeDates.BirthEvent, lifeDates.DeathEvent, lifeDates.DeathEvent == null);
1005                 } else {
1006                     int birthYear = GetChronologicalYear(lifeDates.BirthEvent);
1007                     if (birthYear != 0) {
1008                         result = toYear - birthYear;
1009                     }
1010                 }
1011             } catch (Exception ex) {
1012                 Logger.WriteError("GKUtils.GetAge()", ex);
1013             }
1014 
1015             return result;
1016         }
1017 
GetMarriageDate(GDMFamilyRecord fRec)1018         public static GDMCustomDate GetMarriageDate(GDMFamilyRecord fRec)
1019         {
1020             if (fRec == null) {
1021                 return null;
1022             }
1023 
1024             GDMCustomEvent evt = fRec.FindEvent(GEDCOMTagType.MARR);
1025             return ((evt == null) ? null : evt.Date.Value);
1026         }
1027 
GetMarriageDateStr(GDMFamilyRecord fRec, DateFormat dateFormat, bool sign)1028         public static string GetMarriageDateStr(GDMFamilyRecord fRec, DateFormat dateFormat, bool sign)
1029         {
1030             GDMCustomDate date = GetMarriageDate(fRec);
1031             return (date == null) ? string.Empty : date.GetDisplayStringExt(dateFormat, sign, false);
1032         }
GetMarriageDateStr(GDMFamilyRecord fRec, DateFormat dateFormat)1033         public static string GetMarriageDateStr(GDMFamilyRecord fRec, DateFormat dateFormat)
1034         {
1035             GDMCustomDate date = GetMarriageDate(fRec);
1036             return (date == null) ? string.Empty : date.GetDisplayStringExt(dateFormat, false, false);
1037         }
1038 
1039         /// <summary>
1040         /// Get number of days remained until the birthday of the specified
1041         /// person.
1042         /// </summary>
1043         /// <param name="iRec">A person record to check.</param>
1044         /// <returns>Number of days remained until the birthday of
1045         /// the <paramref name="iRec" />. The caller must ignore this value if
1046         /// the method returns -1.</returns>
GetDaysForBirth(GDMIndividualRecord iRec)1047         public static int GetDaysForBirth(GDMIndividualRecord iRec)
1048         {
1049             int distance = -1;
1050 
1051             if (iRec != null) {
1052                 int bdD, bdM, bdY;
1053 
1054                 try {
1055                     GDMCustomEvent evt = iRec.FindEvent(GEDCOMTagType.DEAT);
1056                     if (evt == null) {
1057                         evt = iRec.FindEvent(GEDCOMTagType.BIRT);
1058                         if (evt != null) {
1059                             var dt = evt.Date.Value as GDMDate;
1060                             if (dt != null) {
1061                                 bdM = dt.Month;
1062                                 bdD = dt.Day;
1063 
1064                                 if (bdM != 0 && bdD != 0) {
1065                                     DateTime dtNow = DateTime.Now.Date;
1066                                     int curY = dtNow.Year;
1067                                     int curM = dtNow.Month;
1068                                     int curD = dtNow.Day;
1069                                     double dt2 = curY + bdM / 12.0 + bdD / 12.0 / 31.0;
1070                                     double dt3 = curY + curM / 12.0 + curD / 12.0 / 31.0;
1071                                     bdY = (dt2 < dt3) ? (curY + 1) : curY;
1072 
1073                                     // There are valid birthdays on February 29th in leap years.
1074                                     // For other years, we need a correction for an acceptable day.
1075                                     if (bdD == 29 && bdM == 2 && !DateTime.IsLeapYear(bdY)) {
1076                                         bdD -= 1;
1077                                     }
1078 
1079                                     distance = DateHelper.DaysBetween(dtNow, new DateTime(bdY, bdM, bdD));
1080                                 }
1081                             }
1082                         }
1083                     }
1084                 } catch (Exception ex) {
1085                     Logger.WriteError("GKUtils.GetDaysForBirth()", ex);
1086                 }
1087             }
1088 
1089             return distance;
1090         }
1091 
1092         #endregion
1093 
1094         #region Places functions
1095 
GetBirthPlace(GDMIndividualRecord iRec)1096         public static string GetBirthPlace(GDMIndividualRecord iRec)
1097         {
1098             return (iRec == null) ? string.Empty : GetPlaceStr(iRec.FindEvent(GEDCOMTagType.BIRT), false);
1099         }
1100 
GetDeathPlace(GDMIndividualRecord iRec)1101         public static string GetDeathPlace(GDMIndividualRecord iRec)
1102         {
1103             return (iRec == null) ? string.Empty : GetPlaceStr(iRec.FindEvent(GEDCOMTagType.DEAT), false);
1104         }
1105 
GetResidencePlace(GDMIndividualRecord iRec, bool includeAddress)1106         public static string GetResidencePlace(GDMIndividualRecord iRec, bool includeAddress)
1107         {
1108             return (iRec == null) ? string.Empty : GetPlaceStr(iRec.FindEvent(GEDCOMTagType.RESI), includeAddress);
1109         }
1110 
GetPlaceStr(GDMCustomEvent evt, bool includeAddress)1111         public static string GetPlaceStr(GDMCustomEvent evt, bool includeAddress)
1112         {
1113             if (evt == null || !evt.HasPlace) return string.Empty;
1114 
1115             string result = evt.Place.StringValue;
1116 
1117             if (includeAddress) {
1118                 string resi = evt.StringValue;
1119 
1120                 if (evt.HasAddress) {
1121                     string addrText = evt.Address.Lines.Text.Trim();
1122                     if (resi != "" && addrText != "") {
1123                         resi += ", ";
1124                     }
1125                     resi += addrText;
1126                 }
1127 
1128                 if (resi != "") {
1129                     result = result + " [" + resi + "]";
1130                 }
1131             }
1132 
1133             return result;
1134         }
1135 
1136         #endregion
1137 
1138         #region Individual functions
1139 
GetAncestorsCount(GDMTree tree, GDMIndividualRecord iRec)1140         public static int GetAncestorsCount(GDMTree tree, GDMIndividualRecord iRec)
1141         {
1142             var indiCounters = new GKVarCache<GDMIndividualRecord, int>(-1);
1143             return GetAncestorsCount(tree, iRec, indiCounters);
1144         }
1145 
GetAncestorsCount(GDMTree tree, GDMIndividualRecord iRec, GKVarCache<GDMIndividualRecord, int> counters)1146         private static int GetAncestorsCount(GDMTree tree, GDMIndividualRecord iRec, GKVarCache<GDMIndividualRecord, int> counters)
1147         {
1148             int result = 0;
1149 
1150             if (iRec != null) {
1151                 int val = counters[iRec];
1152 
1153                 if (val < 0) {
1154                     val = 1;
1155 
1156                     GDMFamilyRecord family = tree.GetParentsFamily(iRec);
1157                     if (family != null) {
1158                         GDMIndividualRecord anc;
1159 
1160                         anc = tree.GetPtrValue(family.Husband);
1161                         val += GetAncestorsCount(tree, anc, counters);
1162 
1163                         anc = tree.GetPtrValue(family.Wife);
1164                         val += GetAncestorsCount(tree, anc, counters);
1165                     }
1166 
1167                     counters[iRec] = val;
1168                 }
1169 
1170                 result = val;
1171             }
1172 
1173             return result;
1174         }
1175 
GetDescendantsCount(GDMTree tree, GDMIndividualRecord iRec)1176         public static int GetDescendantsCount(GDMTree tree, GDMIndividualRecord iRec)
1177         {
1178             var indiCounters = new GKVarCache<GDMIndividualRecord, int>(-1);
1179             return GetDescendantsCount(tree, iRec, indiCounters);
1180         }
1181 
GetDescendantsCount(GDMTree tree, GDMIndividualRecord iRec, GKVarCache<GDMIndividualRecord, int> counters)1182         private static int GetDescendantsCount(GDMTree tree, GDMIndividualRecord iRec, GKVarCache<GDMIndividualRecord, int> counters)
1183         {
1184             int result = 0;
1185 
1186             if (iRec != null) {
1187                 int val = counters[iRec];
1188                 if (val < 0) {
1189                     val = 1;
1190 
1191                     int num = iRec.SpouseToFamilyLinks.Count;
1192                     for (int i = 0; i < num; i++) {
1193                         GDMFamilyRecord family = tree.GetPtrValue(iRec.SpouseToFamilyLinks[i]);
1194 
1195                         int num2 = family.Children.Count;
1196                         for (int j = 0; j < num2; j++) {
1197                             GDMIndividualRecord child = tree.GetPtrValue(family.Children[j]);
1198                             val += GetDescendantsCount(tree, child, counters);
1199                         }
1200                     }
1201                     counters[iRec] = val;
1202                 }
1203                 result = val;
1204             }
1205 
1206             return result;
1207         }
1208 
GetDescGens_Recursive(GDMTree tree, GDMIndividualRecord iRec)1209         private static int GetDescGens_Recursive(GDMTree tree, GDMIndividualRecord iRec)
1210         {
1211             int result = 0;
1212 
1213             if (iRec != null) {
1214                 int max = 0;
1215 
1216                 int num = iRec.SpouseToFamilyLinks.Count;
1217                 for (int i = 0; i < num; i++) {
1218                     GDMFamilyRecord family = tree.GetPtrValue(iRec.SpouseToFamilyLinks[i]);
1219 
1220                     int num2 = family.Children.Count;
1221                     for (int j = 0; j < num2; j++) {
1222                         GDMIndividualRecord child = tree.GetPtrValue(family.Children[j]);
1223                         int res = GetDescGens_Recursive(tree, child);
1224                         if (max < res) {
1225                             max = res;
1226                         }
1227                     }
1228                 }
1229                 result = 1 + max;
1230             }
1231 
1232             return result;
1233         }
1234 
GetDescGenerations(GDMTree tree, GDMIndividualRecord iRec)1235         public static int GetDescGenerations(GDMTree tree, GDMIndividualRecord iRec)
1236         {
1237             return GetDescGens_Recursive(tree, iRec) - 1;
1238         }
1239 
GetMarriagesCount(GDMIndividualRecord iRec)1240         public static int GetMarriagesCount(GDMIndividualRecord iRec)
1241         {
1242             int result = ((iRec == null) ? 0 : iRec.SpouseToFamilyLinks.Count);
1243             return result;
1244         }
1245 
GetSpousesDiff(GDMTree tree, GDMFamilyRecord fRec)1246         public static int GetSpousesDiff(GDMTree tree, GDMFamilyRecord fRec)
1247         {
1248             int result = -1;
1249 
1250             try {
1251                 if (fRec != null) {
1252                     GDMIndividualRecord husb = tree.GetPtrValue(fRec.Husband);
1253                     GDMIndividualRecord wife = tree.GetPtrValue(fRec.Wife);
1254 
1255                     if (husb != null && wife != null) {
1256                         GDMCustomEvent evH = husb.FindEvent(GEDCOMTagType.BIRT);
1257                         GDMCustomEvent evW = wife.FindEvent(GEDCOMTagType.BIRT);
1258 
1259                         result = GetEventsYearsDiff(evH, evW, false);
1260                     }
1261                 }
1262             } catch (Exception ex) {
1263                 Logger.WriteError("GKUtils.GetSpousesDiff()", ex);
1264             }
1265 
1266             return result;
1267         }
1268 
GetFirstborn(GDMTree tree, GDMIndividualRecord iRec)1269         public static GDMIndividualRecord GetFirstborn(GDMTree tree, GDMIndividualRecord iRec)
1270         {
1271             GDMIndividualRecord iChild = null;
1272             if (iRec == null) return iChild;
1273 
1274             try {
1275                 int firstYear = 0;
1276 
1277                 int num = iRec.SpouseToFamilyLinks.Count;
1278                 for (int i = 0; i < num; i++) {
1279                     GDMFamilyRecord family = tree.GetPtrValue(iRec.SpouseToFamilyLinks[i]);
1280                     if (family == null) continue;
1281 
1282                     int num2 = family.Children.Count;
1283                     for (int j = 0; j < num2; j++) {
1284                         GDMIndividualRecord child = tree.GetPtrValue(family.Children[j]);
1285                         if (child == null) continue;
1286 
1287                         GDMCustomEvent evt = child.FindEvent(GEDCOMTagType.BIRT);
1288                         if (evt == null) continue;
1289 
1290                         int childYear = evt.GetChronologicalYear();
1291 
1292                         if (firstYear == 0) {
1293                             firstYear = childYear;
1294                             iChild = child;
1295                         } else {
1296                             if (firstYear > childYear) {
1297                                 firstYear = childYear;
1298                                 iChild = child;
1299                             }
1300                         }
1301                     }
1302                 }
1303             } catch (Exception ex) {
1304                 Logger.WriteError("GKUtils.GetFirstborn()", ex);
1305             }
1306             return iChild;
1307         }
1308 
GetFirstbornAge(GDMIndividualRecord iRec, GDMIndividualRecord iChild)1309         public static int GetFirstbornAge(GDMIndividualRecord iRec, GDMIndividualRecord iChild)
1310         {
1311             int result = 0;
1312             if (iRec == null || iChild == null) return result;
1313 
1314             try {
1315                 GDMCustomEvent evt = iRec.FindEvent(GEDCOMTagType.BIRT);
1316                 if (evt == null) return result;
1317                 int parentYear = evt.GetChronologicalYear();
1318 
1319                 evt = iChild.FindEvent(GEDCOMTagType.BIRT);
1320                 if (evt == null) return result;
1321                 int childYear = evt.GetChronologicalYear();
1322 
1323                 if (parentYear != 0 && childYear != 0) {
1324                     result = (childYear - parentYear);
1325                 }
1326             } catch (Exception ex) {
1327                 Logger.WriteError("GKUtils.GetFirstbornAge()", ex);
1328             }
1329             return result;
1330         }
1331 
GetMarriageAge(GDMTree tree, GDMIndividualRecord iRec)1332         public static int GetMarriageAge(GDMTree tree, GDMIndividualRecord iRec)
1333         {
1334             int result = 0;
1335             if (iRec == null) return result;
1336 
1337             try {
1338                 int firstYear = 0;
1339 
1340                 GDMCustomEvent evt = iRec.FindEvent(GEDCOMTagType.BIRT);
1341                 if (evt != null) {
1342                     int mainYear = evt.GetChronologicalYear();
1343 
1344                     int num = iRec.SpouseToFamilyLinks.Count;
1345                     for (int i = 0; i < num; i++) {
1346                         GDMFamilyRecord family = tree.GetPtrValue(iRec.SpouseToFamilyLinks[i]);
1347                         if (family == null) continue;
1348 
1349                         GDMCustomEvent marrEvt = family.FindEvent(GEDCOMTagType.MARR);
1350                         if (marrEvt == null) continue;
1351 
1352                         int spouseYear = marrEvt.GetChronologicalYear();
1353 
1354                         if (firstYear == 0) {
1355                             firstYear = spouseYear;
1356                         } else {
1357                             if (firstYear > spouseYear) {
1358                                 firstYear = spouseYear;
1359                             }
1360                         }
1361                     }
1362 
1363                     if (mainYear != 0 && firstYear != 0) {
1364                         result = (firstYear - mainYear);
1365                     }
1366                 }
1367             } catch (Exception ex) {
1368                 Logger.WriteError("GKUtils.GetMarriageAge()", ex);
1369             }
1370             return result;
1371         }
1372 
1373         #endregion
1374 
1375         #region Tree utils
1376 
PrepareHeader(GDMTree tree, string fileName, GEDCOMCharacterSet charSet, bool zeroRev)1377         public static void PrepareHeader(GDMTree tree, string fileName, GEDCOMCharacterSet charSet, bool zeroRev)
1378         {
1379             GDMHeader header = tree.Header;
1380 
1381             string subm = header.Submitter.XRef;
1382             int oldRev = header.File.Revision;
1383             GDMLanguageID langId = header.Language;
1384 
1385             header.Clear();
1386             header.Source.StringValue = "GEDKeeper";
1387             header.ReceivingSystemName = "GEDKeeper";
1388             header.CharacterSet.Value = charSet;
1389             header.Language = langId;
1390             header.GEDCOM.Version = "5.5.1";
1391             header.GEDCOM.Form = "LINEAGE-LINKED";
1392             header.File.StringValue = Path.GetFileName(fileName);
1393             header.TransmissionDateTime = DateTime.Now;
1394 
1395             header.Source.Version = GKData.APP_FORMAT_CURVER.ToString();
1396 
1397             if (zeroRev) {
1398                 header.File.Revision = 0;
1399             } else {
1400                 header.File.Revision = oldRev + 1;
1401             }
1402 
1403             if (!string.IsNullOrEmpty(subm)) {
1404                 header.Submitter.XRef = subm;
1405             }
1406         }
1407 
1408         #endregion
1409 
1410         #region Folder functions
1411 
GetTempDir()1412         public static string GetTempDir()
1413         {
1414             string tempPath = Environment.GetEnvironmentVariable("TEMP");
1415             return tempPath + Path.DirectorySeparatorChar;
1416         }
1417 
GetBinPath()1418         public static string GetBinPath()
1419         {
1420             Assembly asm = Assembly.GetEntryAssembly();
1421             if (asm == null) {
1422                 asm = Assembly.GetExecutingAssembly();
1423             }
1424 
1425             Module[] mods = asm.GetModules();
1426             string fn = mods[0].FullyQualifiedName;
1427             return Path.GetDirectoryName(fn) + Path.DirectorySeparatorChar;
1428         }
1429 
GetAppPath()1430         public static string GetAppPath()
1431         {
1432             string result = Path.GetFullPath(Path.Combine(GetBinPath(), @".." + Path.DirectorySeparatorChar));
1433             return result;
1434         }
1435 
GetPluginsPath()1436         public static string GetPluginsPath()
1437         {
1438             string appPath = GetAppPath();
1439             return appPath + "plugins" + Path.DirectorySeparatorChar;
1440         }
1441 
GetLangsPath()1442         public static string GetLangsPath()
1443         {
1444             string appPath = GetAppPath();
1445             return appPath + "locales" + Path.DirectorySeparatorChar;
1446         }
1447 
GetHelpPath(string langSign)1448         public static string GetHelpPath(string langSign)
1449         {
1450             string appPath = GetLangsPath();
1451             return appPath + "help_" + langSign + Path.DirectorySeparatorChar;
1452         }
1453 
GetBackgroundsPath()1454         public static string GetBackgroundsPath()
1455         {
1456             string appPath = GetAppPath();
1457             return appPath + "backgrounds" + Path.DirectorySeparatorChar;
1458         }
1459 
GetHomePath()1460         public static string GetHomePath()
1461         {
1462             string homePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
1463             return homePath + Path.DirectorySeparatorChar;
1464         }
1465 
CopyFile(FileInfo source, FileInfo target, IProgressController progressController)1466         public static void CopyFile(FileInfo source, FileInfo target, IProgressController progressController)
1467         {
1468             using (var sourceStm = source.OpenRead()) {
1469                 CopyFile(sourceStm, target, progressController);
1470             }
1471         }
1472 
CopyFile(Stream sourceStm, FileInfo target, IProgressController progressController)1473         public static void CopyFile(Stream sourceStm, FileInfo target, IProgressController progressController)
1474         {
1475             const int bufferSize = 1024 * 1024; // 1MB
1476             byte[] buffer = new byte[bufferSize];
1477             int reportedProgress = 0, read = 0;
1478             long len = sourceStm.Length;
1479             float flen = len;
1480 
1481             using (var targetStm = target.OpenWrite()) {
1482                 targetStm.SetLength(sourceStm.Length);
1483                 for (long size = 0; size < len; size += read) {
1484                     if (progressController != null) {
1485                         int progress = (int)((size / flen) * 100);
1486                         if (progress != reportedProgress) {
1487                             reportedProgress = progress;
1488                             progressController.ProgressStep(reportedProgress);
1489                         }
1490                     }
1491 
1492                     read = sourceStm.Read(buffer, 0, bufferSize);
1493                     targetStm.Write(buffer, 0, read);
1494                 }
1495             }
1496         }
1497 
LoadResourceStream(string resName)1498         public static Stream LoadResourceStream(string resName)
1499         {
1500             return LoadResourceStream(typeof(GKUtils), resName);
1501         }
1502 
LoadResourceStream(Type baseType, string resName)1503         public static Stream LoadResourceStream(Type baseType, string resName)
1504         {
1505             Assembly assembly = baseType.Assembly;
1506             return assembly.GetManifestResourceStream(resName);
1507         }
1508 
GetRelativePath(string fromFileName, string toFileName)1509         public static string GetRelativePath(string fromFileName, string toFileName)
1510         {
1511             var fromPath = Path.GetDirectoryName(fromFileName)+ Path.DirectorySeparatorChar;
1512 
1513             var fromUri = new Uri(fromPath);
1514             var toUri = new Uri(toFileName);
1515 
1516             var relativeUri = fromUri.MakeRelativeUri(toUri);
1517             var relativePath = Uri.UnescapeDataString(relativeUri.ToString());
1518 
1519             return relativePath.Replace('/', Path.DirectorySeparatorChar);
1520         }
1521 
1522         #endregion
1523 
1524         #region Show information summary
1525 
ShowAddressSummary(GDMAddress address, StringList summary)1526         private static void ShowAddressSummary(GDMAddress address, StringList summary)
1527         {
1528             if (address != null && !address.IsEmpty() && summary != null)
1529             {
1530                 summary.Add("    " + LangMan.LS(LSID.LSID_Address) + ":");
1531 
1532                 string ts = "";
1533                 if (address.AddressCountry != "")
1534                 {
1535                     ts = ts + address.AddressCountry + ", ";
1536                 }
1537                 if (address.AddressState != "")
1538                 {
1539                     ts = ts + address.AddressState + ", ";
1540                 }
1541                 if (address.AddressCity != "")
1542                 {
1543                     ts += address.AddressCity;
1544                 }
1545                 if (ts != "")
1546                 {
1547                     summary.Add("    " + ts);
1548                 }
1549 
1550                 ts = "";
1551                 if (address.AddressPostalCode != "")
1552                 {
1553                     ts = ts + address.AddressPostalCode + ", ";
1554                 }
1555                 if (address.Lines.Text.Trim() != "")
1556                 {
1557                     ts += address.Lines.Text.Trim();
1558                 }
1559                 if (ts != "")
1560                 {
1561                     summary.Add("    " + ts);
1562                 }
1563 
1564                 int num = address.PhoneNumbers.Count;
1565                 for (int i = 0; i < num; i++) {
1566                     summary.Add("    " + address.PhoneNumbers[i].StringValue);
1567                 }
1568 
1569                 int num2 = address.EmailAddresses.Count;
1570                 for (int i = 0; i < num2; i++) {
1571                     summary.Add("    " + address.EmailAddresses[i].StringValue);
1572                 }
1573 
1574                 int num3 = address.WebPages.Count;
1575                 for (int i = 0; i < num3; i++) {
1576                     summary.Add("    " + address.WebPages[i].StringValue);
1577                 }
1578             }
1579         }
1580 
ShowDetailCause(GDMCustomEvent evt, StringList summary)1581         private static void ShowDetailCause(GDMCustomEvent evt, StringList summary)
1582         {
1583             string cause = GetEventCause(evt);
1584             if (summary != null && !string.IsNullOrEmpty(cause)) {
1585                 summary.Add("    " + cause);
1586             }
1587         }
1588 
ShowEventDetailInfo(IBaseContext baseContext, GDMCustomEvent eventDetail, StringList summary)1589         private static void ShowEventDetailInfo(IBaseContext baseContext, GDMCustomEvent eventDetail, StringList summary)
1590         {
1591             if (eventDetail == null)
1592                 throw new ArgumentNullException("eventDetail");
1593 
1594             if (summary != null && eventDetail.SourceCitations.Count != 0) {
1595                 summary.Add("   " + LangMan.LS(LSID.LSID_RPSources) + " (" + eventDetail.SourceCitations.Count.ToString() + "):");
1596 
1597                 int num = eventDetail.SourceCitations.Count;
1598                 for (int i = 0; i < num; i++) {
1599                     GDMSourceCitation sourCit = eventDetail.SourceCitations[i];
1600                     GDMSourceRecord sourceRec = baseContext.Tree.GetPtrValue<GDMSourceRecord>(sourCit);
1601                     if (sourceRec == null) continue;
1602 
1603                     string nm = "\"" + sourceRec.ShortTitle + "\"";
1604                     if (!string.IsNullOrEmpty(sourCit.Page)) {
1605                         nm = nm + ", " + sourCit.Page;
1606                     }
1607                     summary.Add("     " + HyperLink(sourceRec.XRef, nm, 0));
1608                 }
1609             }
1610         }
1611 
ShowEvent(GDMTree tree, GDMRecord subj, StringList aToList, GDMRecord aRec, GDMCustomEvent evt)1612         private static void ShowEvent(GDMTree tree, GDMRecord subj, StringList aToList, GDMRecord aRec, GDMCustomEvent evt)
1613         {
1614             if (subj is GDMNoteRecord) {
1615                 int num = evt.Notes.Count;
1616                 for (int i = 0; i < num; i++) {
1617                     if (evt.Notes[i].XRef == subj.XRef) {
1618                         ShowLink(tree, subj, aToList, aRec, evt, null);
1619                     }
1620                 }
1621             } else if (subj is GDMMultimediaRecord) {
1622                 int num2 = evt.MultimediaLinks.Count;
1623                 for (int i = 0; i < num2; i++) {
1624                     if (evt.MultimediaLinks[i].XRef == subj.XRef) {
1625                         ShowLink(tree, subj, aToList, aRec, evt, null);
1626                     }
1627                 }
1628             } else if (subj is GDMSourceRecord) {
1629                 int num3 = evt.SourceCitations.Count;
1630                 for (int i = 0; i < num3; i++) {
1631                     if (evt.SourceCitations[i].XRef == subj.XRef) {
1632                         ShowLink(tree, subj, aToList, aRec, evt, evt.SourceCitations[i]);
1633                     }
1634                 }
1635             }
1636         }
1637 
ShowLink(GDMTree tree, GDMRecord aSubject, StringList aToList, GDMRecord aRec, GDMTag aTag, GDMPointer aExt)1638         private static void ShowLink(GDMTree tree, GDMRecord aSubject, StringList aToList, GDMRecord aRec, GDMTag aTag, GDMPointer aExt)
1639         {
1640             string prefix;
1641             if (aSubject is GDMSourceRecord && aExt != null) {
1642                 GDMSourceCitation cit = (aExt as GDMSourceCitation);
1643                 if (cit != null && !string.IsNullOrEmpty(cit.Page)) {
1644                     prefix = cit.Page + ": ";
1645                 } else {
1646                     prefix = "";
1647                 }
1648             } else {
1649                 prefix = "";
1650             }
1651 
1652             string suffix;
1653             if (aTag is GDMCustomEvent) {
1654                 suffix = ", " + GetEventName((GDMCustomEvent) aTag).ToLower();
1655             } else {
1656                 suffix = "";
1657             }
1658             aToList.Add("    " + prefix + GenRecordLink(tree, aRec, true) + suffix);
1659         }
1660 
ShowPersonExtInfo(GDMTree tree, GDMIndividualRecord iRec, StringList summary)1661         private static void ShowPersonExtInfo(GDMTree tree, GDMIndividualRecord iRec, StringList summary)
1662         {
1663             /*if (tree == null || iRec == null || summary == null) return;
1664 
1665             summary.Add("");
1666             int num = tree.RecordsCount;
1667             for (int i = 0; i < num; i++) {
1668         		GEDCOMRecord rec = tree[i];
1669                 if (rec.RecordType != GEDCOMRecordType.rtIndividual) continue;
1670 
1671                 GEDCOMIndividualRecord ir = (GEDCOMIndividualRecord)rec;
1672 
1673                 bool first = true;
1674                 for (int k = 0, cnt = ir.Associations.Count; k < cnt; k++) {
1675                     GEDCOMAssociation asso = ir.Associations[k];
1676 
1677                     if (asso.Individual == iRec) {
1678                         if (first) {
1679                             summary.Add(LangMan.LS(LSID.LSID_Associations) + ":");
1680                             first = false;
1681                         }
1682                         summary.Add("    " + HyperLink(ir.XRef, ir.GetNameString(true, false), 0));
1683                     }
1684                 }
1685             }*/
1686         }
1687 
ShowPersonNamesakes(GDMTree tree, GDMIndividualRecord iRec, StringList summary)1688         private static void ShowPersonNamesakes(GDMTree tree, GDMIndividualRecord iRec, StringList summary)
1689         {
1690             try {
1691                 StringList namesakes = new StringList();
1692                 try {
1693                     string st = GetNameString(iRec, true, false);
1694 
1695                     int num3 = tree.RecordsCount;
1696                     for (int i = 0; i < num3; i++) {
1697                         GDMRecord rec = tree[i];
1698 
1699                         if (rec is GDMIndividualRecord && rec != iRec) {
1700                             GDMIndividualRecord relPerson = (GDMIndividualRecord) rec;
1701 
1702                             string unk = GetNameString(relPerson, true, false);
1703                             if (st == unk) {
1704                                 namesakes.AddObject(unk + GetLifeStr(relPerson), relPerson);
1705                             }
1706                         }
1707                     }
1708 
1709                     if (namesakes.Count > 0) {
1710                         summary.Add("");
1711                         summary.Add(LangMan.LS(LSID.LSID_Namesakes) + ":");
1712 
1713                         int num4 = namesakes.Count;
1714                         for (int i = 0; i < num4; i++) {
1715                             GDMIndividualRecord relPerson = (GDMIndividualRecord)namesakes.GetObject(i);
1716 
1717                             summary.Add("    " + HyperLink(relPerson.XRef, namesakes[i], 0));
1718                         }
1719                     }
1720                 }
1721                 finally {
1722                     namesakes.Dispose();
1723                 }
1724             } catch (Exception ex) {
1725                 Logger.WriteError("GKUtils.ShowPersonNamesakes()", ex);
1726             }
1727         }
1728 
ShowSubjectLinks(GDMTree tree, GDMRecord aInRecord, GDMRecord subject, StringList aToList)1729         private static void ShowSubjectLinks(GDMTree tree, GDMRecord aInRecord, GDMRecord subject, StringList aToList)
1730         {
1731             try {
1732                 int num;
1733 
1734                 if (subject is GDMNoteRecord && aInRecord.HasNotes) {
1735                     num = aInRecord.Notes.Count;
1736                     for (int i = 0; i < num; i++) {
1737                         if (aInRecord.Notes[i].XRef == subject.XRef) {
1738                             ShowLink(tree, subject, aToList, aInRecord, null, null);
1739                         }
1740                     }
1741                 } else if (subject is GDMMultimediaRecord && aInRecord.HasMultimediaLinks) {
1742                     num = aInRecord.MultimediaLinks.Count;
1743                     for (int i = 0; i < num; i++) {
1744                         if (aInRecord.MultimediaLinks[i].XRef == subject.XRef) {
1745                             ShowLink(tree, subject, aToList, aInRecord, null, null);
1746                         }
1747                     }
1748                 } else if (subject is GDMSourceRecord && aInRecord.HasSourceCitations) {
1749                     num = aInRecord.SourceCitations.Count;
1750                     for (int i = 0; i < num; i++) {
1751                         var sourCit = aInRecord.SourceCitations[i];
1752                         if (sourCit.XRef == subject.XRef) {
1753                             ShowLink(tree, subject, aToList, aInRecord, null, sourCit);
1754                         }
1755                     }
1756                 }
1757 
1758                 var evsRec = aInRecord as GDMRecordWithEvents;
1759                 if (evsRec != null && evsRec.HasEvents) {
1760                     num = evsRec.Events.Count;
1761                     for (int i = 0; i < num; i++) {
1762                         ShowEvent(tree, subject, aToList, evsRec, evsRec.Events[i]);
1763                     }
1764                 }
1765             } catch (Exception ex) {
1766                 Logger.WriteError("GKUtils.ShowSubjectLinks()", ex);
1767             }
1768         }
1769 
SearchRecordLinks(List<GDMObject> linksList, GDMRecord inRecord, GDMRecord searchRec)1770         public static void SearchRecordLinks(List<GDMObject> linksList, GDMRecord inRecord, GDMRecord searchRec)
1771         {
1772             try {
1773                 int num;
1774                 switch (searchRec.RecordType) {
1775                     case GDMRecordType.rtNote:
1776                         num = inRecord.Notes.Count;
1777                         for (int i = 0; i < num; i++) {
1778                             var notes = inRecord.Notes[i];
1779                             if (notes.XRef == searchRec.XRef) {
1780                                 linksList.Add(notes);
1781                             }
1782                         }
1783                         break;
1784 
1785                     case GDMRecordType.rtMultimedia:
1786                         num = inRecord.MultimediaLinks.Count;
1787                         for (int i = 0; i < num; i++) {
1788                             var mmLink = inRecord.MultimediaLinks[i];
1789                             if (mmLink.XRef == searchRec.XRef) {
1790                                 linksList.Add(mmLink);
1791                             }
1792                         }
1793                         break;
1794 
1795                     case GDMRecordType.rtSource:
1796                         num = inRecord.SourceCitations.Count;
1797                         for (int i = 0; i < num; i++) {
1798                             var sourCit = inRecord.SourceCitations[i];
1799                             if (sourCit.XRef == searchRec.XRef) {
1800                                 linksList.Add(sourCit);
1801                             }
1802                         }
1803                         break;
1804                 }
1805 
1806                 /*var recordWithEvents = aInRecord as GEDCOMRecordWithEvents;
1807                 if (recordWithEvents != null) {
1808                     GEDCOMRecordWithEvents evsRec = recordWithEvents;
1809 
1810                     num = evsRec.Events.Count;
1811                     for (int i = 0; i < num; i++) {
1812                         ShowEvent(subject, linksList, evsRec, evsRec.Events[i]);
1813                     }
1814                 }*/
1815             } catch (Exception ex) {
1816                 Logger.WriteError("GKUtils.SearchRecordLinks()", ex);
1817             }
1818         }
1819 
SearchRecordLinks(List<GDMObject> linksList, GDMTree tree, GDMRecord searchRec)1820         public static void SearchRecordLinks(List<GDMObject> linksList, GDMTree tree, GDMRecord searchRec)
1821         {
1822             int num = tree.RecordsCount;
1823             for (int i = 0; i < num; i++) {
1824                 SearchRecordLinks(linksList, tree[i], searchRec);
1825             }
1826         }
1827 
SearchPortraits(GDMTree tree, GDMMultimediaRecord mmRec)1828         public static StringList SearchPortraits(GDMTree tree, GDMMultimediaRecord mmRec)
1829         {
1830             var result = new StringList();
1831 
1832             int num = tree.RecordsCount;
1833             for (int i = 0; i < num; i++) {
1834                 var rec = tree[i];
1835                 if (rec.RecordType == GDMRecordType.rtIndividual) {
1836                     var iRec = rec as GDMIndividualRecord;
1837 
1838                     num = iRec.MultimediaLinks.Count;
1839                     for (int k = 0; i < num; i++) {
1840                         var mmLink = iRec.MultimediaLinks[k];
1841                         if (mmLink.XRef == mmRec.XRef && mmLink.IsPrimary) {
1842                             string indiName = GKUtils.GetNameString(iRec, true, false);
1843                             ExtRect region = mmLink.CutoutPosition.Value;
1844                             result.AddObject(indiName, region);
1845                         }
1846                     }
1847                 }
1848             }
1849 
1850             return result;
1851         }
1852 
RecListMediaRefresh(IBaseContext baseContext, GDMRecord record, StringList summary)1853         private static void RecListMediaRefresh(IBaseContext baseContext, GDMRecord record, StringList summary)
1854         {
1855             if (record == null || summary == null) return;
1856 
1857             try {
1858                 if (record.HasMultimediaLinks) {
1859                     summary.Add("");
1860                     summary.Add(LangMan.LS(LSID.LSID_RPMultimedia) + " (" + record.MultimediaLinks.Count.ToString() + "):");
1861 
1862                     int num = record.MultimediaLinks.Count;
1863                     for (int i = 0; i < num; i++) {
1864                         GDMMultimediaLink mmLink = record.MultimediaLinks[i];
1865                         GDMMultimediaRecord mmRec = baseContext.Tree.GetPtrValue<GDMMultimediaRecord>(mmLink);
1866                         if (mmRec == null || mmRec.FileReferences.Count == 0) continue;
1867 
1868                         string st = mmRec.FileReferences[0].Title;
1869                         summary.Add("  " + HyperLink(mmRec.XRef, st, 0) + " (" +
1870                                     HyperLink("view_" + mmRec.XRef, LangMan.LS(LSID.LSID_MediaView), 0) + ")");
1871                     }
1872                 }
1873             } catch (Exception ex) {
1874                 Logger.WriteError("GKUtils.RecListMediaRefresh()", ex);
1875             }
1876         }
1877 
RecListNotesRefresh(IBaseContext baseContext, GDMRecord record, StringList summary)1878         private static void RecListNotesRefresh(IBaseContext baseContext, GDMRecord record, StringList summary)
1879         {
1880             if (record == null || summary == null) return;
1881 
1882             try {
1883                 if (record.HasNotes) {
1884                     summary.Add("");
1885                     summary.Add(LangMan.LS(LSID.LSID_RPNotes) + " (" + record.Notes.Count.ToString() + "):");
1886 
1887                     int num = record.Notes.Count;
1888                     for (int i = 0; i < num; i++) {
1889                         if (i > 0) {
1890                             summary.Add("");
1891                         }
1892 
1893                         GDMLines noteLines = baseContext.Tree.GetNoteLines(record.Notes[i]);
1894                         int num2 = noteLines.Count;
1895                         for (int k = 0; k < num2; k++) {
1896                             summary.Add(noteLines[k]);
1897                         }
1898                     }
1899                 }
1900             } catch (Exception ex) {
1901                 Logger.WriteError("GKUtils.RecListNotesRefresh()", ex);
1902             }
1903         }
1904 
RecListSourcesRefresh(IBaseContext baseContext, GDMRecord record, StringList summary)1905         private static void RecListSourcesRefresh(IBaseContext baseContext, GDMRecord record, StringList summary)
1906         {
1907             if (record == null || summary == null) return;
1908 
1909             try {
1910                 if (record.HasSourceCitations) {
1911                     summary.Add("");
1912                     summary.Add(LangMan.LS(LSID.LSID_RPSources) + " (" + record.SourceCitations.Count.ToString() + "):");
1913 
1914                     int num = record.SourceCitations.Count;
1915                     for (int i = 0; i < num; i++) {
1916                         GDMSourceCitation sourCit = record.SourceCitations[i];
1917                         GDMSourceRecord sourceRec = baseContext.Tree.GetPtrValue<GDMSourceRecord>(sourCit);
1918                         if (sourceRec == null) continue;
1919 
1920                         string nm = "\"" + sourceRec.ShortTitle + "\"";
1921 
1922                         if (sourCit.Page != "") {
1923                             nm = nm + ", " + sourCit.Page;
1924                         }
1925 
1926                         summary.Add("  " + HyperLink(sourceRec.XRef, nm, 0));
1927                     }
1928                 }
1929             } catch (Exception ex) {
1930                 Logger.WriteError("GKUtils.RecListSourcesRefresh()", ex);
1931             }
1932         }
1933 
RecListAssociationsRefresh(IBaseContext baseContext, GDMIndividualRecord record, StringList summary)1934         private static void RecListAssociationsRefresh(IBaseContext baseContext, GDMIndividualRecord record, StringList summary)
1935         {
1936             if (record == null || summary == null) return;
1937 
1938             try {
1939                 if (record.HasAssociations) {
1940                     summary.Add("");
1941                     summary.Add(LangMan.LS(LSID.LSID_Associations) + ":");
1942 
1943                     int num = record.Associations.Count;
1944                     for (int i = 0; i < num; i++) {
1945                         GDMAssociation ast = record.Associations[i];
1946                         var relIndi = baseContext.Tree.GetPtrValue(ast);
1947 
1948                         string nm = ((relIndi == null) ? string.Empty : GetNameString(relIndi, true, false));
1949                         string xref = ((relIndi == null) ? string.Empty : relIndi.XRef);
1950 
1951                         summary.Add("    " + ast.Relation + " " + HyperLink(xref, nm, 0));
1952                     }
1953                 }
1954             } catch (Exception ex) {
1955                 Logger.WriteError("GKUtils.RecListAssociationsRefresh()", ex);
1956             }
1957         }
1958 
RecListIndividualEventsRefresh(IBaseContext baseContext, GDMIndividualRecord record, StringList summary)1959         private static void RecListIndividualEventsRefresh(IBaseContext baseContext, GDMIndividualRecord record, StringList summary)
1960         {
1961             if (record == null || summary == null) return;
1962 
1963             try {
1964                 if (record.HasEvents) {
1965                     summary.Add("");
1966                     summary.Add(LangMan.LS(LSID.LSID_Events) + ":");
1967 
1968                     int num = record.Events.Count;
1969                     for (int i = 0; i < num; i++) {
1970                         summary.Add("");
1971 
1972                         GDMCustomEvent evt = record.Events[i];
1973                         string st = GetEventName(evt);
1974 
1975                         string sv = "";
1976                         if (evt.StringValue != "") {
1977                             sv = evt.StringValue + ", ";
1978                         }
1979                         summary.Add("  " + st + ": " + sv + GetEventDesc(baseContext.Tree, evt));
1980 
1981                         ShowDetailCause(evt, summary);
1982                         if (evt.HasAddress) {
1983                             ShowAddressSummary(evt.Address, summary);
1984                         }
1985                         ShowEventDetailInfo(baseContext, evt, summary);
1986                     }
1987                 }
1988             } catch (Exception ex) {
1989                 Logger.WriteError("GKUtils.RecListIndividualEventsRefresh()", ex);
1990             }
1991         }
1992 
RecListFamilyEventsRefresh(IBaseContext baseContext, GDMFamilyRecord record, StringList summary)1993         private static void RecListFamilyEventsRefresh(IBaseContext baseContext, GDMFamilyRecord record, StringList summary)
1994         {
1995             if (record == null || summary == null) return;
1996 
1997             try {
1998                 if (record.HasEvents) {
1999                     summary.Add("");
2000                     summary.Add(LangMan.LS(LSID.LSID_Events) + ":");
2001 
2002                     int num = record.Events.Count;
2003                     for (int i = 0; i < num; i++) {
2004                         summary.Add("");
2005 
2006                         GDMFamilyEvent evt = (GDMFamilyEvent)record.Events[i];
2007 
2008                         string st = GetEventName(evt);
2009                         summary.Add("  " + st + ": " + GetEventDesc(baseContext.Tree, evt));
2010 
2011                         ShowDetailCause(evt, summary);
2012                         ShowEventDetailInfo(baseContext, evt, summary);
2013                     }
2014                 }
2015             } catch (Exception ex) {
2016                 Logger.WriteError("GKUtils.RecListFamilyEventsRefresh()", ex);
2017             }
2018         }
2019 
RecListGroupsRefresh(IBaseContext baseContext, GDMIndividualRecord record, StringList summary)2020         private static void RecListGroupsRefresh(IBaseContext baseContext, GDMIndividualRecord record, StringList summary)
2021         {
2022             if (record == null || summary == null) return;
2023 
2024             try {
2025                 if (record.HasGroups) {
2026                     summary.Add("");
2027                     summary.Add(LangMan.LS(LSID.LSID_RPGroups) + ":");
2028 
2029                     int num = record.Groups.Count;
2030                     for (int i = 0; i < num; i++) {
2031                         GDMPointer ptr = record.Groups[i];
2032                         GDMGroupRecord grp = baseContext.Tree.GetPtrValue<GDMGroupRecord>(ptr);
2033                         if (grp == null) continue;
2034 
2035                         summary.Add("    " + HyperLink(grp.XRef, grp.GroupName, 0));
2036                     }
2037                 }
2038             } catch (Exception ex) {
2039                 Logger.WriteError("GKUtils.RecListGroupsRefresh()", ex);
2040             }
2041         }
2042 
2043         //
2044 
ShowFamilyInfo(IBaseContext baseContext, GDMFamilyRecord familyRec, StringList summary)2045         public static void ShowFamilyInfo(IBaseContext baseContext, GDMFamilyRecord familyRec, StringList summary)
2046         {
2047             if (summary == null) return;
2048 
2049             try {
2050                 summary.BeginUpdate();
2051                 try {
2052                     summary.Clear();
2053                     if (familyRec != null) {
2054                         summary.Add("");
2055 
2056                         GDMIndividualRecord spRec = baseContext.Tree.GetPtrValue(familyRec.Husband);
2057                         string st = ((spRec == null) ? LangMan.LS(LSID.LSID_UnkMale) : HyperLink(spRec.XRef, GetNameString(spRec, true, false), 0));
2058                         summary.Add(LangMan.LS(LSID.LSID_Husband) + ": " + st + GetLifeStr(spRec));
2059 
2060                         spRec = baseContext.Tree.GetPtrValue(familyRec.Wife);
2061                         st = ((spRec == null) ? LangMan.LS(LSID.LSID_UnkFemale) : HyperLink(spRec.XRef, GetNameString(spRec, true, false), 0));
2062                         summary.Add(LangMan.LS(LSID.LSID_Wife) + ": " + st + GetLifeStr(spRec));
2063 
2064                         summary.Add("");
2065                         if (familyRec.Children.Count != 0) {
2066                             summary.Add(LangMan.LS(LSID.LSID_Childs) + ":");
2067                         }
2068 
2069                         int num = familyRec.Children.Count;
2070                         for (int i = 0; i < num; i++) {
2071                             var child = baseContext.Tree.GetPtrValue(familyRec.Children[i]);
2072                             summary.Add("    " + HyperLink(child.XRef, GetNameString(child, true, false), 0) + GetLifeStr(child));
2073                         }
2074                         summary.Add("");
2075 
2076                         RecListFamilyEventsRefresh(baseContext, familyRec, summary);
2077                         RecListNotesRefresh(baseContext, familyRec, summary);
2078                         RecListMediaRefresh(baseContext, familyRec, summary);
2079                         RecListSourcesRefresh(baseContext, familyRec, summary);
2080                     }
2081                 } finally {
2082                     summary.EndUpdate();
2083                 }
2084             } catch (Exception ex) {
2085                 Logger.WriteError("GKUtils.ShowFamilyInfo()" , ex);
2086             }
2087         }
2088 
ShowGroupInfo(IBaseContext baseContext, GDMGroupRecord groupRec, StringList summary)2089         public static void ShowGroupInfo(IBaseContext baseContext, GDMGroupRecord groupRec, StringList summary)
2090         {
2091             if (summary == null) return;
2092 
2093             try {
2094                 StringList mbrList = new StringList();
2095                 summary.BeginUpdate();
2096                 try {
2097                     summary.Clear();
2098                     if (groupRec != null) {
2099                         summary.Add("");
2100                         summary.Add("[u][b][size=+1]" + groupRec.GroupName + "[/size][/b][/u]");
2101                         summary.Add("");
2102                         summary.Add(LangMan.LS(LSID.LSID_Members) + " (" + groupRec.Members.Count.ToString() + "):");
2103 
2104                         int num = groupRec.Members.Count;
2105                         for (int i = 0; i < num; i++) {
2106                             GDMPointer ptr = groupRec.Members[i];
2107                             var member = baseContext.Tree.GetPtrValue<GDMIndividualRecord>(ptr);
2108 
2109                             mbrList.AddObject(GetNameString(member, true, false), member);
2110                         }
2111                         mbrList.Sort();
2112 
2113                         int num2 = mbrList.Count;
2114                         for (int i = 0; i < num2; i++) {
2115                             GDMIndividualRecord member = (GDMIndividualRecord)mbrList.GetObject(i);
2116 
2117                             summary.Add("    " + HyperLink(member.XRef, mbrList[i], i + 1));
2118                         }
2119 
2120                         RecListNotesRefresh(baseContext, groupRec, summary);
2121                         RecListMediaRefresh(baseContext, groupRec, summary);
2122                     }
2123                 } finally {
2124                     summary.EndUpdate();
2125                     mbrList.Dispose();
2126                 }
2127             } catch (Exception ex) {
2128                 Logger.WriteError("GKUtils.ShowGroupInfo()", ex);
2129             }
2130         }
2131 
ShowMultimediaInfo(IBaseContext baseContext, GDMMultimediaRecord mediaRec, StringList summary)2132         public static void ShowMultimediaInfo(IBaseContext baseContext, GDMMultimediaRecord mediaRec, StringList summary)
2133         {
2134             if (summary == null) return;
2135 
2136             try {
2137                 summary.BeginUpdate();
2138                 try {
2139                     summary.Clear();
2140                     if (mediaRec != null) {
2141                         GDMFileReferenceWithTitle fileRef = mediaRec.FileReferences[0];
2142                         string mediaTitle = (fileRef == null) ? LangMan.LS(LSID.LSID_Unknown) : fileRef.Title;
2143 
2144                         summary.Add("");
2145                         summary.Add("[u][b][size=+1]" + mediaTitle + "[/size][/b][/u]");
2146                         summary.Add("");
2147                         if (fileRef != null) {
2148                             summary.Add("[ " + HyperLink("view_" + mediaRec.XRef, LangMan.LS(LSID.LSID_View), 0) + " ]");
2149                         }
2150                         summary.Add("");
2151                         summary.Add(LangMan.LS(LSID.LSID_Links) + ":");
2152 
2153                         GDMTree tree = baseContext.Tree;
2154                         int num = tree.RecordsCount;
2155                         for (int i = 0; i < num; i++) {
2156                             ShowSubjectLinks(tree, tree[i], mediaRec, summary);
2157                         }
2158 
2159                         RecListNotesRefresh(baseContext, mediaRec, summary);
2160                         RecListSourcesRefresh(baseContext, mediaRec, summary);
2161                     }
2162                 } finally {
2163                     summary.EndUpdate();
2164                 }
2165             } catch (Exception ex) {
2166                 Logger.WriteError("GKUtils.ShowMultimediaInfo()", ex);
2167             }
2168         }
2169 
ShowNoteInfo(IBaseContext baseContext, GDMNoteRecord noteRec, StringList summary)2170         public static void ShowNoteInfo(IBaseContext baseContext, GDMNoteRecord noteRec, StringList summary)
2171         {
2172             if (summary == null) return;
2173 
2174             try {
2175                 summary.BeginUpdate();
2176                 try {
2177                     summary.Clear();
2178                     if (noteRec != null) {
2179                         summary.Add("");
2180                         summary.AddStrings(noteRec.Lines.ToArray());
2181                         summary.Add("");
2182                         summary.Add(LangMan.LS(LSID.LSID_Links) + ":");
2183 
2184                         GDMTree tree = baseContext.Tree;
2185                         int num = tree.RecordsCount;
2186                         for (int i = 0; i < num; i++) {
2187                             ShowSubjectLinks(tree, tree[i], noteRec, summary);
2188                         }
2189                     }
2190                 } finally {
2191                     summary.EndUpdate();
2192                 }
2193             } catch (Exception ex) {
2194                 Logger.WriteError("GKUtils.ShowNoteInfo()", ex);
2195             }
2196         }
2197 
ShowPersonInfo(IBaseContext baseContext, GDMIndividualRecord iRec, StringList summary)2198         public static void ShowPersonInfo(IBaseContext baseContext, GDMIndividualRecord iRec, StringList summary)
2199         {
2200             if (summary == null) return;
2201 
2202             try {
2203                 summary.BeginUpdate();
2204                 summary.Clear();
2205                 try {
2206                     if (iRec != null) {
2207                         GDMTree tree = baseContext.Tree;
2208 
2209                         summary.Add("");
2210                         summary.Add("[u][b][size=+1]" + GetNameString(iRec, true, true) + "[/size][/u][/b]");
2211                         summary.Add(LangMan.LS(LSID.LSID_Sex) + ": " + SexStr(iRec.Sex));
2212                         try {
2213                             GDMIndividualRecord father, mother;
2214                             tree.GetParents(iRec, out father, out mother);
2215 
2216                             if (father != null || mother != null) {
2217                                 summary.Add("");
2218                                 summary.Add(LangMan.LS(LSID.LSID_Parents) + ":");
2219 
2220                                 string st;
2221 
2222                                 st = (father == null) ? LangMan.LS(LSID.LSID_UnkMale) : HyperLink(father.XRef, GetNameString(father, true, false), 0);
2223                                 summary.Add("  " + LangMan.LS(LSID.LSID_Father) + ": " + st + GetLifeStr(father));
2224 
2225                                 st = (mother == null) ? LangMan.LS(LSID.LSID_UnkFemale) : HyperLink(mother.XRef, GetNameString(mother, true, false), 0);
2226                                 summary.Add("  " + LangMan.LS(LSID.LSID_Mother) + ": " + st + GetLifeStr(mother));
2227                             }
2228                         } catch (Exception ex) {
2229                             Logger.WriteError("GKUtils.ShowPersonInfo().Parents()", ex);
2230                         }
2231 
2232                         try {
2233                             int num = iRec.SpouseToFamilyLinks.Count;
2234                             for (int i = 0; i < num; i++) {
2235                                 GDMFamilyRecord family = tree.GetPtrValue(iRec.SpouseToFamilyLinks[i]);
2236                                 if (family == null) continue;
2237                                 if (!baseContext.IsRecordAccess(family.Restriction)) continue;
2238 
2239                                 string st;
2240                                 GDMIndividualRecord spRec;
2241                                 string unk;
2242                                 if (iRec.Sex == GDMSex.svMale) {
2243                                     spRec = tree.GetPtrValue(family.Wife);
2244                                     st = LangMan.LS(LSID.LSID_Wife) + ": ";
2245                                     unk = LangMan.LS(LSID.LSID_UnkFemale);
2246                                 } else {
2247                                     spRec = tree.GetPtrValue(family.Husband);
2248                                     st = LangMan.LS(LSID.LSID_Husband) + ": ";
2249                                     unk = LangMan.LS(LSID.LSID_UnkMale);
2250                                 }
2251                                 string marr = GetMarriageDateStr(family, GlobalOptions.Instance.DefDateFormat);
2252                                 if (marr != "") {
2253                                     marr = LangMan.LS(LSID.LSID_LMarriage) + " " + marr;
2254                                 } else {
2255                                     marr = LangMan.LS(LSID.LSID_LFamily);
2256                                 }
2257 
2258                                 summary.Add("");
2259                                 if (spRec != null) {
2260                                     st = st + HyperLink(spRec.XRef, GetNameString(spRec, true, false), 0) + " (" + HyperLink(family.XRef, marr, 0) + ")";
2261                                 } else {
2262                                     st = st + unk + " (" + HyperLink(family.XRef, marr, 0) + ")";
2263                                 }
2264                                 summary.Add(st);
2265 
2266                                 int chNum = family.Children.Count;
2267                                 if (chNum != 0) {
2268                                     summary.Add("");
2269                                     summary.Add(LangMan.LS(LSID.LSID_Childs) + ":");
2270 
2271                                     for (int k = 0; k < chNum; k++) {
2272                                         GDMIndividualRecord child = tree.GetPtrValue(family.Children[k]);
2273                                         if (child == null) continue;
2274 
2275                                         summary.Add("    " + HyperLink(child.XRef, GetNameString(child, true, false), 0) + GetLifeStr(child));
2276                                     }
2277                                 }
2278                             }
2279                         } catch (Exception ex) {
2280                             Logger.WriteError("GKUtils.ShowPersonInfo().Families()", ex);
2281                         }
2282 
2283                         RecListIndividualEventsRefresh(baseContext, iRec, summary);
2284                         RecListNotesRefresh(baseContext, iRec, summary);
2285                         RecListMediaRefresh(baseContext, iRec, summary);
2286                         RecListSourcesRefresh(baseContext, iRec, summary);
2287                         RecListAssociationsRefresh(baseContext, iRec, summary);
2288                         RecListGroupsRefresh(baseContext, iRec, summary);
2289 
2290                         ShowPersonNamesakes(tree, iRec, summary);
2291                         ShowPersonExtInfo(tree, iRec, summary);
2292                     }
2293                 } finally {
2294                     summary.EndUpdate();
2295                 }
2296             } catch (Exception ex) {
2297                 Logger.WriteError("GKUtils.ShowPersonInfo()", ex);
2298             }
2299         }
2300 
ShowSourceInfo(IBaseContext baseContext, GDMSourceRecord sourceRec, StringList summary)2301         public static void ShowSourceInfo(IBaseContext baseContext, GDMSourceRecord sourceRec, StringList summary)
2302         {
2303             if (summary == null) return;
2304 
2305             try {
2306                 summary.BeginUpdate();
2307                 StringList linkList = new StringList();
2308                 try {
2309                     summary.Clear();
2310                     if (sourceRec != null) {
2311                         summary.Add("");
2312                         summary.Add("[u][b][size=+1]" + sourceRec.ShortTitle + "[/size][/b][/u]");
2313                         summary.Add("");
2314                         summary.AddMultiline(LangMan.LS(LSID.LSID_Author) + ": " + sourceRec.Originator.Lines.Text.Trim());
2315                         summary.AddMultiline(LangMan.LS(LSID.LSID_Title) + ": \"" + sourceRec.Title.Lines.Text.Trim() + "\"");
2316                         summary.AddMultiline(LangMan.LS(LSID.LSID_Publication) + ": \"" + sourceRec.Publication.Lines.Text.Trim() + "\"");
2317                         summary.AddMultiline(LangMan.LS(LSID.LSID_Text) + ": \"" + sourceRec.Text.Lines.Text.Trim() + "\"");
2318 
2319                         if (sourceRec.RepositoryCitations.Count > 0) {
2320                             summary.Add("");
2321                             summary.Add(LangMan.LS(LSID.LSID_RPRepositories) + ":");
2322 
2323                             int num = sourceRec.RepositoryCitations.Count;
2324                             for (int i = 0; i < num; i++) {
2325                                 GDMRepositoryRecord rep = baseContext.Tree.GetPtrValue<GDMRepositoryRecord>(sourceRec.RepositoryCitations[i]);
2326 
2327                                 summary.Add("    " + HyperLink(rep.XRef, rep.RepositoryName, 0));
2328                             }
2329                         }
2330 
2331                         summary.Add("");
2332                         summary.Add(LangMan.LS(LSID.LSID_Links) + ":");
2333 
2334                         GDMTree tree = baseContext.Tree;
2335 
2336                         int num2 = tree.RecordsCount;
2337                         for (int j = 0; j < num2; j++) {
2338                             ShowSubjectLinks(tree, tree[j], sourceRec, linkList);
2339                         }
2340 
2341                         linkList.Sort();
2342 
2343                         int num3 = linkList.Count;
2344                         for (int j = 0; j < num3; j++) {
2345                             summary.Add(linkList[j]);
2346                         }
2347 
2348                         RecListNotesRefresh(baseContext, sourceRec, summary);
2349                         RecListMediaRefresh(baseContext, sourceRec, summary);
2350                     }
2351                 } finally {
2352                     linkList.Dispose();
2353                     summary.EndUpdate();
2354                 }
2355             } catch (Exception ex) {
2356                 Logger.WriteError("GKUtils.ShowSourceInfo()", ex);
2357             }
2358         }
2359 
ShowRepositoryInfo(IBaseContext baseContext, GDMRepositoryRecord repositoryRec, StringList summary)2360         public static void ShowRepositoryInfo(IBaseContext baseContext, GDMRepositoryRecord repositoryRec, StringList summary)
2361         {
2362             if (summary == null) return;
2363 
2364             try {
2365                 summary.BeginUpdate();
2366                 try {
2367                     summary.Clear();
2368                     if (repositoryRec != null) {
2369                         summary.Add("");
2370                         summary.Add("[u][b][size=+1]" + repositoryRec.RepositoryName.Trim() + "[/size][/b][/u]");
2371                         summary.Add("");
2372 
2373                         ShowAddressSummary(repositoryRec.Address, summary);
2374 
2375                         summary.Add("");
2376                         summary.Add(LangMan.LS(LSID.LSID_RPSources) + ":");
2377 
2378                         var sortedSources = new List<Tuple<string, string>>();
2379                         GDMTree tree = baseContext.Tree;
2380                         int num = tree.RecordsCount;
2381                         for (int i = 0; i < num; i++) {
2382                             GDMRecord rec = tree[i];
2383 
2384                             if (rec.RecordType == GDMRecordType.rtSource) {
2385                                 GDMSourceRecord srcRec = (GDMSourceRecord) rec;
2386 
2387                                 int num2 = srcRec.RepositoryCitations.Count;
2388                                 for (int j = 0; j < num2; j++) {
2389                                     if (srcRec.RepositoryCitations[j].XRef == repositoryRec.XRef) {
2390                                         sortedSources.Add(GenRecordLinkTuple(baseContext.Tree, srcRec, false));
2391                                     }
2392                                 }
2393                             }
2394                         }
2395                         sortedSources.Sort();
2396                         foreach (var tpl in sortedSources) {
2397                             summary.Add("    " + tpl.Item2);
2398                         }
2399 
2400                         RecListNotesRefresh(baseContext, repositoryRec, summary);
2401                     }
2402                 } finally {
2403                     summary.EndUpdate();
2404                 }
2405             } catch (Exception ex) {
2406                 Logger.WriteError("GKUtils.ShowRepositoryInfo()", ex);
2407             }
2408         }
2409 
ShowResearchInfo(IBaseContext baseContext, GDMResearchRecord researchRec, StringList summary)2410         public static void ShowResearchInfo(IBaseContext baseContext, GDMResearchRecord researchRec, StringList summary)
2411         {
2412             if (summary == null) return;
2413 
2414             try {
2415                 summary.BeginUpdate();
2416                 try {
2417                     summary.Clear();
2418                     if (researchRec != null) {
2419                         summary.Add("");
2420                         summary.Add(LangMan.LS(LSID.LSID_Title) + ": [u][b][size=+1]\"" + researchRec.ResearchName.Trim() + "\"[/size][/b][/u]");
2421                         summary.Add("");
2422                         summary.Add(LangMan.LS(LSID.LSID_Priority) + ": " + LangMan.LS(GKData.PriorityNames[(int)researchRec.Priority]));
2423                         summary.Add(LangMan.LS(LSID.LSID_Status) + ": " + LangMan.LS(GKData.StatusNames[(int)researchRec.Status]) + " (" + researchRec.Percent.ToString() + "%)");
2424                         summary.Add(LangMan.LS(LSID.LSID_StartDate) + ": " + researchRec.StartDate.GetDisplayString(GlobalOptions.Instance.DefDateFormat));
2425                         summary.Add(LangMan.LS(LSID.LSID_StopDate) + ": " + researchRec.StopDate.GetDisplayString(GlobalOptions.Instance.DefDateFormat));
2426 
2427                         if (researchRec.Tasks.Count > 0) {
2428                             summary.Add("");
2429                             summary.Add(LangMan.LS(LSID.LSID_RPTasks) + ":");
2430 
2431                             int num = researchRec.Tasks.Count;
2432                             for (int i = 0; i < num; i++) {
2433                                 var taskRec = baseContext.Tree.GetPtrValue<GDMTaskRecord>(researchRec.Tasks[i]);
2434                                 summary.Add("    " + GenRecordLink(baseContext.Tree, taskRec, false));
2435                             }
2436                         }
2437 
2438                         if (researchRec.Communications.Count > 0) {
2439                             summary.Add("");
2440                             summary.Add(LangMan.LS(LSID.LSID_RPCommunications) + ":");
2441 
2442                             int num2 = researchRec.Communications.Count;
2443                             for (int i = 0; i < num2; i++) {
2444                                 var corrRec = baseContext.Tree.GetPtrValue<GDMCommunicationRecord>(researchRec.Communications[i]);
2445                                 summary.Add("    " + GenRecordLink(baseContext.Tree, corrRec, false));
2446                             }
2447                         }
2448 
2449                         if (researchRec.Groups.Count != 0) {
2450                             summary.Add("");
2451                             summary.Add(LangMan.LS(LSID.LSID_RPGroups) + ":");
2452 
2453                             int num3 = researchRec.Groups.Count;
2454                             for (int i = 0; i < num3; i++) {
2455                                 var grp = baseContext.Tree.GetPtrValue<GDMGroupRecord>(researchRec.Groups[i]);
2456                                 summary.Add("    " + HyperLink(grp.XRef, grp.GroupName, 0));
2457                             }
2458                         }
2459 
2460                         RecListNotesRefresh(baseContext, researchRec, summary);
2461                     }
2462                 } finally {
2463                     summary.EndUpdate();
2464                 }
2465             } catch (Exception ex) {
2466                 Logger.WriteError("GKUtils.ShowResearchInfo()", ex);
2467             }
2468         }
2469 
ShowTaskInfo(IBaseContext baseContext, GDMTaskRecord taskRec, StringList summary)2470         public static void ShowTaskInfo(IBaseContext baseContext, GDMTaskRecord taskRec, StringList summary)
2471         {
2472             if (summary == null) return;
2473 
2474             try {
2475                 summary.BeginUpdate();
2476                 try {
2477                     summary.Clear();
2478                     if (taskRec != null) {
2479                         summary.Add("");
2480                         summary.Add(LangMan.LS(LSID.LSID_Goal) + ": [u][b][size=+1]" + GetTaskGoalStr(baseContext.Tree, taskRec) + "[/size][/b][/u]");
2481                         summary.Add("");
2482                         summary.Add(LangMan.LS(LSID.LSID_Priority) + ": " + LangMan.LS(GKData.PriorityNames[(int)taskRec.Priority]));
2483                         summary.Add(LangMan.LS(LSID.LSID_StartDate) + ": " + taskRec.StartDate.GetDisplayString(GlobalOptions.Instance.DefDateFormat));
2484                         summary.Add(LangMan.LS(LSID.LSID_StopDate) + ": " + taskRec.StopDate.GetDisplayString(GlobalOptions.Instance.DefDateFormat));
2485 
2486                         RecListNotesRefresh(baseContext, taskRec, summary);
2487                     }
2488                 } finally {
2489                     summary.EndUpdate();
2490                 }
2491             } catch (Exception ex) {
2492                 Logger.WriteError("GKUtils.ShowTaskInfo()", ex);
2493             }
2494         }
2495 
ShowCommunicationInfo(IBaseContext baseContext, GDMCommunicationRecord commRec, StringList summary)2496         public static void ShowCommunicationInfo(IBaseContext baseContext, GDMCommunicationRecord commRec, StringList summary)
2497         {
2498             if (summary == null) return;
2499 
2500             try {
2501                 summary.BeginUpdate();
2502                 try {
2503                     summary.Clear();
2504                     if (commRec != null) {
2505                         GDMTree tree = baseContext.Tree;
2506 
2507                         summary.Add("");
2508                         summary.Add(LangMan.LS(LSID.LSID_Theme) + ": [u][b][size=+1]\"" + commRec.CommName.Trim() + "\"[/size][/b][/u]");
2509                         summary.Add("");
2510                         summary.Add(LangMan.LS(LSID.LSID_Corresponder) + ": " + GetCorresponderStr(tree, commRec, true));
2511                         summary.Add(LangMan.LS(LSID.LSID_Type) + ": " + LangMan.LS(GKData.CommunicationNames[(int)commRec.CommunicationType]));
2512                         summary.Add(LangMan.LS(LSID.LSID_Date) + ": " + commRec.Date.GetDisplayString(GlobalOptions.Instance.DefDateFormat));
2513 
2514                         RecListNotesRefresh(baseContext, commRec, summary);
2515                         RecListMediaRefresh(baseContext, commRec, summary);
2516                     }
2517                 } finally {
2518                     summary.EndUpdate();
2519                 }
2520             } catch (Exception ex) {
2521                 Logger.WriteError("GKUtils.ShowCommunicationInfo()", ex);
2522             }
2523         }
2524 
ShowLocationInfo(IBaseContext baseContext, GDMLocationRecord locRec, StringList summary)2525         public static void ShowLocationInfo(IBaseContext baseContext, GDMLocationRecord locRec, StringList summary)
2526         {
2527             if (summary == null) return;
2528 
2529             try {
2530                 summary.BeginUpdate();
2531                 StringList linkList = null;
2532                 try {
2533                     summary.Clear();
2534                     if (locRec != null) {
2535                         summary.Add("");
2536                         summary.Add("[u][b][size=+1]" + locRec.LocationName.Trim() + "[/size][/b][/u]");
2537                         summary.Add("");
2538                         summary.Add(LangMan.LS(LSID.LSID_Latitude) + ": " + locRec.Map.Lati);
2539                         summary.Add(LangMan.LS(LSID.LSID_Longitude) + ": " + locRec.Map.Long);
2540 
2541                         GDMTree tree = baseContext.Tree;
2542 
2543                         linkList = GetLocationLinks(tree, locRec);
2544 
2545                         if (linkList.Count > 0) {
2546                             linkList.Sort();
2547 
2548                             summary.Add("");
2549                             summary.Add(LangMan.LS(LSID.LSID_Links) + ":");
2550 
2551                             int num = linkList.Count;
2552                             for (int i = 0; i < num; i++) {
2553                                 GDMRecord rec = linkList.GetObject(i) as GDMRecord;
2554                                 summary.Add("    " + HyperLink(rec.XRef, linkList[i], 0));
2555                             }
2556                         }
2557 
2558                         RecListNotesRefresh(baseContext, locRec, summary);
2559                         RecListMediaRefresh(baseContext, locRec, summary);
2560                     }
2561                 } finally {
2562                     if (linkList != null) linkList.Dispose();
2563                     summary.EndUpdate();
2564                 }
2565             } catch (Exception ex) {
2566                 Logger.WriteError("GKUtils.ShowLocationInfo()", ex);
2567             }
2568         }
2569 
GetRecordContent(IBaseContext baseContext, GDMRecord record, StringList ctx)2570         public static void GetRecordContent(IBaseContext baseContext, GDMRecord record, StringList ctx)
2571         {
2572             if (record != null && ctx != null) {
2573                 try {
2574                     switch (record.RecordType) {
2575                         case GDMRecordType.rtIndividual:
2576                             GKUtils.ShowPersonInfo(baseContext, record as GDMIndividualRecord, ctx);
2577                             break;
2578 
2579                         case GDMRecordType.rtFamily:
2580                             GKUtils.ShowFamilyInfo(baseContext, record as GDMFamilyRecord, ctx);
2581                             break;
2582 
2583                         case GDMRecordType.rtNote:
2584                             GKUtils.ShowNoteInfo(baseContext, record as GDMNoteRecord, ctx);
2585                             break;
2586 
2587                         case GDMRecordType.rtMultimedia:
2588                             GKUtils.ShowMultimediaInfo(baseContext, record as GDMMultimediaRecord, ctx);
2589                             break;
2590 
2591                         case GDMRecordType.rtSource:
2592                             GKUtils.ShowSourceInfo(baseContext, record as GDMSourceRecord, ctx);
2593                             break;
2594 
2595                         case GDMRecordType.rtRepository:
2596                             GKUtils.ShowRepositoryInfo(baseContext, record as GDMRepositoryRecord, ctx);
2597                             break;
2598 
2599                         case GDMRecordType.rtGroup:
2600                             GKUtils.ShowGroupInfo(baseContext, record as GDMGroupRecord, ctx);
2601                             break;
2602 
2603                         case GDMRecordType.rtResearch:
2604                             GKUtils.ShowResearchInfo(baseContext, record as GDMResearchRecord, ctx);
2605                             break;
2606 
2607                         case GDMRecordType.rtTask:
2608                             GKUtils.ShowTaskInfo(baseContext, record as GDMTaskRecord, ctx);
2609                             break;
2610 
2611                         case GDMRecordType.rtCommunication:
2612                             GKUtils.ShowCommunicationInfo(baseContext, record as GDMCommunicationRecord, ctx);
2613                             break;
2614 
2615                         case GDMRecordType.rtLocation:
2616                             GKUtils.ShowLocationInfo(baseContext, record as GDMLocationRecord, ctx);
2617                             break;
2618                     }
2619                 } catch (Exception ex) {
2620                     Logger.WriteError("GKUtils.GetRecordContext()", ex);
2621                 }
2622             }
2623         }
2624 
2625         #endregion
2626 
2627         #region Multimedia support (static)
2628 
GetStoreFolder(MultimediaKind mmKind)2629         public static string GetStoreFolder(MultimediaKind mmKind)
2630         {
2631             string result = "";
2632             switch (mmKind)
2633             {
2634                 case MultimediaKind.mkNone:
2635                     result = "unknown";
2636                     break;
2637 
2638                 case MultimediaKind.mkImage:
2639                     result = "images";
2640                     break;
2641 
2642                 case MultimediaKind.mkAudio:
2643                     result = "audio";
2644                     break;
2645 
2646                 case MultimediaKind.mkText:
2647                     result = "texts";
2648                     break;
2649 
2650                 case MultimediaKind.mkVideo:
2651                     result = "video";
2652                     break;
2653             }
2654             return result + Path.DirectorySeparatorChar;
2655         }
2656 
GetMultimediaKind(GDMMultimediaFormat format)2657         public static MultimediaKind GetMultimediaKind(GDMMultimediaFormat format)
2658         {
2659             switch (format)
2660             {
2661                 case GDMMultimediaFormat.mfNone:
2662                     return MultimediaKind.mkNone;
2663 
2664                 case GDMMultimediaFormat.mfBMP:
2665                 case GDMMultimediaFormat.mfGIF:
2666                 case GDMMultimediaFormat.mfJPG:
2667                 case GDMMultimediaFormat.mfPCX: // .net isn't supports
2668                 case GDMMultimediaFormat.mfTIF:
2669                 case GDMMultimediaFormat.mfTGA: // .net isn't supports
2670                 case GDMMultimediaFormat.mfPNG:
2671                 case GDMMultimediaFormat.mfRAW: // .net isn't supports
2672                 case GDMMultimediaFormat.mfPSD: // .net isn't supports
2673                     return MultimediaKind.mkImage;
2674 
2675                 case GDMMultimediaFormat.mfTXT:
2676                 case GDMMultimediaFormat.mfRTF:
2677                 case GDMMultimediaFormat.mfHTM:
2678                 case GDMMultimediaFormat.mfPDF:
2679                     return MultimediaKind.mkText;
2680 
2681                 case GDMMultimediaFormat.mfWAV:
2682                 case GDMMultimediaFormat.mfMP3:
2683                 case GDMMultimediaFormat.mfWMA:
2684                 case GDMMultimediaFormat.mfMKA:
2685                     return MultimediaKind.mkAudio;
2686 
2687                 case GDMMultimediaFormat.mfAVI:
2688                 case GDMMultimediaFormat.mfMPG:
2689                 case GDMMultimediaFormat.mfMP4:
2690                 case GDMMultimediaFormat.mfOGV:
2691                 case GDMMultimediaFormat.mfWMV:
2692                 case GDMMultimediaFormat.mfMKV:
2693                 case GDMMultimediaFormat.mfMOV:
2694                     return MultimediaKind.mkVideo;
2695 
2696                 case GDMMultimediaFormat.mfOLE:
2697                 case GDMMultimediaFormat.mfUnknown:
2698                 default:
2699                     return MultimediaKind.mkNone;
2700             }
2701         }
2702 
GetStoreTypeEx(GDMFileReference fileReference)2703         public static MediaStoreType GetStoreTypeEx(GDMFileReference fileReference)
2704         {
2705             if (fileReference == null)
2706                 throw new ArgumentNullException("fileReference");
2707 
2708             string fileRef = fileReference.StringValue;
2709             MediaStoreType result = MediaStoreType.mstReference;
2710 
2711             for (int i = 1; i <= 4; i++) {
2712                 if (fileRef.StartsWith(GKData.GKStoreTypes[i].Sign, StringComparison.Ordinal)) {
2713                     result = (MediaStoreType)i;
2714                     break;
2715                 }
2716             }
2717 
2718             return result;
2719         }
2720 
GetStoreType(GDMFileReference fileReference)2721         public static MediaStore GetStoreType(GDMFileReference fileReference)
2722         {
2723             if (fileReference == null)
2724                 throw new ArgumentNullException("fileReference");
2725 
2726             string fileName = fileReference.StringValue;
2727             MediaStoreType storeType = GetStoreTypeEx(fileReference);
2728 
2729             if (storeType != MediaStoreType.mstReference && storeType != MediaStoreType.mstURL) {
2730                 fileName = fileName.Remove(0, 4);
2731             }
2732 
2733             return new MediaStore(storeType, fileName);
2734         }
2735 
UseEmbeddedViewer(GDMMultimediaFormat format)2736         public static bool UseEmbeddedViewer(GDMMultimediaFormat format)
2737         {
2738             MultimediaKind mmKind = GKUtils.GetMultimediaKind(format);
2739 
2740             switch (mmKind) {
2741                 case MultimediaKind.mkImage:
2742                     return !(format == GDMMultimediaFormat.mfPCX || format == GDMMultimediaFormat.mfTGA ||
2743                              format == GDMMultimediaFormat.mfRAW || format == GDMMultimediaFormat.mfPSD);
2744 
2745                 case MultimediaKind.mkVideo:
2746                 case MultimediaKind.mkAudio:
2747                     return GlobalOptions.Instance.EmbeddedMediaPlayer;
2748 
2749                 case MultimediaKind.mkText:
2750                     return (format == GDMMultimediaFormat.mfTXT || format == GDMMultimediaFormat.mfRTF ||
2751                             format == GDMMultimediaFormat.mfHTM);
2752 
2753                 case MultimediaKind.mkNone:
2754                 default:
2755                     return false;
2756             }
2757         }
2758 
InitSecurityProtocol()2759         public static void InitSecurityProtocol()
2760         {
2761             // Mono v4.6 doesn't contain SecurityProtocolType.Tls11
2762             #if NET35 || NET40 || MONO
2763             const int Tls11 = 768;
2764             const int Tls12 = 3072;
2765             #endif
2766 
2767             try {
2768                 #if NET35 || NET40 || MONO
2769                 ServicePointManager.SecurityProtocol =
2770                     (SecurityProtocolType)(ServicePointManager.SecurityProtocol |
2771                                            SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls |
2772                                            (SecurityProtocolType)Tls11 | (SecurityProtocolType)Tls12);
2773                 #else
2774                 ServicePointManager.SecurityProtocol =
2775                     (SecurityProtocolType)(ServicePointManager.SecurityProtocol |
2776                                            SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls |
2777                                            SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12);
2778                 #endif
2779             } catch (Exception ex) {
2780                 // crash on WinXP, TLS 1.2 not supported
2781                 Logger.WriteError("GKUtils.InitSecurityProtocol()", ex);
2782             }
2783         }
2784 
GetWebStream(string uri)2785         public static Stream GetWebStream(string uri)
2786         {
2787             InitSecurityProtocol();
2788 
2789             using (var webClient = new WebClient()) {
2790                 byte[] dataBytes = webClient.DownloadData(uri);
2791                 var ms = new MemoryStream();
2792                 ms.Write(dataBytes, 0, dataBytes.Length);
2793                 return ms;
2794             }
2795         }
2796 
2797         #endregion
2798 
2799         #region Archives support (static)
2800 
GetContainerName(string fileName, bool arc)2801         public static string GetContainerName(string fileName, bool arc)
2802         {
2803             string result = Path.GetFileNameWithoutExtension(fileName);
2804             if (arc) {
2805                 result += ".zip";
2806             } else {
2807                 result += Path.DirectorySeparatorChar;
2808             }
2809             return result;
2810         }
2811 
FileCanBeArchived(string fileName)2812         public static bool FileCanBeArchived(string fileName)
2813         {
2814             GDMMultimediaFormat fileFmt = GDMFileReference.RecognizeFormat(fileName);
2815 
2816             FileInfo info = new FileInfo(fileName);
2817             double fileSize = (((double)info.Length / 1024) / 1024); // mb
2818 
2819             MultimediaKind mKind = GetMultimediaKind(fileFmt);
2820             return ((mKind == MultimediaKind.mkImage || mKind == MultimediaKind.mkText) && fileSize <= 10);
2821         }
2822 
2823         #endregion
2824 
2825         #region Names processing
2826 
GetFamilyString(GDMTree tree, GDMFamilyRecord family)2827         public static string GetFamilyString(GDMTree tree, GDMFamilyRecord family)
2828         {
2829             if (family == null)
2830                 throw new ArgumentNullException("family");
2831 
2832             return GetFamilyString(tree, family, LangMan.LS(LSID.LSID_UnkMale), LangMan.LS(LSID.LSID_UnkFemale));
2833         }
2834 
GetFamilyString(GDMTree tree, GDMFamilyRecord family, string unkHusband, string unkWife)2835         public static string GetFamilyString(GDMTree tree, GDMFamilyRecord family, string unkHusband, string unkWife)
2836         {
2837             if (tree == null)
2838                 throw new ArgumentNullException("tree");
2839 
2840             if (family == null)
2841                 throw new ArgumentNullException("family");
2842 
2843             string result = "";
2844 
2845             GDMIndividualRecord spouse = tree.GetPtrValue(family.Husband);
2846             if (spouse == null) {
2847                 if (unkHusband == null) unkHusband = "?";
2848                 result += unkHusband;
2849             } else {
2850                 result += GetNameString(spouse, true, false);
2851             }
2852 
2853             result += " - ";
2854 
2855             spouse = tree.GetPtrValue(family.Wife);
2856             if (spouse == null) {
2857                 if (unkWife == null) unkWife = "?";
2858                 result += unkWife;
2859             } else {
2860                 result += GetNameString(spouse, true, false);
2861             }
2862 
2863             return result;
2864         }
2865 
GetNickString(GDMIndividualRecord iRec)2866         public static string GetNickString(GDMIndividualRecord iRec)
2867         {
2868             if (iRec == null)
2869                 throw new ArgumentNullException("iRec");
2870 
2871             string result = (iRec.PersonalNames.Count > 0) ? iRec.PersonalNames[0].Pieces.Nickname : string.Empty;
2872             return result;
2873         }
2874 
2875         // TODO: what if you want to display only a surname that is missing?
GetFmtSurname(GDMSex iSex, GDMPersonalName personalName, string defSurname)2876         public static string GetFmtSurname(GDMSex iSex, GDMPersonalName personalName, string defSurname)
2877         {
2878             if (personalName == null)
2879                 throw new ArgumentNullException("personalName");
2880 
2881             string result;
2882 
2883             WomanSurnameFormat wsFmt = GlobalOptions.Instance.WomanSurnameFormat;
2884             if (iSex == GDMSex.svFemale && wsFmt != WomanSurnameFormat.wsfNotExtend) {
2885                 string marriedSurname = personalName.Pieces.MarriedName;
2886                 switch (wsFmt) {
2887                     case WomanSurnameFormat.wsfMaiden_Married:
2888                         result = defSurname;
2889                         if (marriedSurname.Length > 0) {
2890                             if (result.Length > 0) result += " ";
2891                             result += "(" + marriedSurname + ")";
2892                         }
2893                         break;
2894 
2895                     case WomanSurnameFormat.wsfMarried_Maiden:
2896                         result = marriedSurname;
2897                         if (defSurname.Length > 0) {
2898                             if (result.Length > 0) result += " ";
2899                             result += "(" + defSurname + ")";
2900                         }
2901                         break;
2902 
2903                     case WomanSurnameFormat.wsfMaiden:
2904                         result = defSurname; // by default GEDCOMPersonalName.Surname is maiden surname
2905                         break;
2906 
2907                     case WomanSurnameFormat.wsfMarried:
2908                         result = marriedSurname;
2909                         break;
2910 
2911                     default:
2912                         result = defSurname;
2913                         break;
2914                 }
2915             } else {
2916                 result = defSurname;
2917             }
2918 
2919             return result;
2920         }
2921 
GetNameString(GDMIndividualRecord iRec, GDMPersonalName np, bool firstSurname, bool includePieces)2922         public static string GetNameString(GDMIndividualRecord iRec, GDMPersonalName np, bool firstSurname, bool includePieces)
2923         {
2924             if (iRec == null)
2925                 throw new ArgumentNullException("iRec");
2926 
2927             if (np == null)
2928                 throw new ArgumentNullException("np");
2929 
2930             string result;
2931 
2932             string firstPart = np.FirstPart;
2933             string surname = np.Surname;
2934 
2935             surname = GetFmtSurname(iRec.Sex, np, surname);
2936 
2937             if (firstSurname) {
2938                 result = surname + " " + firstPart;
2939             } else {
2940                 result = firstPart + " " + surname;
2941             }
2942 
2943             if (includePieces) {
2944                 string nick = np.Pieces.Nickname;
2945                 if (!string.IsNullOrEmpty(nick)) result = result + " [" + nick + "]";
2946             }
2947 
2948             return result.Trim();
2949         }
2950 
GetNameString(GDMIndividualRecord iRec, bool firstSurname, bool includePieces)2951         public static string GetNameString(GDMIndividualRecord iRec, bool firstSurname, bool includePieces)
2952         {
2953             if (iRec == null)
2954                 throw new ArgumentNullException("iRec");
2955 
2956             string result;
2957             if (iRec.PersonalNames.Count > 0) {
2958                 GDMPersonalName np = iRec.PersonalNames[0];
2959                 result = GetNameString(iRec, np, firstSurname, includePieces);
2960             } else {
2961                 result = "";
2962             }
2963             return result;
2964         }
2965 
SetMarriedSurname(GDMIndividualRecord iRec, string marriedSurname)2966         public static void SetMarriedSurname(GDMIndividualRecord iRec, string marriedSurname)
2967         {
2968             if (iRec == null)
2969                 throw new ArgumentNullException("iRec");
2970 
2971             GDMPersonalName personalName;
2972             if (iRec.PersonalNames.Count <= 0) {
2973                 personalName = iRec.AddPersonalName(new GDMPersonalName());
2974             } else {
2975                 personalName = iRec.PersonalNames[0];
2976             }
2977 
2978             personalName.Pieces.MarriedName = marriedSurname.Trim();
2979         }
2980 
SetNameParts(GDMPersonalName personalName, string surname, string name, string patronymic)2981         public static void SetNameParts(GDMPersonalName personalName, string surname, string name, string patronymic)
2982         {
2983             if (personalName == null)
2984                 throw new ArgumentNullException("personalName");
2985 
2986             surname = surname.Trim();
2987             name = name.Trim();
2988             patronymic = patronymic.Trim();
2989 
2990             personalName.SetNameParts(name + " " + patronymic, surname, personalName.LastPart);
2991 
2992             var pnPieces = personalName.Pieces;
2993             pnPieces.Surname = surname;
2994             pnPieces.Given = name;
2995             pnPieces.PatronymicName = patronymic;
2996         }
2997 
GetNameParts(GDMTree tree, GDMIndividualRecord iRec, GDMPersonalName personalName, bool formatted = true)2998         public static NamePartsRet GetNameParts(GDMTree tree, GDMIndividualRecord iRec, GDMPersonalName personalName, bool formatted = true)
2999         {
3000             if (iRec == null)
3001                 throw new ArgumentNullException("iRec");
3002 
3003             ICulture culture = DefineCulture(tree, personalName);
3004 
3005             NamePartsRet nameParts = culture.GetNameParts(personalName);
3006 
3007             if (formatted) {
3008                 nameParts.Surname = GetFmtSurname(iRec.Sex, personalName, nameParts.Surname);
3009             }
3010 
3011             return nameParts;
3012         }
3013 
GetNameParts(GDMTree tree, GDMIndividualRecord iRec, bool formatted = true)3014         public static NamePartsRet GetNameParts(GDMTree tree, GDMIndividualRecord iRec, bool formatted = true)
3015         {
3016             if (iRec == null)
3017                 throw new ArgumentNullException("iRec");
3018 
3019             if (iRec.PersonalNames.Count > 0) {
3020                 return GetNameParts(tree, iRec, iRec.PersonalNames[0], formatted);
3021             } else {
3022                 return new NamePartsRet();
3023             }
3024         }
3025 
DefineCulture(GDMTree tree, GDMPersonalName personalName)3026         public static ICulture DefineCulture(GDMTree tree, GDMPersonalName personalName)
3027         {
3028             // first priority - local langID from name
3029             GDMLanguageID langID = (personalName != null) ? personalName.Language : GDMLanguageID.Unknown;
3030 
3031             // second priority - global langID from tree
3032             if (langID == GDMLanguageID.Unknown && tree != null) {
3033                 langID = tree.Header.Language;
3034             }
3035 
3036             return CulturesPool.DefineCulture(langID);
3037         }
3038 
3039         #endregion
3040     }
3041 }
3042