Example #1
0
 public MiniTreeGroup()
 {
     fMembers            = null;
     fSize               = new SizeF(0.0f, 0.0f);
     fParent             = null;
     fIndividuals        = 0;
     fStalkedIndividuals = 0;
     fBoxLeft            = null;
     fBoxRight           = null;
     fLastAddedObject    = null;
     fCrossbar           = ECrossbar.Solid;
 }
Example #2
0
        // Add a box for the individual to the specified group.
        private static MiniTreeIndividual AddToGroup(GDMIndividualRecord ir, MiniTreeGroup mtg)
        {
            MiniTreeIndividual mti = null;

            if (Exists(ir))
            {
                CBoxText boxtext = new CBoxText(ir);
                mti = mtg.AddIndividual(ir, boxtext.FirstName, boxtext.Surname, boxtext.Date, true, false, false, boxtext.Concealed, true);
            }
            else
            {
                mti = mtg.AddIndividual(null, "", CConfig.Instance.UnknownName, " ", false, false, false, false, true);
            }
            return(mti);
        }
Example #3
0
        // Adds a  CMiniTreeGroup to this group.
        // Informs neighbouring boxes about the group.
        public void AddGroup(MiniTreeGroup mtg)
        {
            if (mtg != null)
            {
                if (fMembers == null)
                {
                    fMembers = new List <MiniTreeObject>();
                }
                fMembers.Add(mtg);

                mtg.fParent = this;

                mtg.LeftObject = fLastAddedObject;

                if (fLastAddedObject != null)
                {
                    fLastAddedObject.RightObject = mtg;
                }

                fLastAddedObject = mtg;
            }
        }
Example #4
0
        // Calculate size required for tree by iterating through individuals and building a data structure.
        protected MiniTreeGroup CreateDataStructure(GDMIndividualRecord irSubject)
        {
            // Add subject's frParents
            GDMFamilyRecord    frParents  = fTree.GetParentsFamily(irSubject);
            MiniTreeGroup      mtgParents = new MiniTreeGroup();
            MiniTreeIndividual mtiFather  = null;

            if (frParents != null)
            {
                mtiFather = AddToGroup(fTree.GetPtrValue(frParents.Husband), mtgParents);
            }

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

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

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

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

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

                if (irSibling == irSubject)
                {
                    // Add spouses and children of subject, (and subject too, if we need to put wife after them.)
                    MiniTreeGroup           mtgOffspring  = null;
                    bool                    bAddedSubject = false;
                    int                     nSpouses      = 0;
                    MiniTreeGroup.ECrossbar ecbCrossbar   = MiniTreeGroup.ECrossbar.Solid;
                    var                     indiFamilies  = GMHelper.GetFamilyList(fTree, irSubject);

                    foreach (GDMFamilyRecord famRec in indiFamilies)
                    {
                        GDMIndividualRecord irSpouse = fTree.GetSpouseBy(famRec, irSubject);

                        if (famRec.Husband.XRef != irSubject.XRef)
                        {
                            mtiRightmostSibling = AddToGroup(irSpouse, mtgSiblings);
                            // Subject is female so all but last husband have dotted bars
                            ecbCrossbar = MiniTreeGroup.ECrossbar.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.FirstName, boxtext.Surname, boxtext.Date, false, frParents != null, true, boxtext.Concealed, false);

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

                        int nGrandchildren = 0;
                        GDMIndividualRecord 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 MiniTreeGroup();

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

                        // Add children by this spouse
                        MiniTreeIndividual mtiChild = null;
                        while ((irGrandchild = GetChild(famRec, nGrandchildren, null)) != null)
                        {
                            if (Exists(irGrandchild))
                            {
                                CBoxText boxtext = new CBoxText(irGrandchild);
                                mtiChild = mtgOffspring.AddIndividual(irGrandchild, boxtext.FirstName, boxtext.Surname, boxtext.Date, true, true, false, boxtext.Concealed, 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 (famRec.Husband.XRef == irSubject.XRef)
                        {
                            ecbCrossbar = MiniTreeGroup.ECrossbar.DottedRight;

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

                            // 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);
                        MiniTreeIndividual mtiWife = mtgSiblings.AddIndividual(irSubject, boxtext.FirstName, boxtext.Surname, boxtext.Date, false, frParents != null, true, boxtext.Concealed, false);

                        if (mtgOffspring != null)
                        {
                            mtgOffspring.fCrossbar = MiniTreeGroup.ECrossbar.Solid;
                            mtgOffspring.RightBox  = mtiWife;
                        }
                    }
                }
                else if (Exists(irSibling))
                {
                    // A sibling (not the subject).
                    CBoxText boxtext = new CBoxText(irSibling);
                    mtgSiblings.AddIndividual(irSibling, boxtext.FirstName, boxtext.Surname, boxtext.Date, true, frParents != null, true, boxtext.Concealed, false);
                }

                nSiblings++;
            }

            // 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)
            {
                MiniTreeIndividual mtiMother = AddToGroup(fTree.GetPtrValue(frParents.Wife), mtgParents);
                mtgSiblings.RightBox = mtiMother;
            }

            // Return the parents group (which contains the other family groups).
            return(mtgParents);
        }
Example #5
0
        // 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 List <MiniTreeMap> CreateMiniTree(Paintbox paintbox, GDMIndividualRecord ir, string fileName, int targetWidth, ImageFormat imageFormat)
        {
            // First calculate size required for tree, by iterating through individuals and building a data structure
            MiniTreeGroup 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, PixelFormat.Format24bppRgb);
            Graphics g   = Graphics.FromImage(bmp);
            Font     f   = paintbox.Font;

            // Record what font windows actually used, in case it chose a different one
            CConfig.Instance.TreeFontName = f.Name;
            CConfig.Instance.TreeFontSize = 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();

            fSizeTotal = 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)(fSizeTotal.Width + 1.0f);
            int nTotalHeight = (int)(fSizeTotal.Height + 1.0f);

            bmp = new Bitmap(nTotalWidth, nTotalHeight, PixelFormat.Format32bppArgb);
            g   = Graphics.FromImage(bmp);

            // Do background fill
            if (CConfig.Instance.FakeMiniTreeTransparency && paintbox.BrushFakeTransparency != null)
            {
                g.FillRectangle(paintbox.BrushFakeTransparency, 0, 0, nTotalWidth, nTotalHeight);
            }
            else if (imageFormat == ImageFormat.Gif && paintbox.BrushBgGif != null)
            {
                g.FillRectangle(paintbox.BrushBgGif, 0, 0, nTotalWidth, nTotalHeight);
            }

            List <MiniTreeMap> alMap = new List <MiniTreeMap>();

            mtgParent.DrawBitmap(paintbox, g, alMap);

            // Save the bitmap
            fLogger.WriteInfo("Saving mini tree as " + fileName);

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

            // Save using FileStream to try to avoid crash (only seen by customers)
            FileStream fs = new FileStream(fileName, 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 && !CConfig.Instance.FakeMiniTreeTransparency)
            {
                Image        imageGif;
                ColorPalette colorpalette;
                imageGif     = Image.FromFile(fileName);
                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;

                    fLogger.WriteInfo("Re-saving mini gif as " + fileName);

                    imageGif.Save(fileName, imageFormat);
                }
            }

            return(alMap);
        }