// Adds the given multimedia links to the given multimedia list.
        protected void AddMultimedia( ArrayList multimediaLinks, ArrayList userMultimediaFileReferences, string sMmPrefix, string sMmLargePrefix, uint uMaxWidth, uint uMaxHeight, CStats stats )
        {
            //int nFilesAdded = 0;

              if( multimediaLinks != null )
              {
            foreach( CMultimediaLink ml in multimediaLinks )
            {
              ArrayList alFileRefs = ml.GetFileReferences();
              if( alFileRefs != null )
              {
            // Add the pics from the GEDCOM record.
            AddMultimediaFileReferences( alFileRefs, sMmPrefix, sMmLargePrefix, uMaxWidth, uMaxHeight, stats );
              }
            }
              }
              if( userMultimediaFileReferences != null )
              {
            // Add extra pics added by the user on indi exclude screen.
            AddMultimediaFileReferences( userMultimediaFileReferences, sMmPrefix, sMmLargePrefix, uMaxWidth, uMaxHeight, stats );
              }
        }
        // The main method that causes the page to be created.
        public bool Create(CStats stats)
        {
            // Sanity check
              if( m_sr == null )
              {
            return false;
              }

              // Create the strings to use for the HTML file.
              string sPageDescription = "GEDmill GEDCOM to HTML page for " + m_sr.m_sSourceDescriptiveTitle;
              string sKeywords = "family tree history " + m_sr.m_sSourceDescriptiveTitle;
              string sFilename = String.Concat( MainForm.s_config.m_sOutputFolder, "\\sour", m_sr.m_xref );
              string sFullFilename = String.Concat(sFilename, ".", MainForm.s_config.m_sHtmlExtension);

              CHTMLFile f = null;
              try
              {
            // Create a new file with an HTML header.
            f = new CHTMLFile( sFullFilename, m_sr.DescriptiveTitle, sPageDescription, sKeywords );

            // Create a navbar to main site, front page etc.
            OutputPageHeader( f.m_sw, "", "", true, true );

            f.m_sw.WriteLine("    <div class=\"hr\"></div>");
            f.m_sw.WriteLine("");
            f.m_sw.WriteLine("    <div id=\"page\"> <!-- page -->");

            // Write the page's title text.
            f.m_sw.WriteLine("      <div id=\"main\">");
            f.m_sw.WriteLine("        <div id=\"summary\">");
            f.m_sw.WriteLine("          <div id=\"names\">");
            string sName = m_sr.DescriptiveTitle;
            if( sName == "" )
            {
              sName = "Source ";
              bool bGotSourceName = false;
              // Try user reference number
              foreach( CUserReferenceNumber urn in m_sr.m_alUserReferenceNumbers )
              {
            if( urn.m_sUserReferenceNumber != "" )
            {
              sName += urn.m_sUserReferenceNumber;
              bGotSourceName = true;
              break;
            }
              }
              if( !bGotSourceName && m_sr.m_sAutomatedRecordId != null && m_sr.m_sAutomatedRecordId != "" )
              {
            sName += m_sr.m_sAutomatedRecordId;
              }
              else if( !bGotSourceName )
              {
            sName += m_sr.m_xref;
              }
            }
            f.m_sw.WriteLine(String.Concat("            <h1>",EscapeHTML(sName,false ),"</h1>"));

            // Add repository information
            foreach( CSourceRepositoryCitation src in m_sr.m_alSourceRepositoryCitations )
            {
              string xrefRepo = src.m_xrefRepo;
              CRepositoryRecord rr = m_gedcom.GetRepositoryRecord( xrefRepo );
              if( rr != null )
              {
              if (rr.m_sNameOfRepository != null && rr.m_sNameOfRepository != "")
              {
                  f.m_sw.WriteLine(String.Concat("            <h2>", EscapeHTML(rr.m_sNameOfRepository, false), "</h2>"));
              }
            if( rr.m_alNoteStructures != null && rr.m_alNoteStructures.Count > 0 )
            {
              foreach( CNoteStructure ns in rr.m_alNoteStructures )
              {
                string noteText;
                if( MainForm.s_config.m_bObfuscateEmails )
                {
                  noteText = ObfuscateEmail( ns.Text );
                }
                else
                {
                  noteText = ns.Text;
                }
                f.m_sw.WriteLine(String.Concat("            <p>", EscapeHTML( noteText, false ), "</p>" ) );
              }
            }
              }
              if( src.m_alNoteStructures != null && src.m_alNoteStructures.Count > 0 )
              {
            foreach( CNoteStructure ns in src.m_alNoteStructures )
            {
              string noteText;
              if( MainForm.s_config.m_bObfuscateEmails )
              {
                noteText = ObfuscateEmail( ns.Text );
              }
              else
              {
                noteText = ns.Text;
              }

              f.m_sw.WriteLine(String.Concat("            <p>", EscapeHTML( noteText, false ), "</p>" ) );
            }
              }

            }

            // Add Publication Information
            string sPubFacts;
            if( MainForm.s_config.m_bObfuscateEmails )
            {
              sPubFacts = ObfuscateEmail( m_sr.m_sSourcePublicationFacts );
            }
            else
            {
              sPubFacts = m_sr.m_sSourcePublicationFacts;
            }
            if( sPubFacts != null && sPubFacts != "" )
            {
              if( sPubFacts.Length > 7 && sPubFacts.ToUpper().Substring(0,7) == "HTTP://" )
              {
            f.m_sw.WriteLine(String.Concat("            <h2>","<a href=\"", sPubFacts, "\">", EscapeHTML(sPubFacts,false), "</a>","</h2>"));
              }
              else
              {
            f.m_sw.WriteLine(String.Concat("            <h2>",EscapeHTML(sPubFacts,false),"</h2>"));
              }
            }

            f.m_sw.WriteLine("          </div> <!-- names -->");
            f.m_sw.WriteLine("        </div> <!-- summary -->");

            // Collect together multimedia links.
            if( MainForm.s_config.m_bAllowMultimedia && m_sr.m_alUniqueFileRefs != null )
            {
              m_sr.m_alUniqueFileRefs.Sort( new CMultimediaFileReference.OrderComparer() );

              // Fill m_alMultimediaList:
              AddMultimedia( null, m_sr.m_alUniqueFileRefs, String.Concat(m_sr.m_xref,"mms"), String.Concat(m_sr.m_xref,"mos"), MainForm.s_config.m_uMaxSourceImageWidth, MainForm.s_config.m_uMaxSourceImageHeight, stats );
            }

            // Add pics
            OutputMultimedia(f);

            // Add textFromSource
            string sCleanText = m_sr.m_sTextFromSource;
            CGedcom.ParseWhitespace( ref sCleanText );
            if( MainForm.s_config.m_bObfuscateEmails )
            {
              sCleanText = ObfuscateEmail( sCleanText );
            }
            if( sCleanText != null && sCleanText != "" )
            {
              f.m_sw.WriteLine("        <div id=\"text\">");
              f.m_sw.WriteLine("          <h1>Text</h1>");
              f.m_sw.WriteLine("          <p class=\"pretext\">");
              f.m_sw.WriteLine( EscapeHTML( sCleanText,false) );
              f.m_sw.WriteLine("            </p>");
              f.m_sw.WriteLine("        </div> <!-- text -->");
            }

            // Add notes
            if( m_sr.m_alNoteStructures.Count > 0 )
            {
              // Generate notes list into a local array before adding header title. This is to cope with the case where all notes are nothing but blanks.
              ArrayList alNoteStrings = new ArrayList(m_sr.m_alNoteStructures.Count);

              foreach( CNoteStructure ns in m_sr.m_alNoteStructures )
              {
            if( ns.Text != null && ns.Text.Length > 0 )
            {
              string sNoteText;
              if( MainForm.s_config.m_bObfuscateEmails )
              {
                sNoteText = ObfuscateEmail( ns.Text );
              }
              else
              {
                sNoteText = ns.Text;
              }

              string sourceRefs = "";
              alNoteStrings.Add( String.Concat("          <li>",EscapeHTML(sNoteText,false),sourceRefs,"</li>") );
            }
              }

              if( alNoteStrings.Count > 0 )
              {
            f.m_sw.WriteLine("        <div id=\"notes\">");
            f.m_sw.WriteLine("          <h1>Notes</h1>");
            f.m_sw.WriteLine("          <ul>");

            foreach( string note_string in alNoteStrings )
            {
              f.m_sw.WriteLine(note_string);
            }

            f.m_sw.WriteLine("          </ul>");
            f.m_sw.WriteLine("        </div> <!-- notes -->");
              }
            }

            if( MainForm.s_config.m_bSupressBackreferences == false )
            {
              if( m_sr.m_alBackreferences != null && m_sr.m_alBackreferences.Count > 0 )
              {
            f.m_sw.WriteLine("        <div id=\"citations\">" );
            f.m_sw.WriteLine("          <h1>Citations</h1>");
            f.m_sw.WriteLine("          <ul>");

            Hashtable htBackrefs = m_sr.MakeBackRefList();

            IDictionaryEnumerator enumerator = htBackrefs.GetEnumerator();
            while( enumerator.MoveNext() )
            {
              CIndividualRecord ir = (CIndividualRecord)(enumerator.Value);
              if( ir != null && !ir.Restricted )
              {
                string link = MakeLink(ir);
                if( link != "" )
                {
                  f.m_sw.WriteLine( String.Concat( "            <li>", link, "</li>" ) );
                }
              }
            }

            f.m_sw.WriteLine("          </ul>");
            f.m_sw.WriteLine("        </div> <!-- citations -->");

              }
            }

            f.m_sw.WriteLine("      </div> <!-- main -->");

            // Add footer (Record date, W3C sticker, GEDmill credit etc.)
            OutputFooter( f, m_sr );

            f.m_sw.WriteLine("    </div> <!-- page -->");
              }
              catch( IOException e )
              {
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught IO Exception(6) : " + e.ToString() );
              }
              catch( ArgumentException e )
              {
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught Argument Exception(6) : " + e.ToString() );
              }
              finally
              {
            if( f != null )
            {
              f.Close(); // Adds standard footer to the file
            }
              }
              return true;
        }
 // Constructor
 public CCreatorFrontPage( CGedcom gedcom, IProgressCallback progress, string w3cfile, CStats stats )
     : base(gedcom, progress, w3cfile)
 {
     m_stats = stats;
 }
        // Copies a file from the user's source directory to the website output directory, renaming and resizing as appropriate.
        // Returns the sFilename of the copy.
        // sArea is sAsid sub-part of image
        // sArea is changed to reflect new image size
        // sArea can be {0,0,0,0} meaning use whole image
        // stats can be null if we don't care about keeping count of the multimedia files.
        public static string CopyMultimedia( string sFullFilename, string sNewFilename, uint uMaxWidth, uint uMaxHeight, ref Rectangle rectArea, CStats stats )
        {
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Note, String.Format("CopyMultimedia( {0}, {1}, {2} )", sFullFilename, uMaxWidth, uMaxHeight ) );

              string sResult = sFullFilename;

              if( sNewFilename == "" )
              {
            sNewFilename = Path.GetFileName( sFullFilename );
              }

              try
              {
            string sAsidFilename;
            if (rectArea.Width == 0)
            {
            sAsidFilename = sFullFilename;
            }
            else
            {
            sAsidFilename = String.Concat(sFullFilename, ".", rectArea.X.ToString(), ",", rectArea.Y.ToString(), ",", rectArea.Width.ToString(), ",", rectArea.Height.ToString());
            }

            if( uMaxWidth != 0 && uMaxHeight != 0 )
            {
              sAsidFilename = String.Concat( sAsidFilename, "(", uMaxWidth.ToString(), "x", uMaxHeight.ToString(), ")");
            }

            if( sFullFilename != null && MainForm.s_config.m_sOutputFolder != null && MainForm.s_config.m_sOutputFolder != "" )
            {
              // Have we already copied the sFilename?
              if( s_htCopiedFiles.ContainsKey( sAsidFilename ) )
              {
            CFilenameAndSize filenameAndSize = (CFilenameAndSize)s_htCopiedFiles[ sAsidFilename ];
            sResult = filenameAndSize.m_sFilename;
            rectArea.Width = filenameAndSize.m_nWidth;
            rectArea.Height = filenameAndSize.m_nHeight;
              }
              else
              {
            // Copy file into output directory
            if( MainForm.s_config.m_bCopyMultimedia )
            {
              string sImageFolder = MainForm.s_config.m_sImageFolder;
              string sOutputFolder = MainForm.s_config.m_sOutputFolder;

              if (sImageFolder != "")
              {
                  sImageFolder = sImageFolder + '\\';
              }
              if (sOutputFolder != "")
              {
                  sOutputFolder = sOutputFolder + '\\';
              }

              string sCopyFilename = String.Concat(sImageFolder, sNewFilename);
              string sAbsImageFolder = String.Concat(sOutputFolder,sImageFolder);
              string sAbsCopyFilename = String.Concat(sAbsImageFolder, sNewFilename);

              // If image folder doesn't exist, create it
              if( !File.Exists( sAbsImageFolder ) && !Directory.Exists( sAbsImageFolder ) ) // TODO: this returns false if it exists but you don't have permission!
              {
                Directory.CreateDirectory( sAbsImageFolder ); // TODO: catch failure to create, e.g. output folder not there yet
              }

              // If new sFilename already exists, append a number and keep trying
              uint uCopy = 0;
              string sFilePart = Path.GetFileNameWithoutExtension( sCopyFilename );
              string sExtnPart = Path.GetExtension( sCopyFilename );
              while( File.Exists( sAbsCopyFilename ) )
              {
                const string sAdditionalLetters = "abcdefghijklmnopqrstuvwxyz";
                if( MainForm.s_config.m_bRenameMultimedia == false )
                {
                  uint nCopyPlus = uCopy + 2;
                  sCopyFilename = String.Concat( sImageFolder, sFilePart, "-", nCopyPlus.ToString(), sExtnPart );
                }
                else if( uCopy >= sAdditionalLetters.Length )
                {
                  // Once all the extra letters have been used up, put number as "-n", where n starts from 2.
                  uint nCopyMinus = uCopy - (uint)(sAdditionalLetters.Length - 2);
                  sCopyFilename = String.Concat( sImageFolder, sFilePart, "-", nCopyMinus.ToString(), sExtnPart );
                }
                else
                {
                  sCopyFilename = String.Concat( sImageFolder, sFilePart, sAdditionalLetters[(int)uCopy], sExtnPart );
                }
                uCopy++;

                sAbsCopyFilename = String.Concat( sOutputFolder,sCopyFilename );
              }

              LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Note, String.Format("Copying \"{0}\" to \"{1}\"",sFullFilename, sAbsCopyFilename ) );
              File.Copy( sFullFilename, sAbsCopyFilename, true );
              File.SetAttributes( sFullFilename, System.IO.FileAttributes.Normal ); // Make any Read-Only files read-write.
              if( uMaxWidth != 0 && uMaxHeight != 0 )
              {
                // It must be a picture file
                sCopyFilename = ConvertAndCropImage( sOutputFolder, sCopyFilename, ref rectArea, uMaxWidth, uMaxHeight );
              }
              s_htCopiedFiles[ sAsidFilename ] = new CFilenameAndSize( sCopyFilename, rectArea.Width, rectArea.Height );
              sResult = sCopyFilename;
            }
            else
            {
              if( MainForm.s_config.m_bRelativiseMultimedia )
              {
                // TODO: make path of sFilename relative to MainForm.s_config.m_outputFolder
                string sRelativeFilename = sFullFilename;
                sResult = sRelativeFilename;
              }
            }
            if( null != stats )
            {
              stats.m_unMultimediaFiles++;
            }
              }
            }
            sResult = sResult.Replace( '\\', '/' );
              }
              catch( IOException e )
              {
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught IO Exception : " + e.ToString() );
            sResult = "";
              }
              catch( ArgumentException e )
              {
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught Argument Exception : " + e.ToString() );
            sResult = "";
              }
              catch( CHTMLException e )
              {
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught HTML Exception : " + e.ToString() );
            sResult = "";
              }
              catch( Exception e )
              {
            LogFile.TheLogFile.WriteLine( LogFile.DT_HTML, LogFile.EDebugLevel.Error, "Caught generic exception : " + e.ToString() );
            sResult = "";
              }

              return sResult;
        }
        // The main method that causes the page to be created.
        public bool Create( CStats stats )
        {
            LogFile.TheLogFile.WriteLine(LogFile.DT_HTML, LogFile.EDebugLevel.Note, "CCreatorRecordIndividual.Create()");

              if (m_ir == null)
              {
            return false;
              }

              if (m_ir.Visibility() == CIndividualRecord.EVisibility.Invisible)
              {
            return false;
              }

              // Collect together multimedia links
              if (MainForm.s_config.m_bAllowMultimedia && (m_ir.m_alUniqueFileRefs != null) && !m_bConcealed)
              {
            m_ir.m_alUniqueFileRefs.Sort(new CMultimediaFileReference.OrderComparer());
            AddMultimedia( null, m_ir.m_alUniqueFileRefs, String.Concat(m_ir.m_xref, "mm"), String.Concat(m_ir.m_xref, "mo"), MainForm.s_config.m_uMaxImageWidth, MainForm.s_config.m_uMaxImageHeight, stats );
              }

              AddEvents();

              RemoveLoneOccupation();

              if (m_qdateInferredBirthday != null && m_qdateInferredBirthday.m_eqQualification == CPGQualifiedDate.EQualification.Birth)
              {
            m_dateActualBirthday = m_qdateInferredBirthday.m_date;
              }

              if (m_qdateInferredDeathday != null && m_qdateInferredDeathday.m_eqQualification == CPGQualifiedDate.EQualification.Death)
              {
            m_dateActualDeathday = m_qdateInferredDeathday.m_date;
              }

              ConstructName();

              CPGDate age30;
              if (m_qdateInferredBirthday != null && CPGDate.IsValid(m_qdateInferredBirthday.m_date))
              {
            age30 = new CPGDate(m_qdateInferredBirthday.m_date);
              }
              else
              {
            age30 = new CPGDate(DateTime.Now);
              }
              age30.m_year.m_nYear += MainForm.s_config.m_nAgeForOccupation;

              // We should have birthday and deathday by now, so find longest occupation
              if (!m_bConcealed)
              {
            m_sOccupation = BestOccupation(m_alOccupations, age30, (m_qdateInferredBirthday != null) ? m_qdateInferredBirthday.m_date : null, (m_qdateInferredDeathday != null) ? m_qdateInferredDeathday.m_date : null);// Picks occupation with longest time span
              }

              // Go through all families this person was a irSubject to
              if (!m_bConcealed)
              {
            foreach (CSpouseToFamilyLink sfl in m_ir.m_alSpouseToFamilyLinks)
            {
              CFamilyRecord fr = m_gedcom.GetFamilyRecord(sfl.m_xrefFam);
              if (fr != null)
              {

            // Find the irSubject's name
            CIndividualRecord spouse = null;
            string spouseLink = "";
            if (fr.m_xrefHusband != m_ir.m_xref)
            {
              spouse = m_gedcom.GetIndividualRecord(fr.m_xrefHusband);
            }
            else
            {
              spouse = m_gedcom.GetIndividualRecord(fr.m_xrefWife);
            }
            if (spouse != null && spouse.Visibility() != CIndividualRecord.EVisibility.Invisible)
            {
              spouseLink = MakeLink(spouse);
            }

            // Add fr events as events connected to this individual
            foreach (CFamilyEventStructure fes in fr.m_alFamilyEventStructures)
            {
              ProcessEvent(fes, spouseLink );
            }

            AddChildrensEvents(fr);

            AddMarriage( spouse, spouseLink, fr );
              }
            }
            AddParentsAndSiblings();
              } // end if !concealed

              string birthyear = "";
              string deathyear = "";
              if (!m_bConcealed)
              {
            if (m_qdateInferredBirthday != null && m_qdateInferredBirthday.m_date != null)
            {
              birthyear = m_qdateInferredBirthday.m_date.Year;
            }
            if (m_qdateInferredDeathday != null && m_qdateInferredDeathday.m_date != null)
            {
              deathyear = m_qdateInferredDeathday.m_date.Year;
            }
              }

              string title = m_sName; //"Fred Bloggs 1871-1921"
              string lifeDates = "";
              if (!m_bConcealed)
              {
            if (birthyear != "" || deathyear != "")
            {
              lifeDates = String.Concat(birthyear, "-", deathyear);
              title = String.Concat(m_sName, " ", lifeDates);
            }
              }

              AddIndividualIndexEntry( lifeDates );

              OutputHTML( title );

              return true;
        }
        // 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 );
            }
              }
        }
        // Adds the given list of file references to the multimedia list.
        private void AddMultimediaFileReferences( ArrayList alFileRefs, string sMmPrefix, string sMmLargePrefix, uint uMaxWidth, uint uMaxHeight, CStats stats )
        {
            if( alFileRefs == null )
              {
            return;
              }

              foreach( CMultimediaFileReference mfr in alFileRefs )
              {

            if( mfr.m_sMultimediaFileReference == null || mfr.m_bVisible == false )
            {
              // No sFilename, or else user chose not to show this picture
              continue;
            }

            string sCopyFilename = "";
            int nMmOrdering = mfr.m_nOrderIndex;
            string sMmTitle = mfr.m_sDescriptiveTitle;
            string sMmFilename = mfr.m_sMultimediaFileReference;
            string sMmFormat = mfr.m_sMultimediaFormat;
            Rectangle rectArea = new Rectangle(0,0,0,0);
            string sExtnPart;
            bool bBlockThisMediaType = false;

            // Don't trust extension on sFilename. Use our own. (Happens for .tmp files from embedded data)
            switch( sMmFormat )
            {
              case "bmp":
            sExtnPart = ".bmp";
            break;
              case "gif":
            sExtnPart = ".gif";
            break;
              case "jpg":
              case "jpeg":
            sExtnPart = ".jpg";
            break;
              case "tiff":
              case "tif":
            sExtnPart = ".tif";
            break;
              case "png":
            sExtnPart = ".png";
            break;
              case "ole":
            bBlockThisMediaType = true;
            sExtnPart = ".ole";
            break;
              default:
            sExtnPart = Path.GetExtension( sMmFilename );
            if( sExtnPart.ToUpper() == ".TMP" )
            {
              sExtnPart = "." + sMmFormat;
            }
            break;
            }
            string sOriginalFilename = Path.GetFileName( sMmFilename );

            bool bPictureFormat = mfr.IsPictureFormat();

            if( bPictureFormat || MainForm.s_config.m_bAllowNonPictures )
            {
              if( !bPictureFormat && MainForm.s_config.m_bAllowNonPictures )
              {
            stats.m_bNonPicturesIncluded = true;
              }

              string sNewFilename = sOriginalFilename;
              if( sMmFilename != null && sMmFilename != "" )
              {
            // Give multimedia files a standard name
            if( MainForm.s_config.m_bRenameOriginalPicture )
            {
              //string sFilePart = sMmPrefix;//String.Concat( mm_prefix, nMultimediaFiles.ToString() );
              sNewFilename = String.Concat(sMmPrefix, sExtnPart.ToLower());
            }

            if( !bBlockThisMediaType )
            {
              if( bPictureFormat )
              {
                if( mfr.m_asidPair != null )
                {
                  Rectangle rectAsidArea = mfr.m_asidPair.m_rectArea;
                  rectArea = new Rectangle( rectAsidArea.X, rectAsidArea.Y, rectAsidArea.Width, rectAsidArea.Height );
                }
                sCopyFilename = CopyMultimedia( sMmFilename, sNewFilename, uMaxWidth, uMaxHeight, ref rectArea, stats );
              }
              else
              {
                sCopyFilename = CopyMultimedia( sMmFilename, sNewFilename, 0, 0, ref rectArea, stats );
              }
            }
              }

              if( sCopyFilename != null && sCopyFilename != "" )
              {
            string sLargeFilename = "";
            // Copy original original version
            if( MainForm.s_config.m_bLinkOriginalPicture )
            {
              if( MainForm.s_config.m_bRenameOriginalPicture )
              {
                //string sFilePart = sMmLargePrefix;
                sLargeFilename = String.Concat(sMmLargePrefix, sExtnPart.ToLower());
              }
              else
              {
                sLargeFilename = sOriginalFilename;
              }

              Rectangle rectLargeArea = new Rectangle(0,0,0,0);
              sLargeFilename = CopyMultimedia( sMmFilename, sLargeFilename, 0, 0, ref rectLargeArea, null );
            }

            // Add format and new sFilename to multimedia list
            CIMultimedia imm = new CIMultimedia( nMmOrdering, sMmFormat, sMmTitle, sCopyFilename, sLargeFilename, rectArea.Width, rectArea.Height );
            m_alMultimediaList.Add( imm );
              }
              else
              {
            // Happens e.g. when original file doesn't exist.
              }
            }
              }
        }