// Constructor
        public CListableName( CIndividualRecord ir, string surname, string firstname )
        {
            m_ir = ir;
            m_sSurname = surname;
            m_sFirstName = firstname;

            base.Text = ToString();
        }
 // Constructor.
 public CMiniTreeMap( string sName, CIndividualRecord ir, bool bLinkable, int x1, int y1, int x2, int y2 )
 {
     m_x1 = x1;
       m_y1 = y1;
       m_x2 = x2;
       m_y2 = y2;
       m_ir = ir;
       m_bLinkable = bLinkable;
       m_sName = sName;
 }
        // Recursively visit all individuals connected to this one and record them by adding their xrefs as keys in m_htVisited
        public void PruneMarkConnected( CIndividualRecord ir )
        {
            int nFamily;
            CFamilyRecord fr;

            if( ir.Visibility() == CIndividualRecord.EVisibility.Invisible )
            {
                // Don't follow splits
                return;
            }

            // Mark individual
            m_htVisited[ ir.m_xref ] = true;

            // Mark all ancestors
            nFamily = 0;
            fr = null;
            while( (fr = GetFamilyByChild( ir, nFamily++ )) != null )
            {
                CIndividualRecord irFather = GetHusband( fr );
                if( irFather != null )
                {
                    if( !m_htVisited.ContainsKey( irFather.m_xref ) )
                    {
                        PruneMarkConnected( irFather );
                    }
                }

                CIndividualRecord irMother = GetWife( fr );
                if( irMother != null )
                {
                    if( !m_htVisited.ContainsKey( irMother.m_xref ) )
                    {
                        PruneMarkConnected( irMother );
                    }
                }

                // If both parents are unknown, we can still navigate to the children. Parents appear in minitree as <unknown>, and children also appear.
                // If either mother or father is known, the marking will have happened above.
                if( irMother == null && irFather == null )
                {
                    int i = 0;
                    CIndividualRecord irChild;
                    while ((irChild = fr.GetChildByBirthDate(i)) != null)
                    {
                        i++;
                        if( m_htVisited.ContainsKey( irChild.m_xref ) == false )
                        {
                            PruneMarkConnected( irChild );
                        }
                    }
                }

            }

            // Mark all descendants
            nFamily = 0;
            fr = null;
            while( (fr = GetFamilyBySpouse( ir, nFamily++ )) != null )
            {
                int nChild = 0;
                CIndividualRecord irChild = null;
                while ((irChild = fr.GetChildByBirthDate(nChild++)) != null)
                {
                    if( irChild == null || m_htVisited.ContainsKey( irChild.m_xref ) )
                    {
                        continue;
                    }

                    PruneMarkConnected( irChild );
                }

                // Mark all spouses
                CIndividualRecord irFather = GetHusband( fr );
                if( irFather != ir && irFather != null && !m_htVisited.ContainsKey( irFather.m_xref ) )
                {
                    PruneMarkConnected( irFather );
                }
                CIndividualRecord irMother = GetWife( fr );
                if( irMother != ir && irMother != null && !m_htVisited.ContainsKey( irMother.m_xref ) )
                {
                    PruneMarkConnected( irMother );
                }
            }
        }
        // This is the main tree drawing method.
        // irSubject is the individual for whom the tree is based.
        // nTargeWidth is the width below which the layout is free to use up space to produce a nice tree.
        public ArrayList CreateMiniTree( CPaintbox paintbox, CIndividualRecord ir, string sFilename, int nTargetWidth, System.Drawing.Imaging.ImageFormat imageFormat )
        {
            // First calculate size required for tree, by iterating through individuals and building a data structure
            CMiniTreeGroup mtgParent = CreateDataStructure(ir);

            // For each individual calculate size of box required for display using helper function
            // There must be a better way to get a graphics:
            Bitmap bmp = new Bitmap( 1, 1, System.Drawing.Imaging.PixelFormat.Format24bppRgb );
            Graphics g = Graphics.FromImage( bmp );
            Font f = paintbox.m_font;

            // Record what font windows actually used, in case it chose a different one
            MainForm.s_config.m_sTreeFontName = f.Name;
            MainForm.s_config.m_fTreeFontSize = f.Size;

            // Recursively calculate sizes of other groups
            mtgParent.CalculateSize( g, f );

            g.Dispose();
            bmp.Dispose();

            // Now calculate sizes of each row
            // Total width includes irSubject, their spouses and their siblings.
            // Total height is always three generations

            // Now calculate how best to position each generation
            // Calculate the width of each generation
            // There are three cases : frParents widest, siblings widest, children widest
            // Plus two aims : minimise total width, get offspring centred under frParents.
            // If nTargetWidth is exceeded simply because of number of individuals in one row, that
            // row's width becomes the new target width.
            // If nTargetWidth is exceeded otherwise, minimising total width becomes the priority
            mtgParent.CalculateLayout( 0f, 0f );
            mtgParent.Compress();

            RectangleF rect = mtgParent.GetExtent();
            m_sizeTotal = new SizeF( rect.Width, rect.Height );
            mtgParent.Translate( -rect.Left, -rect.Top );

            // Calculate offset for each row
            // Can't do this so create a new bitmap: bmp.Width = totalSize.Width;
            // Can't do this so create a new bitmap: bmp.Height = totalSize.Height;
            int nTotalWidth = (int)(m_sizeTotal.Width+1.0f);
            int nTotalHeight = (int)(m_sizeTotal.Height+1.0f);
            bmp = new Bitmap( nTotalWidth, nTotalHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb );
            g = Graphics.FromImage( bmp );

            // Do background fill
            if( MainForm.s_config.m_bFakeMiniTreeTransparency && paintbox.m_brushFakeTransparency != null )
            {
                g.FillRectangle( paintbox.m_brushFakeTransparency, 0, 0, nTotalWidth, nTotalHeight );
            }
            else if( imageFormat == ImageFormat.Gif && paintbox.m_brushBgGif != null )
            {
                g.FillRectangle( paintbox.m_brushBgGif, 0, 0, nTotalWidth, nTotalHeight );
            }

            ArrayList alMap = new ArrayList();
            mtgParent.DrawBitmap( paintbox, g, alMap );

            // Save the bitmap
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.eDL_Note, "Saving mini tree as " + sFilename );

            if( System.IO.File.Exists( sFilename ) )
            {
                // Delete any current file
                System.IO.File.SetAttributes( sFilename, System.IO.FileAttributes.Normal );
                System.IO.File.Delete( sFilename );
            }

            // Save using FileStream to try to avoid crash (only seen by customers)
            System.IO.FileStream fs = new System.IO.FileStream( sFilename, System.IO.FileMode.Create );
            bmp.Save( fs, imageFormat );
            fs.Close();

            g.Dispose();
            bmp.Dispose();

            // For gifs we need to reload and set transparency colour
            if( imageFormat == ImageFormat.Gif && !MainForm.s_config.m_bFakeMiniTreeTransparency )
            {
                Image imageGif;
                ColorPalette colorpalette;
                imageGif = Image.FromFile( sFilename );
                colorpalette = imageGif.Palette;

                // Creates a new GIF image with a modified colour palette
                if (colorpalette != null )
                {
                    // Create a new 8 bit per pixel image
                    Bitmap bm=new Bitmap(imageGif.Width,imageGif.Height,PixelFormat.Format8bppIndexed);

                    // Get it's palette
                    ColorPalette colorpaletteNew = bm.Palette;

                    // Copy all the entries from the old palette removing any transparency
                    int n=0;
                    foreach(Color c in colorpalette.Entries)
                    {
                        colorpaletteNew.Entries[n++]=Color.FromArgb(255,c);
                    }

                    // Now to copy the actual bitmap data
                    // Lock the source and destination bits
                    BitmapData src = ((Bitmap)imageGif).LockBits(new Rectangle(0,0,imageGif.Width,imageGif.Height),ImageLockMode.ReadOnly,imageGif.PixelFormat);
                    BitmapData dst = bm.LockBits(new Rectangle(0,0,bm.Width,bm.Height),ImageLockMode.WriteOnly,bm.PixelFormat);

                    // Uses pointers so we need unsafe code.
                    // The project is also compiled with /unsafe
                    byte backColor = 0;
                    unsafe
                    {
                        backColor = ((byte *)src.Scan0.ToPointer())[0]; // Assume transparent colour appears as first pixel.

                        byte* src_ptr = ((byte *)src.Scan0.ToPointer());
                        byte* dst_ptr = ((byte *)dst.Scan0.ToPointer());
                        // May be useful: System.Runtime.InteropServices.Marshal.Copy(IntPtr source, byte[], destination, int start, int length)
                        // May be useful: System.IO.MemoryStream ms = new System.IO.MemoryStream(src_ptr);
                        int width = imageGif.Width;
                        int src_stride = src.Stride - width;
                        int dst_stride = dst.Stride - width;
                        for(int y=0;y<imageGif.Height;y++)
                        {
                            // Can't convert IntPtr to byte[]: Buffer.BlockCopy( src_ptr, 0, dst_ptr, 0, width );
                            int x = width;
                            while( x-- > 0 )
                            {
                                *dst_ptr++ = *src_ptr++;
                            }
                            src_ptr += src_stride;
                            dst_ptr += dst_stride;
                        }
                    }

                    // Set the newly selected transparency
                    colorpaletteNew.Entries[(int)backColor]=Color.FromArgb(0,Color.Magenta);

                    // Re-insert the palette
                    bm.Palette=colorpaletteNew;

                    // All done, unlock the bitmaps
                    ((Bitmap)imageGif).UnlockBits(src);
                    bm.UnlockBits(dst);

                    imageGif.Dispose();

                    // Set the new image in place
                    imageGif=bm;
                    colorpalette=imageGif.Palette;

                    LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.eDL_Note, "Re-saving mini gif as " + sFilename );

                    imageGif.Save( sFilename, imageFormat );
                }
            }

            return alMap;
        }
 // Gets the n'th child in the fr, or returns the default individual if first child requested and no fr.
 private static CIndividualRecord GetChild(CFamilyRecord fr, int nChild, CIndividualRecord irDefault )
 {
     CIndividualRecord irChild = null;
     if (fr != null)
     {
         // The ordering of children in the tree can be selected to be the same as it is in the GEDCOM file. This
         // is because the file should be ordered as the user chose to order the fr when entering the data in
         // their fr history app, regardless of actual birth dates.
         if (MainForm.s_config.m_bKeepSiblingOrder)
         {
             irChild = fr.GetChildByPositionInFile(nChild);
         }
         else
         {
             irChild = fr.GetChild(nChild);
         }
     }
     else
     {
         // Return the default individual as first and only child of fr.
         if (nChild == 0)
         {
             irChild = irDefault;
         }
     }
     return irChild;
 }
 // Add a box for the individual to the specified group.
 private static CMiniTreeIndividual AddToGroup(CIndividualRecord ir, CMiniTreeGroup mtg, bool bSpouse)
 {
     CMiniTreeIndividual mti = null;
     if (Exists(ir))
     {
         CBoxText boxtext = new CBoxText(ir);
         mti = mtg.AddIndividual(ir, boxtext.m_sFirstName, boxtext.m_sSurname, boxtext.m_sDate, true, false, false, boxtext.m_bConcealed, bSpouse);
     }
     else
     {
         mti = mtg.AddIndividual(null, "", MainForm.s_config.m_sUnknownName, " ", false, false, false, false, bSpouse);
     }
     return mti;
 }
        // Attaches sub-items to a list item (before the list item is added to the list)
        private void SetIndividualSubItems(CListableBool lbItem, CIndividualRecord ir, bool bCheckBoxes)
        {
            // Save checkbox state because SubItems.Clear() clears item.Text and item.Checked as well, so replace old value after calling Clear().
            bool bWasChecked = lbItem.Checked;
            string sItemText = lbItem.Text;
            lbItem.SubItems.Clear();
            lbItem.Text = sItemText;
            lbItem.Checked = bWasChecked;

            // If the list view has check boxes, the item is for the checkbox.
            // Otherwise the item is for the name, and so the sub items won't include the name.
            if (bCheckBoxes)
            {
                string sSurname = "";
                string sFirstName = "";
                s_config.CapitaliseName(ir.Name, ref sFirstName, ref sSurname);
                if (ir.NameSuffix != null && ir.NameSuffix != "")
                {
                    sFirstName += ", " + ir.NameSuffix;
                }
                lbItem.SubItems.Add(new CListableName(ir, sSurname, sFirstName));
            }

            CPGQualifiedDate birthDate = ir.BirthDate;
            if (birthDate != null)
            {
                lbItem.SubItems.Add(new CListableYear(birthDate.m_date));
            }
            else
            {
                lbItem.SubItems.Add(new CListableYear(null));
            }

            CPGQualifiedDate deathDate = ir.DeathDate;
            if (deathDate != null)
            {
                lbItem.SubItems.Add(new CListableYear(deathDate.m_date));
            }
            else
            {
                lbItem.SubItems.Add(new CListableYear(null));
            }

            lbItem.SubItems.Add(new CListableString(ir.m_xref));
            lbItem.SubItems.Add(new CListableString(ir.UserReferenceNumber(0)));

            int nVisiblePics = ir.CountVisibleMFRs();
            int nTotalPics = ir.CountAllMFRs();
            if (nVisiblePics != nTotalPics)
            {
                lbItem.SubItems.Add(new CListableNumber(nVisiblePics, String.Format("{0}/{1}", nVisiblePics, nTotalPics)));
            }
            else
            {
                lbItem.SubItems.Add(new CListableNumber(nTotalPics, String.Format("{0}", nTotalPics)));
            }
        }
        // Creates link HTML for the individual e.g. <a href="indiI1.html">Fred Bloggs</a>
        protected static string MakeLink( CIndividualRecord ir )
        {
            string sName = ir.Name;
              string sDummy = "";
              if (sName == "")
              {
              sName = MainForm.s_config.m_sUnknownName;
              }
              else if ((ir.Visibility() != CIndividualRecord.EVisibility.Visible) && !MainForm.s_config.m_bUseWithheldNames)
              {
              sName = MainForm.s_config.m_sConcealedName;
              }
              else
              {
              sName = MainForm.s_config.CapitaliseName(sName, ref sDummy, ref sDummy);
              }

              return MakeLink( ir, sName );
        }
 // Constructor
 public CChild( int nPositionInFile, CIndividualRecord ir )
 {
     m_nPositionInFile = nPositionInFile;
     m_ir = ir;
 }
        // Gets the n'th family record that the given individual is a spouse in
        public CFamilyRecord GetFamilyBySpouse( CIndividualRecord ir, int n )
        {
            if( ir.m_alSpouseToFamilyLinks == null )
            {
                return null;
            }

            if( n < 0 || n >= ir.m_alSpouseToFamilyLinks.Count )
            {
                return null;
            }

            CSpouseToFamilyLink sfl = (CSpouseToFamilyLink)(ir.m_alSpouseToFamilyLinks[ n ]);
            if( sfl == null )
            {
                return null;
            }

            return GetFamilyRecord( sfl.m_xrefFam );
        }
        // Gets the n'th family record that the given individual is a child in
        public CFamilyRecord GetFamilyByChild( CIndividualRecord ir, int n )
        {
            if( ir.m_alChildToFamilyLinks == null )
            {
                return null;
            }

            if( n < 0 || n >= ir.m_alChildToFamilyLinks.Count )
            {
                return null;
            }

            CChildToFamilyLink cfl = (CChildToFamilyLink)(ir.m_alChildToFamilyLinks[ n ]);
            if( cfl == null )
            {
                return null;
            }

            return GetFamilyRecord( cfl.m_xrefFam );
        }
        // Returns an ordered array of all family records that the given individual was a spouse in
        public ArrayList GetFamilyArray( CIndividualRecord ir )
        {
            if( ir == null )
            {
                return null;
            }

            ArrayList alFamily = new ArrayList();

            int nFamilies = 0;
            CFamilyRecord fr;
            while( (fr = GetFamilyBySpouse( ir, nFamilies )) != null )
            {
                alFamily.Add( fr );
                nFamilies++;
            }

            alFamily.Sort( new CFamilyRecord.FamilyComparer() );

            return alFamily;
        }
        // Restricts descendants, and if required, their spouses and their spouses ancestors.
        private void PruneDescendants( CIndividualRecord ir, bool bPruneSpouses, bool bExclude )
        {
            int nFamily = 0;
            CFamilyRecord fr = null;
            while( (fr = GetFamilyBySpouse( ir, nFamily++ )) != null )
            {
                int nChild = 0;
                CIndividualRecord irChild = null;
                while( (irChild = fr.GetChildByBirthDate( nChild++ )) != null )
                {
                    if( irChild == null || m_htVisited.ContainsKey( irChild.m_xref ) )
                    {
                        continue;
                    }

                    if( bExclude )
                    {
                        if( !irChild.Restricted )
                        {
                            if( MainForm.m_mainForm != null )
                            {
                                MainForm.m_mainForm.m_nPruneExcluded++;
                            }
                            irChild.Restricted = true;
                        }
                    }
                    else
                    {
                        if( irChild.Restricted )
                        {
                            if( MainForm.m_mainForm != null )
                            {
                                MainForm.m_mainForm.m_nPruneIncluded++;
                            }
                            irChild.Restricted = false;
                        }
                    }
                    m_htVisited[ irChild.m_xref ] = true;
                    PruneDescendants( irChild, bPruneSpouses, bExclude );
                }

                if( bPruneSpouses )
                {
                    CIndividualRecord irFather = GetHusband( fr );
                    if( irFather != ir && irFather != null && !m_htVisited.ContainsKey( irFather.m_xref ) )
                    {
                        if( bExclude )
                        {
                            if( !irFather.Restricted )
                            {
                                if( MainForm.m_mainForm != null )
                                {
                                    MainForm.m_mainForm.m_nPruneExcluded++;
                                }
                                irFather.Restricted = true;
                            }
                        }
                        else
                        {
                            if( irFather.Restricted )
                            {
                                if( MainForm.m_mainForm != null )
                                {
                                    MainForm.m_mainForm.m_nPruneIncluded++;
                                }
                                irFather.Restricted = false;
                            }
                        }
                        m_htVisited[ irFather.m_xref ] = true;
                        PruneAncestors( irFather, bExclude );
                        PruneDescendants( irFather, false );
                    }
                    CIndividualRecord irMother = GetWife( fr );
                    if( irMother != ir && irMother != null && !m_htVisited.ContainsKey( irMother.m_xref ) )
                    {
                        if( bExclude )
                        {
                            if( !irMother.Restricted )
                            {
                                if( MainForm.m_mainForm != null )
                                {
                                    MainForm.m_mainForm.m_nPruneExcluded++;
                                }
                                irMother.Restricted = true;
                            }
                        }
                        else
                        {
                            if( irMother.Restricted )
                            {
                                if( MainForm.m_mainForm != null )
                                {
                                    MainForm.m_mainForm.m_nPruneIncluded++;
                                }
                                irMother.Restricted = false;
                            }
                        }
                        m_htVisited[ irMother.m_xref ] = true;
                        PruneAncestors( irMother, bExclude );
                        PruneDescendants( irMother, false );
                    }
                }
            }
        }
        // Restricts their siblings and their ancestors and their ancestors spouses
        private void PruneAncestors( CIndividualRecord ir, bool bPruneSpouses, bool bExclude )
        {
            int nFamily = 0;
            CFamilyRecord fr = null;
            while( (fr = GetFamilyByChild( ir, nFamily++ )) != null )
            {
                CIndividualRecord irFather = GetHusband( fr );
                if( irFather != null )
                {
                    if( !m_htVisited.ContainsKey( irFather.m_xref ) )
                    {
                        PruneAncestors( irFather, bExclude );
                    }
                    if( bExclude )
                    {
                        if( !irFather.Restricted )
                        {
                            if( MainForm.m_mainForm != null )
                            {
                                MainForm.m_mainForm.m_nPruneExcluded++;
                            }
                            irFather.Restricted = true;
                        }
                    }
                    else
                    {
                        if( irFather.Restricted )
                        {
                            if( MainForm.m_mainForm != null )
                            {
                                MainForm.m_mainForm.m_nPruneIncluded++;
                            }
                            irFather.Restricted = false;
                        }
                    }
                    m_htVisited[ irFather.m_xref ] = true;
                }

                CIndividualRecord irMother = GetWife( fr );
                if( irMother != null )
                {
                    if( !m_htVisited.ContainsKey( irMother.m_xref ) )
                    {
                        PruneAncestors( irMother, bExclude );
                    }
                    if( bExclude )
                    {
                        if( !irMother.Restricted )
                        {
                            if( MainForm.m_mainForm != null )
                            {
                                MainForm.m_mainForm.m_nPruneExcluded++;
                            }
                            irMother.Restricted = true;
                        }
                    }
                    else
                    {
                        if( irMother.Restricted )
                        {
                            if( MainForm.m_mainForm != null )
                            {
                                MainForm.m_mainForm.m_nPruneIncluded++;
                            }
                            irMother.Restricted = false;
                        }
                    }
                    m_htVisited[ irMother.m_xref ] = true;
                }
            }
        }
        // Restricts presentation of sources connected with an individual (for when individual themselves are marked as restricted)
        public void RestrictAssociatedSources( CIndividualRecord ir )
        {
            // Restrict sources connected with individual directly
            foreach( CSourceCitation sc in ir.m_alSourceCitations )
            {
                RestrictSource( sc, true );
            }

            // Restrict sources connected with name
            foreach( CPersonalNameStructure pns in ir.m_alPersonalNameStructures )
            {
                if( pns.m_personalNamePieces != null )
                {
                    foreach( CSourceCitation sc in pns.m_personalNamePieces.m_alSourceCitations )
                    {
                        RestrictSource( sc, true );
                    }
                }
            }

            // Restrict sources connected with events
            foreach( CIndividualEventStructure ies in ir.m_alIndividualEventStructures )
            {
                if( ies.m_eventDetail != null )
                {
                    foreach( CSourceCitation sc in ies.m_eventDetail.m_alSourceCitations )
                    {
                        RestrictSource( sc, true );
                    }
                }
            }

            // Restrict sources connected with m_ldsIndividualOrdinances
            foreach( CLdsOrdinance lo in ir.m_alLdsIndividualOrdinances )
            {
                foreach( CSourceCitation sc in lo.m_alSourceCitations )
                {
                    RestrictSource( sc, true );
                }
            }

            // Restrict sources connected with m_associationStructures
            foreach( CAssociationStructure ass in ir.m_alAssociationStructures )
            {
                foreach( CSourceCitation sc in ass.m_alSourceCitations )
                {
                    RestrictSource( sc, true );
                }
            }
        }
 // Returns true if the given individual is the female parent in this family
 public bool IsWife(CIndividualRecord ir)
 {
     return (ir != null && m_xrefWife == ir.m_xref);
 }
 // Returns a string to use as a sFilename for this individual's HTML page.
 // The string is just the sFilename, not a fully qualified path.
 protected static string GetIndividualHTMLFilename( CIndividualRecord ir )
 {
     string sRelativeFilename = String.Concat( "indi", ir.m_xref, ".", MainForm.s_config.m_sHtmlExtension );
       if( MainForm.s_config.m_bUserRecFilename )
       {
     if( ir.m_alUserReferenceNumbers.Count > 0 )
     {
       CUserReferenceNumber urn = (CUserReferenceNumber)ir.m_alUserReferenceNumbers[0];
       string sFilenameUserRef = EscapeFilename(urn.m_sUserReferenceNumber);
       if( sFilenameUserRef.Length > 0 )
       {
     sRelativeFilename = String.Concat( "indi", sFilenameUserRef, ".", MainForm.s_config.m_sHtmlExtension );
       }
     }
       }
       return sRelativeFilename;
 }
        // Parser
        public static CIndividualRecord Parse( CGedcom gedcom, int nLevel )
        {
            CGedcomLine gedcomLine;
            bool bParsingFinished;

            // Temporary holders for class members.
            CPersonalNameStructure personalNameStructure;
            CIndividualEventStructure individualEventStructure;
            CLdsOrdinance ldsIndividualOrdinance;
            CChildToFamilyLink childToFamilyLink;
            CSpouseToFamilyLink spouseToFamilyLink;
            CAssociationStructure associationStructure;
            CNoteStructure noteStructure;
            CSourceCitation sourceCitation;
            CMultimediaLink multimediaLink;
            ArrayList alAliases = new ArrayList();

            // Without an xref header, we can't continue
            if ((gedcomLine = gedcom.GetLine(nLevel, "INDI")) == null)
            {
                // Not one of us
                return null;
            }

            CIndividualRecord ir = new CIndividualRecord( gedcom );

            ir.m_xref = gedcomLine.XrefID;
            gedcom.IncrementLineIndex(1);

            do
            {
                bParsingFinished = true;

                // Family Historian tag _FLGS
                if( (gedcomLine = gedcom.GetLine(nLevel+1, "_FLGS")) != null )
                {
                    gedcom.IncrementLineIndex(1);
                    if( (gedcomLine = gedcom.GetLine(nLevel+2, "__LIVING")) != null )
                    {
                        ir.m_sStillLiving = gedcomLine.LineItem;
                        gedcom.IncrementLineIndex(1);
                    }
                    bParsingFinished = false;
                }
                // Let Record have a go at parsing the rest
                else if( ir.ParseRecord( gedcom, nLevel ) )
                {
                    bParsingFinished = false;
                    continue;
                }
                else if( (gedcomLine = gedcom.GetLine( nLevel+1, "RESN" )) != null )
                {
                    ir.m_sRestrictionNotice = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (personalNameStructure = CPersonalNameStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alPersonalNameStructures.Add( personalNameStructure );
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "SEX")) != null )
                {
                    ir.m_sSexValue = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (individualEventStructure = CIndividualEventStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alIndividualEventStructures.Add( individualEventStructure );
                    bParsingFinished = false;

                    if( individualEventStructure.Type == "ADOP" )
                    {
                        gedcom.m_alAdoptedIndividuals.Add( ir );
                    }
                }
                else if( (ldsIndividualOrdinance = CLdsOrdinance.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alLdsIndividualOrdinances.Add( ldsIndividualOrdinance );
                    bParsingFinished = false;
                }
                else if( (childToFamilyLink = CChildToFamilyLink.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alChildToFamilyLinks.Add( childToFamilyLink );
                    bParsingFinished = false;
                }
                else if( (spouseToFamilyLink = CSpouseToFamilyLink.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alSpouseToFamilyLinks.Add( spouseToFamilyLink );
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "SUBM")) != null )
                {
                    ir.m_alXrefSubms.Add( gedcomLine.LinePointer );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (associationStructure = CAssociationStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alAssociationStructures.Add( associationStructure );
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "ALIA")) != null )
                {
                    if( gedcomLine.LinePointer != null )
                    {
                        ir.m_alXrefAlias.Add( gedcomLine.LinePointer );
                        gedcom.IncrementLineIndex(1);
                        bParsingFinished = false;
                    }
                    else if( gedcomLine.LineItem != null && gedcomLine.LineItem.Length > 0 )
                    {
                        alAliases.Add( gedcomLine.LineItem );
                        gedcom.IncrementLineIndex(1);
                        bParsingFinished = false;
                    }
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "ANCI")) != null )
                {
                    ir.m_alXrefAncis.Add( gedcomLine.LinePointer );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "DESI")) != null )
                {
                    ir.m_alXrefDesis.Add( gedcomLine.LinePointer );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "RFN")) != null )
                {
                    ir.m_sPermanentRecordFileNumber = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "AFN")) != null )
                {
                    ir.m_sAncestralFileNumber = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (sourceCitation = CSourceCitation.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alSourceCitations.Add( sourceCitation );
                    bParsingFinished = false;
                }
                else if( (multimediaLink = CMultimediaLink.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alMultimediaLinks.Add( multimediaLink );
                    bParsingFinished = false;
                }
                else if( (noteStructure = CNoteStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alNoteStructures.Add( noteStructure );
                    bParsingFinished = false;
                }
                else if( ( gedcomLine = gedcom.GetLine()).Level > nLevel )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, "Unknown tag :" );
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, gedcomLine.ToString() );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
            }
            while( !bParsingFinished );

            // Workaround for GEDCOM that incorrectly contains this: ALIA Fred /Bloggs/ (instead of ALIA @I12@)
            if( alAliases.Count > 0 )
            {
                foreach( string sAlias in alAliases )
                {
                    sAlias.Trim();
                    if( sAlias.Length > 0 )
                    {
                        CPersonalNameStructure pns = new CPersonalNameStructure(gedcom);
                        pns.m_sNamePersonal = sAlias;
                        ir.m_alPersonalNameStructures.Add( pns );
                    }
                }
            }

            return ir;
        }
        // Creates link HTML for the individual e.g. <a href="indiI1.html">Next Child</a>. Uses name provided by caller.
        protected static string MakeLink( CIndividualRecord ir, string sName )
        {
            string sLink;
              if( ir.Visibility() == CIndividualRecord.EVisibility.Invisible )
              {
            // TODO: Why are we linking to invisible people?
            sLink = CCreator.EscapeHTML(sName, true);
              }
              else
              {
            sLink = String.Concat("<a href=\"", GetIndividualHTMLFilename(ir), "\">", CCreator.EscapeHTML(sName, false), "</a>");
              }

              return sLink;
        }
 // Constructor
 public CCreatorRecordIndividual( CGedcom gedcom, IProgressCallback progress, string sW3cfile, CIndividualRecord ir, CCreatorIndexIndividuals indiIndexCreator, CPaintbox paintbox )
     : base(gedcom, progress, sW3cfile)
 {
     m_ir = ir;
       m_indiIndexCreator = indiIndexCreator;
       m_paintbox = paintbox;
       m_htFirstFoundEvent = new Hashtable();
       m_sBirthdaySourceRefs = "";
       m_sDeathdaySourceRefs = "";
       m_sNameTitle = "";
       m_bUnknownName = false;
       m_sName = m_ir.Name;
       m_sNameSuffix = m_ir.NameSuffix;
       m_sFirstName = "";
       m_sSurname = "";
       m_sOccupation = "";
       m_bConcealed = m_ir.Visibility() == CIndividualRecord.EVisibility.Restricted;
       m_alEventList = new ArrayList();
       m_alAttributeList = new ArrayList();
       m_alReferenceList = new ArrayList();
       m_alOccupations = new ArrayList();
       m_sPreviousChildLink = "";
       m_sNextChildLink = "";
       m_alOtherNames = new ArrayList();
       m_qdateInferredBirthday = null;
       m_dateActualBirthday = null;
       m_qdateInferredDeathday = null;
       m_dateActualDeathday = null;
       m_alParents = new ArrayList();
 }
 // Constructor
 public CMiniTreeIndividual( CIndividualRecord ir, string sFirstNames, string sSurname, string sDate, bool bCreateLink, bool bCreateStalk, bool bHighlight, bool bConcealed, bool bShade, bool bConserveWidth )
 {
     m_ir = ir;
     m_sFirstnames = sFirstNames;
     m_sSurname = sSurname;
     m_sDate = sDate;
     m_bLinkable = bCreateLink;
     m_bHighlight = bHighlight;
     m_bConcealed = bConcealed;
     m_bChild = bCreateStalk;
     m_bShade = bShade;
     m_fFirstnamesPad = 0f;
     m_fSurnamePad = 0f;
     m_fDatePad = 0f;
     m_bConserveWidth = bConserveWidth;
     m_sizeText = new Size();
 }
        // Adds the marriage associated with the fr record to the list of events. Also adds irSubject death if within this person's lifetime.
        private void AddMarriage( CIndividualRecord spouse, string spouseLink, CFamilyRecord fr )
        {
            // Find wedding date
              if (spouse != null)
              {
            string sourceRefs = AddSpouseDeath(spouse, spouseLink);

            CPGDate marriageDate;
            string marriageNote;
            string marriagePlace;
            sourceRefs = AddMarriageEvent(fr, sourceRefs, out marriageDate, out marriageNote, out marriagePlace);

            marriageNote = BuildMaritalStatusNote(fr, marriageNote);

            // Add fr record notes to marriage event
            if (fr.m_alNoteStructures != null)
            {
              foreach (CNoteStructure ns in fr.m_alNoteStructures)
              {
            if (ns.Text != null && ns.Text.Length > 0)
            {
              if (marriageNote != "")
              {
                marriageNote += "\n";
              }
              if (MainForm.s_config.m_bObfuscateEmails)
              {
                marriageNote += ObfuscateEmail(ns.Text);
              }
              else
              {
                marriageNote += ns.Text;
              }
            }
              }
            }
            string marriedString = "married ";
            if (fr.WereTheyReallyMarried() == false)
            {
              marriedString = "partner of ";
            }
            if (marriageDate != null)
            {
              CIEvent iEvent = new CIEvent(marriageDate, "_MARRIAGE", String.Concat(marriedString, spouseLink, marriagePlace, ".", sourceRefs), "", marriageNote, true, MainForm.s_config.m_bCapitaliseEventDescriptions);
              m_alEventList.Add(iEvent);
            }
            // else its an attribute.
            else
            {
              CIEvent iEvent = new CIEvent(marriageDate, "_MARRIAGE", String.Concat(marriedString, spouseLink, marriagePlace, ".", sourceRefs), "", marriageNote, true, MainForm.s_config.m_bCapitaliseEventDescriptions);
              // Marriages go at the front of the list so that they appear first in "Other facts"
              m_alAttributeList.Insert(0, iEvent);
            }

              } // end if (irSubject != null )
        }
        // Calculate size required for tree by iterating through individuals and building a data structure.
        protected CMiniTreeGroup CreateDataStructure( CIndividualRecord irSubject )
        {
            // Add subject's frParents
            CFamilyRecord frParents = m_gedcom.GetFamilyByChild(irSubject, 0);
            CMiniTreeGroup mtgParents = new CMiniTreeGroup();
            CMiniTreeIndividual mtiFather = null;
            if( frParents != null )
            {
                mtiFather = AddToGroup(frParents.m_xrefHusband, mtgParents, false);
            }

            // Create a group for the subejct and their siblings.
            CMiniTreeGroup mtgSiblings = new CMiniTreeGroup();

            // Keeps count of subject's siblings (including subject)
            int nSiblings = 0;

            // Keeps track of last added sibling, to hook up to next added sibling.
            CMiniTreeIndividual mtiRightmostSibling = null;

            // Keeps track of last added child, to hook up to next added child.
            CMiniTreeIndividual mtiRightmostChild = null;

            // For each sibling (including the subject)
            for(;;)
            {
                CIndividualRecord irSibling = GetChild(frParents, nSiblings, irSubject);
                if (null == irSibling)
                {
                    break;
                }

                if (irSibling == irSubject)
                {
                    // Add spouses and children of subject, (and subject too, if we need to put wife after them.)
                    CMiniTreeGroup mtgOffspring = null;
                    bool bAddedSubject = false;
                    int nSpouses = 0;
                    CMiniTreeGroup.ECrossbar ecbCrossbar = CMiniTreeGroup.ECrossbar.eCB_Solid;
                    ArrayList alFamily = m_gedcom.GetFamilyArray(irSubject);

                    foreach( CFamilyRecord fr in alFamily )
                    {
                        CIndividualRecord irSpouse = fr.GetSpouse(irSubject);

                        if (!fr.IsHusband(irSubject))
                        {
                            mtiRightmostSibling = AddToGroup(irSpouse, mtgSiblings, true);
                            // Subject is female so all but last husband have dotted bars
                            ecbCrossbar = CMiniTreeGroup.ECrossbar.eCB_DottedLeft;
                        }
                        else if (Exists(irSubject) && !bAddedSubject)
                        {
                            // Subject is male, so need to put them in now, before their children.
                            // (Otherwise they get added as a regular sibling later)
                            CBoxText boxtext = new CBoxText(irSubject);
                            mtiRightmostSibling = mtgSiblings.AddIndividual(irSubject, boxtext.m_sFirstName, boxtext.m_sSurname, boxtext.m_sDate, false, frParents != null, true, boxtext.m_bConcealed, false);

                            // To stop subject being added as regular sibling.
                            bAddedSubject = true;
                        }

                        int nGrandchildren = 0;
                        CIndividualRecord irGrandchild = null;

                        // If we have already added an offspring box (from previous marriage) need connect this box to it as its right box.
                        if (mtgOffspring != null)
                        {
                            mtgOffspring.RightBox = mtiRightmostSibling;
                        }

                        // Create a box for the offspring of this marriage
                        mtgOffspring = new CMiniTreeGroup();

                        // Set crossbar that joins subject to spouse according to whether this is subject's first spouse.
                        mtgOffspring.m_ecCrossbar = ecbCrossbar;

                        // Add children by this spouse
                        CMiniTreeIndividual mtiChild = null;
                        while(true)
                        {
                            // The ordering of children in the tree can be selected to be the same as it is in the GEDCOM file. This
                            // is because the file should be ordered as the user chose to order the fr when entering the data in
                            // their fr history app, regardless of actual birth dates.
                            if (MainForm.s_config.m_bKeepSiblingOrder)
                            {
                                irGrandchild = fr.GetChildByPositionInFile(nGrandchildren);
                            }
                            else
                            {
                                irGrandchild = fr.GetChild(nGrandchildren);
                            }
                            if (irGrandchild == null)
                            {
                                break;
                            }

                            if( Exists(irGrandchild) )
                            {
                                CBoxText boxtext = new CBoxText(irGrandchild);
                                mtiChild = mtgOffspring.AddIndividual(irGrandchild, boxtext.m_sFirstName, boxtext.m_sSurname, boxtext.m_sDate, true, true, false, boxtext.m_bConcealed, false);

                                // Hook this up to any children by previous spouses.
                                if( nGrandchildren==0 && mtiRightmostChild != null )
                                {
                                    mtiRightmostChild.RightObjectAlien = mtiChild;
                                    mtiChild.LeftObjectAlien = mtiRightmostChild;
                                }
                            }
                            nGrandchildren++;
                        }

                        // If we added anything, record it as the right-most child ready to hook to children by next spouse.
                        if( mtiChild != null )
                        {
                            mtiRightmostChild = mtiChild;
                        }

                        // Add the subjects children to the siblings group
                        mtgSiblings.AddGroup( mtgOffspring );

                        // Hook the offspring group to the previous sibling
                        if( mtgOffspring != null )
                        {
                            mtgOffspring.LeftBox = mtiRightmostSibling;
                        }

                        // If subject is husband then we need to add their wife now.
                        if (fr.IsWife(irSpouse))
                        {
                            ecbCrossbar = CMiniTreeGroup.ECrossbar.eCB_DottedRight;

                            // Hook up to previous rightmost sibling and set this as new rightmost sibling.
                            mtiRightmostSibling = AddToGroup(irSpouse, mtgSiblings, true);

                            // Hook the wife up as box on right of offspring box.
                            if( mtgOffspring != null )
                            {
                                mtgOffspring.RightBox = mtiRightmostSibling;
                            }
                        }

                        nSpouses++;
                    }

                    if (!bAddedSubject)
                    {
                        CBoxText boxtext = new CBoxText(irSubject);
                        CMiniTreeIndividual mtiWife = mtgSiblings.AddIndividual(irSubject, boxtext.m_sFirstName, boxtext.m_sSurname, boxtext.m_sDate, false, frParents != null, true, boxtext.m_bConcealed, false);

                        if (mtgOffspring != null)
                        {
                            mtgOffspring.m_ecCrossbar = CMiniTreeGroup.ECrossbar.eCB_Solid;
                            mtgOffspring.RightBox = mtiWife;
                        }
                    }
                }
                else if( Exists(irSibling) )
                {
                    // A sibling (not the subject).
                    CBoxText boxtext = new CBoxText(irSibling);
                    mtgSiblings.AddIndividual(irSibling, boxtext.m_sFirstName, boxtext.m_sSurname, boxtext.m_sDate, true, frParents != null, false, boxtext.m_bConcealed, false);
                }

                nSiblings++;
            }
            // End: for each child in frParents.

            // Add siblings group after subject's father
            mtgParents.AddGroup( mtgSiblings );

            // Hook up to subject's father
            mtgSiblings.LeftBox = mtiFather;

            // Add subject's mother
            if( frParents != null )
            {
                CMiniTreeIndividual mtiMother = AddToGroup(frParents.m_xrefWife, mtgParents, false);
                mtgSiblings.RightBox = mtiMother;
            }

            // Return the parents group (which contains the other family groups).
            return mtgParents;
        }
        // Extracts the data from the DEAT event for the given individual and adds it if it was an event in the current individual's lifetime.
        private string AddSpouseDeath( CIndividualRecord spouse, string spouseLink )
        {
            string sourceRefs = "";
              string place = "";
              if (spouse.Visibility() == CIndividualRecord.EVisibility.Visible)
              {
            // Record death of irSubject if within this person's lifetime
            CPGDate spouseDeathDate = null;
            foreach (CIndividualEventStructure ies in spouse.m_alIndividualEventStructures)
            {
              if (ies.Type == "DEAT")
              {
            if (ies.m_eventDetail != null)
            {
              spouseDeathDate = ies.m_eventDetail.m_dateValue;
              if (spouseDeathDate != null)
              {
                if (m_qdateInferredDeathday == null || m_qdateInferredDeathday.m_date == null || spouseDeathDate.CompareTo(m_qdateInferredDeathday.m_date) <= 0)
                {
                  if (ies.m_eventDetail.m_placeStructure != null)
                  {
                    if (ies.m_eventDetail.m_placeStructure.m_sPlaceName != "")
                      place = String.Concat(" ", MainForm.s_config.m_sPlaceWord, " ", EscapeHTML(ies.m_eventDetail.m_placeStructure.m_sPlaceName, false));
                  }

                  sourceRefs = AddSources(ref m_alReferenceList, ies.m_eventDetail.m_alSourceCitations);

                  if (spouseDeathDate != null)
                  {
                    CIEvent iEvent = new CIEvent(spouseDeathDate, "_SPOUSEDIED", String.Concat("death of ", spouseLink, place, ".", sourceRefs), "", null, false, MainForm.s_config.m_bCapitaliseEventDescriptions);
                    m_alEventList.Add(iEvent);
                  }
                  // else its an attribute.
                  else
                  {
                    CIEvent iEvent = new CIEvent(null, "_SPOUSEDIED", String.Concat("death of ", spouseLink, place, ".", sourceRefs), "", null, false, MainForm.s_config.m_bCapitaliseEventDescriptions);
                    m_alAttributeList.Add(iEvent);
                  }
                }
                break;
              }
            }
              }
            }
              }
              return sourceRefs;
        }
 // Returns true if the individual record should be added to the tree
 private static bool Exists( CIndividualRecord ir )
 {
     return (ir != null && ir.Visibility() != CIndividualRecord.EVisibility.eV_Invisible);
 }
 // Gets the parent who isn't the given individual
 public CIndividualRecord GetSpouse(CIndividualRecord ir)
 {
     CIndividualRecord irSpouse = null;
     if (ir != null)
     {
         if (m_xrefHusband == ir.m_xref)
         {
             irSpouse = Gedcom.GetIndividualRecord(m_xrefWife);
         }
         else
         {
             irSpouse = Gedcom.GetIndividualRecord(m_xrefHusband);
         }
     }
     return irSpouse;
 }
            public CBoxText(CIndividualRecord ir)
            {
                m_sFirstName = "";
                m_sSurname = "";
                m_bConcealed = (ir.Visibility() == CIndividualRecord.EVisibility.eV_Restricted);
                if (m_bConcealed && !MainForm.s_config.m_bUseWithheldNames)
                {
                    m_sFirstName = "";
                    m_sSurname = m_sName = MainForm.s_config.m_sConcealedName;
                }
                else
                {
                    if (ir.Name != "")
                    {
                        m_sName = MainForm.s_config.CapitaliseName(ir.Name, ref m_sFirstName, ref m_sSurname);
                    }
                    else
                    {
                        m_sFirstName = "";
                        m_sSurname = m_sName = MainForm.s_config.m_sUnknownName;
                    }
                }

                if (m_bConcealed)
                {
                    m_sDate = "";
                }
                else
                {
                    m_sDate = ir.LifeYears;
                }
            }
 // Returns true if the given individual is the male parent in this family
 public bool IsHusband(CIndividualRecord ir)
 {
     return (ir != null && m_xrefHusband == ir.m_xref);
 }
        // Creates a CMiniTreeIndividual for the individual specified and adds it to the group.
        // Informs neighbouring boxes about this box.
        // bCreateLink decides whether to make this box a clickable link in the HTML.
        public CMiniTreeIndividual AddIndividual( CIndividualRecord ir, string sFirstnames, string sSurname, string sDate, bool bCreateLink, bool bCreateStalk, bool bHighlight, bool bConcealed, bool bShade )
        {
            CMiniTreeIndividual mti = new CMiniTreeIndividual( ir, sFirstnames, sSurname, sDate, bCreateLink, bCreateStalk, bHighlight, bConcealed, bShade, MainForm.s_config.m_bConserveTreeWidth );

              if( m_alMembers == null )
              {
            m_alMembers = new ArrayList();
              }
              m_alMembers.Add( mti );

              m_uIndividuals++;

              if( bCreateStalk )
              {
            m_uStalkedIndividuals++;
              }

              mti.LeftObject = m_mtoLastAddedObject;

              if( m_mtoLastAddedObject != null )
              {
            m_mtoLastAddedObject.RightObject = mti;
              }

              m_mtoLastAddedObject = mti;

              return mti;
        }
 // Restricts descendants only
 public void PruneDescendants( CIndividualRecord ir, bool bExclude )
 {
     m_htVisited[ ir.m_xref ] = true;
     PruneDescendants( ir, false, bExclude );
 }