// Draws the group to the graphics instance. public override void DrawBitmap( CPaintbox paintbox, Graphics g, ArrayList alMap ) { if( m_alMembers == null ) { // Empty group return; } foreach( object obj in m_alMembers ) { if( obj is CMiniTreeGroup ) { if( ((CMiniTreeGroup)obj).m_mtiBoxLeft != null && ((CMiniTreeGroup)obj).m_mtiBoxRight != null ) { // Draw crossbar float fCrossbarLeft = ((CMiniTreeGroup)obj).m_mtiBoxLeft.TeeRight; float fCrossbarRight = ((CMiniTreeGroup)obj).m_mtiBoxRight.TeeLeft; float fCrossbarLeftGap = ((CMiniTreeGroup)obj).m_mtiBoxLeft.Right; float fCrossbarRightGap = ((CMiniTreeGroup)obj).m_mtiBoxRight.Left; float fCrossbarY = (((CMiniTreeGroup)obj).m_mtiBoxLeft.TeeCentreVert + ((CMiniTreeGroup)obj).m_mtiBoxRight.TeeCentreVert) / 2f; switch( ((CMiniTreeGroup)obj).m_ecCrossbar ) { case ECrossbar.eCB_Solid: g.DrawLine( paintbox.m_penConnector, fCrossbarLeft, fCrossbarY, fCrossbarRight, fCrossbarY ); break; case ECrossbar.eCB_DottedLeft: g.DrawLine( paintbox.m_penConnectorDotted, fCrossbarLeft, fCrossbarY, fCrossbarRightGap, fCrossbarY ); break; case ECrossbar.eCB_DottedRight: g.DrawLine( paintbox.m_penConnectorDotted, fCrossbarLeftGap, fCrossbarY, fCrossbarRight, fCrossbarY ); break; default: break; } if( ((CMiniTreeGroup)obj).m_uStalkedIndividuals > 0 ) { // Draw down to individuals // Use y coord of first individual, assuming all are at the same y coord float fIndividualY = 0f; bool bHaveIndividuals = false; foreach( CMiniTreeObject groupObj in ((CMiniTreeGroup)obj).m_alMembers ) { if( groupObj is CMiniTreeIndividual ) { fIndividualY = ((CMiniTreeIndividual)groupObj).Top; bHaveIndividuals = true; break; } } float fCrossbarCentre = (fCrossbarLeft+fCrossbarRight)/2f; if( bHaveIndividuals ) { g.DrawLine( paintbox.m_penConnector, fCrossbarCentre, fCrossbarY, fCrossbarCentre, fIndividualY ); // Connect individuals SizeF stalkMinMax = ((CMiniTreeGroup)obj).StalkMinMax; // Width irrelevant, using SizeF simply as a way to pass 2 floats: float fStalkMin = stalkMinMax.Width; // Height irrelevant, using SizeF simply as a way to pass 2 floats float fStalkMax = stalkMinMax.Height; if( fCrossbarCentre < fStalkMin ) { fStalkMin = fCrossbarCentre; } else if( fCrossbarCentre > fStalkMax ) { fStalkMax = fCrossbarCentre; } g.DrawLine( paintbox.m_penConnector, fStalkMin, fIndividualY, fStalkMax, fIndividualY ); } } } ((CMiniTreeGroup)obj).DrawBitmap( paintbox, g, alMap ); } else if( obj is CMiniTreeIndividual ) { // Draw individual box ((CMiniTreeIndividual)obj).DrawBitmap( paintbox, g, alMap ); } } }
// Draws the element to the graphics instance. public abstract void DrawBitmap( CPaintbox paintbox, Graphics g, ArrayList alMap );
// 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; }
// Draws the actual box, and adds the region of the box to the image alMap list. public override void DrawBitmap( CPaintbox paintbox, Graphics g, ArrayList alImageMap ) { SolidBrush solidbrushBg, solidbrushText; if( m_bConcealed ) { solidbrushBg = paintbox.m_brushBoxConcealed; } else if( m_bHighlight ) { solidbrushBg = paintbox.m_brushBoxHighlight; } else if( m_bShade ) { solidbrushBg = paintbox.m_brushBoxShade; } else { solidbrushBg = paintbox.m_brushBox; } if( m_bLinkable ) { solidbrushText = paintbox.m_brushTextLink; } else if( m_bConcealed ) { solidbrushText = paintbox.m_brushTextConcealed; } else { solidbrushText = paintbox.m_brushText; } g.FillRectangle( solidbrushBg, m_x+MARGIN_HORIZ, m_y+MARGIN_VERT, m_sizeText.Width, m_sizeText.Height-1f ); g.DrawRectangle( paintbox.m_penBox, m_x+MARGIN_HORIZ, m_y+MARGIN_VERT, m_sizeText.Width, m_sizeText.Height-1f ); float fTextX = m_x+MARGIN_HORIZ+PADDING_HORIZ; float fTextY = m_y+MARGIN_VERT+PADDING_VERT; if( m_bConserveWidth ) { g.DrawString( m_sFirstnames, paintbox.m_font, solidbrushText, fTextX + m_fFirstnamesPad, fTextY ); fTextY += m_sizeFirstnames.Height; g.DrawString( m_sSurname, paintbox.m_font, solidbrushText, fTextX + m_fSurnamePad, fTextY ); fTextY += m_sizeSurname.Height; } else { g.DrawString( Name, paintbox.m_font, solidbrushText, fTextX + m_fSurnamePad, fTextY ); fTextY += m_sizeSurname.Height; } g.DrawString( m_sDate, paintbox.m_font, solidbrushText, fTextX + m_fDatePad, fTextY ); if( m_bChild ) { g.DrawLine( paintbox.m_penConnector, m_x+MARGIN_HORIZ+m_sizeText.Width/2f, m_y, m_x+MARGIN_HORIZ+m_sizeText.Width/2f, m_y + MARGIN_VERT/* -1f*/ ); } if( m_ir != null ) { alImageMap.Add( new CMiniTreeMap( Name, m_ir, m_bLinkable, (int)(m_x+MARGIN_HORIZ), (int)(m_y+MARGIN_VERT), (int)(m_x+MARGIN_HORIZ+m_sizeText.Width), (int)(m_y+MARGIN_VERT+m_sizeText.Height-1f) ) ); } }
// 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(); }
// The heart of GEDmill is here. public void Create() { LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Note, "CWebsite::Create()" ); // 1 means the process was aborted, for signalling back to calling thread. 2 means file nError. CThreadError threaderror = new CThreadError( 1, "No error" ); try { // The value to indicate in the progress bar to show how much of the website creation is complete. int nProgress = 0; // The maximum value of the progress bar, i.e. when website creation is fully complete. int nProgressMax = 1 // Site-wide multimedia files + 1 // W3C Sticker + 1 // Background image + 1 // Style sheet + m_gedcom.CountIndividuals + 1 // Individuals Index + m_gedcom.CountSources + 1 // Front page + 1 // Help page + 1 // CD ROM (Doesn't matter here that CD ROM autorun might not be included.) + 1; // Scripts (Doesn't matter here that scripts might not be included.) // The paintbox with which to draw the mini tree CPaintbox paintbox = new CPaintbox(MainForm.s_config); paintbox.SetBackgroundImage(MainForm.s_config.m_sBackgroundImage); // Object to keep count of number of files created etc. CStats stats = new CStats(); // Here goes.... // Start the progress indicator. m_progressWindow.Begin( 0, nProgressMax ); if (m_progressWindow.IsAborting) { return; } // Copy the images to use in place of non-pic multimedia files. m_progressWindow.SetText("Copying multimedia"); CopyIcons(); if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo(++nProgress); // Copy the W3C sticker file. m_progressWindow.SetText("Copying W3C sticker"); string sW3CFilename = ""; if (MainForm.s_config.m_bIncludeValiditySticker) { sW3CFilename = CopyW3CSticker(); } if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo(++nProgress); // Create the index creator for use by the individuals records creator. CCreatorIndexIndividuals indiIndexCreator = new CCreatorIndexIndividuals( m_gedcom, m_progressWindow, sW3CFilename ); // Copy the image for the background of the webpages. m_progressWindow.SetText("Copying background image"); string sBackgroundImageFilename = CopyBackgroundImage(); if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo( ++nProgress ); // Create the style sheet m_progressWindow.SetText("Creating style sheet"); string cssFilename = String.Concat( MainForm.s_config.m_sOutputFolder, "\\", MainForm.s_config.m_sStylesheetFilename, ".css" ); if( MainForm.s_config.m_sStylesheetFilename.Length>0 && (!MainForm.s_config.m_bPreserveStylesheet || !File.Exists( cssFilename ) ) ) { CCreatorStylesheet csc = new CCreatorStylesheet( m_gedcom, m_progressWindow, sW3CFilename, cssFilename, sBackgroundImageFilename ); csc.Create(); } if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo( ++nProgress ); // Create the pages for the individual records. m_progressWindow.SetText("Creating individual pages"); foreach (CIndividualRecord ir in m_gedcom.m_alIndividualRecords) { CCreatorRecordIndividual ipc = new CCreatorRecordIndividual( m_gedcom, m_progressWindow, sW3CFilename, ir, indiIndexCreator, paintbox ); if (ipc.Create( stats )) { stats.m_unIndividuals++; } if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo(++nProgress); } // Create the index for the individual records pages. m_progressWindow.SetText("Creating individuals index"); indiIndexCreator.Create(); if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo(++nProgress); // Clear list of copied files, so that source images get copied afresh // and so get resized differently to any indi images based on the same file. CCreator.ClearCopiedFilesList(); // Create the pages for the source records. m_progressWindow.SetText( "Creating source pages" ); foreach( CSourceRecord sr in m_gedcom.m_alSourceRecords ) { if( sr != null && sr.ContainsAnyInformation() ) { CCreatorRecordSource spc = new CCreatorRecordSource( m_gedcom, m_progressWindow, sW3CFilename, sr ); if( spc.Create( stats ) ) { stats.m_unSources++; } } if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo(++nProgress); } if (m_progressWindow.IsAborting) { return; } // Create the front page m_progressWindow.SetText("Creating front page"); string front_page_filename = String.Concat(MainForm.s_config.m_sOutputFolder, "\\", MainForm.s_config.m_sFrontPageFilename, ".", MainForm.s_config.m_sHtmlExtension); if( MainForm.s_config.m_sFrontPageFilename.Length>0 && (!MainForm.s_config.m_bPreserveFrontPage || !File.Exists( front_page_filename ) ) ) { CCreatorFrontPage fpc = new CCreatorFrontPage( m_gedcom, m_progressWindow, sW3CFilename, stats ); fpc.Create(); } m_progressWindow.StepTo( ++nProgress ); if (m_progressWindow.IsAborting) { return; } // Create the help page m_progressWindow.SetText("Creating help page"); string help_page_filename = String.Concat(MainForm.s_config.m_sOutputFolder, "\\", "help.", MainForm.s_config.m_sHtmlExtension); if( MainForm.s_config.m_bIncludeHelppage ) { CCreatorHelppage hpc = new CCreatorHelppage( m_gedcom, m_progressWindow, sW3CFilename ); hpc.Create(); } m_progressWindow.StepTo( ++nProgress ); if (m_progressWindow.IsAborting) { return; } // Copy the CD ROM autorun file m_progressWindow.SetText( "Creating CD-ROM files" ); if( MainForm.s_config.m_bCreateCDROMFiles ) { CreateCDROMFiles( m_gedcom ); } if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo(++nProgress); // Copy the Javascript m_progressWindow.SetText( "Creating Javascript file" ); if( MainForm.s_config.m_bAllowMultipleImages ) // Currently (10Dec08) the only thing that uses javascript is the multiple images feature. { CreateJavascriptFiles( m_gedcom ); } if (m_progressWindow.IsAborting) { return; } m_progressWindow.StepTo(++nProgress); // Done LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Note, "Finished" ); m_progressWindow.SetText( "Done" ); threaderror.m_nError = 0; threaderror.m_sMessage = ""; } catch( ArgumentException e ) { LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught Argument Exception : " + e.ToString() ); threaderror.m_nError = 2; // 2 => abnormal abort. threaderror.m_sMessage = ""; } catch( IOException e ) { LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught IO Exception : " + e.ToString() ); threaderror.m_nError = 2; // 2 => abnormal abort. threaderror.m_sMessage = ""; } catch( NullReferenceException e ) { LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught NullReference Exception : " + e.ToString() ); threaderror.m_nError = 2; // 2 => abnormal abort. threaderror.m_sMessage = ""; } catch( CHTMLException e ) { threaderror.m_nError = 2; // 2 => abnormal abort. threaderror.m_sMessage = e.Message; } catch( Exception e ) { LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught Exception : " + e.ToString() ); threaderror.m_nError = 2; // 2 => abnormal abort. threaderror.m_sMessage = ""; } finally { LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Note, "Thread ending..." ); if( m_progressWindow != null ) { m_progressWindow.End( threaderror ); } } }