public GedcomFamilyRecord(GedcomDatabase database, GedcomIndividualRecord indi1, GedcomIndividualRecord indi2) : this()
		{
			Level = 0;
			Database = database;
			XRefID = database.GenerateXref("FAM");
			
			if (indi1 != null)
			{
				GedcomFamilyLink link = new GedcomFamilyLink();
				link.Database = database;
				link.Family = XRefID;
				link.Indi = indi1.XRefID;
				indi1.SpouseIn.Add(link);
				
				if (indi2 != null)
				{
					link = new GedcomFamilyLink();
					link.Database = database;
					link.Family = XRefID;
					link.Indi = indi2.XRefID;
					indi2.SpouseIn.Add(link);	
				}
				
				switch (indi1.Sex)
				{
					case GedcomSex.Female:
						Wife = indi1.XRefID;
						if (indi2 != null)
						{
							Husband = indi2.XRefID;
						}
						break;
					default:
						// got to put some where if not male or female,
						// go with same as male
						Husband = indi1.XRefID;
						if (indi2 != null)
						{
							Wife = indi2.XRefID;
						}
						break;
				}
			}
			
			database.Add(XRefID, this);
		}
		private void AppendFamilyDetails(GedcomFamilyLink link, XmlNode root, int generation)
		{
			string famID = link.Family;
			
			if (!_processed.Contains(famID))
			{
				_processed.Add(famID);
				
				GedcomFamilyRecord fam = _database[famID] as GedcomFamilyRecord;
			
				if (fam != null)
				{
					foreach (GedcomFamilyEvent famEvent in fam.Events)
					{
						famEvent.EventXRefID = _database.GenerateXref("EVENT");
						AppendEvent(famEvent, root);	
						
						AppendSources(famEvent, root);
					}
					
					AppendFamily(fam, root);
					
					if (!string.IsNullOrEmpty(fam.Husband))
					{
						GedcomIndividualRecord husb = _database[fam.Husband] as GedcomIndividualRecord;
						if (husb != null)
						{
							AppendIndividualDetails(husb, root, generation);
						}
						else
						{
							throw new Exception("Husband points to non individual record");
						}
					}

					if (!string.IsNullOrEmpty(fam.Wife))
					{
						GedcomIndividualRecord wife = _database[fam.Wife] as GedcomIndividualRecord;
						if (wife != null)
						{
							AppendIndividualDetails(wife, root, generation);
						}
						else
						{
							throw new Exception("Husband points to non individual record");
						}
					}
					
					foreach (string childID in fam.Children)
					{
						GedcomIndividualRecord child = _database[childID] as GedcomIndividualRecord;
						if (child != null)
						{
							int childGeneration = generation - 1;
							AppendIndividualDetails(child, root, childGeneration);
						}
						else
						{
							throw new Exception("Child points to non individual record");
						}
					}
					
				}
				else
				{
					throw new Exception("Family link points to non family record");
				}	
			}
		}
		public bool ChildInFamily(string family, out GedcomFamilyLink famLink)
		{
			bool ret = false;
			famLink = null;
			
			foreach (GedcomFamilyLink link in ChildIn)
			{
				if (link.Family == family)
				{
					ret = true;
					famLink = link;
					break;
				}
			}
			
			return ret;
		}
Exemple #4
0
		private void Rebuild(Gtk.Table table, uint[][][] positions, GedcomIndividualRecord activePerson, GedcomFamilyLink[] lst)
		{
			foreach (Gtk.Widget child in table.Children)
			{
				child.Destroy();
			}
			table.Resize(1,1);
			
			uint xmax = 0;
			uint ymax = 0;
			
			for (int i = 0; i < positions.Length; i ++)
			{
				uint x = positions[i][0][0] + 1;
				uint y = positions[i][0][1] + 1;
				uint w = positions[i][0][2];
				uint h = positions[i][0][3];
				
				GedcomFamilyLink famLink = (GedcomFamilyLink)lst[i];
				if (famLink == null)
				{	
					PedigreeBox pw = new PedigreeBox(null, 0, null);
					
					if (i > 0 && lst[((i+1)/2)-1] != null)
					{
						GedcomFamilyLink missingFamLink = (GedcomFamilyLink)lst[((i+1)/2)-1];
						
						// missing parent button
						pw.ForceMouseOver = true;
					}
					// FIXME: both conditions do the same thing, double checking
					// the gramps code it doesn't appear to be a mistake in porting
					if (positions[i][0][2] > 1)
					{
						table.Attach(pw,x, x+w, y, y+h, Gtk.AttachOptions.Fill, Gtk.AttachOptions.Fill, 0, 0);
					}
					else
					{
						table.Attach(pw,x, x+w, y, y+h, Gtk.AttachOptions.Fill, Gtk.AttachOptions.Fill, 0, 0);
					}
					
					xmax = (uint)Math.Max(xmax, x + w);
					ymax = (uint)Math.Max(ymax, y + h);
				}
				else
				{
					GedcomIndividualRecord indi = (GedcomIndividualRecord)_database[famLink.Indi];
					
					if (_showImages && i < ((positions.Length - 1) / 2) && positions[i][0][3] > 1)
					{
						
					}
					
					PedigreeBox pw = new PedigreeBox(indi, positions[i][0][3], null);
					pw.SelectIndividual += PedigreeBox_SelectIndividual;
					
					if (positions[i][0][3] < 7)
					{
						pw.TooltipMarkup = pw.FormatPerson(11, true);
					}
					
					if (positions[i][0][2] > 1)
					{
						table.Attach(pw, x, x+w, y, y+h, Gtk.AttachOptions.Expand | Gtk.AttachOptions.Fill, Gtk.AttachOptions.Expand | Gtk.AttachOptions.Fill, 0, 0);
					}
					else
					{
						table.Attach(pw, x, x+w, y, y+h, Gtk.AttachOptions.Fill, Gtk.AttachOptions.Fill, 0, 0);
					}
					
					xmax = (uint)Math.Max(xmax, x + w);
					ymax = (uint)Math.Max(ymax, y + h);
				}
				
				// connection lines
				if (positions[i].Length > 1)
				{
					// separate boxes for father and mother
					x = positions[i][1][0] + 1;
					y = positions[i][1][1] + 1;
					w = 1;
					h = positions[i][1][2];
					
					Gtk.DrawingArea line = new Gtk.DrawingArea();
					line.ExposeEvent += Line_Expose;
					bool rela = false;
					if (famLink != null && (famLink.Pedigree == PedegreeLinkageType.Birth || famLink.Pedigree == PedegreeLinkageType.Unknown))
					{
						line.AddEvents((int)Gdk.EventMask.ButtonPressMask);
						rela = true;
					}
					Utility.Pair<int, bool> lineData = new Pair<int,bool>();
					lineData.First = i * 2 + 1;
					lineData.Second = rela;
					_lines[line] = lineData;
					
					table.Attach(line, x, x + w, y, y + h, Gtk.AttachOptions.Fill, Gtk.AttachOptions.Fill, 0, 0);
					
					xmax = (uint)Math.Max(xmax, x + w);
					ymax = (uint)Math.Max(ymax, y + h);
					
					x = positions[i][2][0] + 1;
					y = positions[i][2][1] + 1;
					w = 1;
					h = positions[i][2][2];
					
					line = new Gtk.DrawingArea();
					line.ExposeEvent += Line_Expose;
					rela = false;
					if (famLink != null && (famLink.Pedigree == PedegreeLinkageType.Birth || famLink.Pedigree == PedegreeLinkageType.Unknown))
					{
						line.AddEvents((int)Gdk.EventMask.ButtonPressMask);
						rela = true;
					}
					lineData = new Pair<int,bool>();
					lineData.First = i * 2 + 2;
					lineData.Second = rela;
					_lines[line] = lineData;
					
					table.Attach(line, x, x + w, y, y + h, Gtk.AttachOptions.Fill, Gtk.AttachOptions.Fill, 0, 0);
					
					xmax = (uint)Math.Max(xmax, x + w);
					ymax = (uint)Math.Max(ymax, y + h);
				}
				
				// marriage data
				if (_showMarriageData && positions[i].Length > 3)
				{
					string  text = string.Empty;
					if (famLink != null && (famLink.Pedigree == PedegreeLinkageType.Birth || famLink.Pedigree == PedegreeLinkageType.Unknown))
					{
						text = "foo";
					}
					Gtk.Label label = new Gtk.Label(text);
					label.Justify = Gtk.Justification.Left;
					label.LineWrap = true;
					label.SetAlignment(0.1F, 0.5F);
					
					x = positions[i][3][0] + 1;
					y = positions[i][3][1] + 1;
					w = positions[i][3][2];
					h = positions[i][3][3];
					
					table.Attach(label, x, x + w, y, y + h, Gtk.AttachOptions.Fill, Gtk.AttachOptions.Fill, 0, 0);
				}
			}
			
			// nav arrows
			if (lst[0] != null)
			{
				Gtk.Button arrowButton = new Gtk.Button();
				arrowButton.Add(new Gtk.Arrow(Gtk.ArrowType.Left, Gtk.ShadowType.In));
				
				arrowButton.Sensitive = (_dummyFam.Children.Count > 0);
				arrowButton.Clicked += ArrowButton_Click;
				if (arrowButton.Sensitive)
				{
					arrowButton.TooltipText = "Jump to child...";
				}
				
				uint ymid = (uint)Math.Floor(ymax / 2.0F);
				table.Attach(arrowButton, 0, 1, ymid, ymid + 1, 0, 0, 0, 0);
				
				// father
				arrowButton = new Gtk.Button();
				arrowButton.Add(new Gtk.Arrow(Gtk.ArrowType.Right, Gtk.ShadowType.In));
				arrowButton.Sensitive = (lst[1] != null);
				arrowButton.Clicked += FatherButton_Click;
				if (arrowButton.Sensitive)
				{
					arrowButton.TooltipText = "Jump to father";
				}
				
				ymid = (uint)Math.Floor(ymax / 4.0F);
				table.Attach(arrowButton, xmax, xmax + 1, ymid - 1, ymid + 2, 0, 0, 0, 0);
				
				// mother
				arrowButton = new Gtk.Button();
				arrowButton.Add(new Gtk.Arrow(Gtk.ArrowType.Right, Gtk.ShadowType.In));
				arrowButton.Sensitive = (lst[2] != null);
				arrowButton.Clicked += MotherButton_Click;
				if (arrowButton.Sensitive)
				{
					arrowButton.TooltipText = "Jump to mother";
				}
				
				ymid = (uint)Math.Floor(ymax / 4.0F * 3);
				table.Attach(arrowButton, xmax, xmax + 1, ymid - 1, ymid + 2, 0, 0, 0, 0);
				
				
				// dummy widgets to allow pedigree to be centred
				Gtk.Label l = new Gtk.Label(string.Empty);
				table.Attach(l, 0, 1, 0, 1, Gtk.AttachOptions.Expand | Gtk.AttachOptions.Fill, Gtk.AttachOptions.Expand | Gtk.AttachOptions.Fill, 0, 0);
				l = new Gtk.Label(string.Empty);
				table.Attach(l, xmax, xmax + 1, ymax, ymax + 1, Gtk.AttachOptions.Expand | Gtk.AttachOptions.Fill, Gtk.AttachOptions.Expand | Gtk.AttachOptions.Fill, 0, 0);
	
				table.ShowAll();
			}
		}	
Exemple #5
0
		private void FindTree(GedcomIndividualRecord indi, int i, int depth, GedcomFamilyLink[] lst)
		{
			if (depth <= 5 && indi != null)
			{				
				List<GedcomFamilyLink> families = indi.ChildIn;
				if (families == null || families.Count == 0)
				{
					// indi doesn't exist as a child in any family, create
					// a dummy link record
					GedcomFamilyLink famLink = new GedcomFamilyLink();
					famLink.Indi = indi.XRefID;
					famLink.Pedigree = PedegreeLinkageType.Unknown;
					famLink.Database = _database;
					lst[i] = famLink;
				}
				else
				{
					GedcomFamilyLink famLink = families[0];
										
					lst[i] = famLink;
					
					GedcomFamilyRecord famRec = (GedcomFamilyRecord)_database[famLink.Family];
					
					if (!string.IsNullOrEmpty(famRec.Husband))
					{
						indi = (GedcomIndividualRecord)_database[famRec.Husband];
						FindTree(indi, (2*i)+1, depth + 1, lst);
					}
					if (!string.IsNullOrEmpty(famRec.Wife))
					{
						indi = (GedcomIndividualRecord)_database[famRec.Wife];
						FindTree(indi, (2*i)+2, depth + 1, lst);
					}
				}
			}
		}
Exemple #6
0
		private void BuildTrees()
		{
			uint[][][] pos2 = new uint[][][] {
				new uint[][] { new uint[] { 0, 3, 3, 3 }, new uint[] { 1, 0, 3 }, new uint[] { 1, 6, 4 }, new uint[] { 3, 3, 2, 3 } },
				new uint[][] { new uint[] { 2, 0, 3, 3 } },
				new uint[][] { new uint[] { 2, 6, 3, 3 } }
			};
			
			uint[][][] pos3 = new uint[][][] {
				new uint[][] { new uint[] { 0, 4, 3, 5 }, new uint[] { 1, 1, 3 }, new uint[] { 1, 9, 4 }, new uint[] { 3, 5, 2, 3 } },
				new uint[][] { new uint[] { 2, 1, 3, 3 }, new uint[] { 3, 0, 1 }, new uint[] { 3, 4, 1 }, new uint[] { 5, 1, 2, 3 } },
				new uint[][] { new uint[] { 2, 9, 3, 3 }, new uint[] { 3, 8, 1 }, new uint[] { 3, 12, 1 }, new uint[] { 5, 9, 2, 3 } },
				new uint[][] { new uint[] { 4, 0, 3, 1 } },
				new uint[][] { new uint[] { 4, 4, 3, 1 } },
				new uint[][] { new uint[] { 4, 8, 3, 1 } },
				new uint[][] { new uint[] { 4, 12, 3, 1 } }
			};
			
			uint[][][] pos4 = new uint[][][] {
				new uint[][] { new uint[] { 0, 5, 3, 5 }, new uint[] { 1, 2, 3 }, new uint[] { 1, 10, 4 }, new uint[] { 3, 6, 2, 3 } },
				new uint[][] { new uint[] { 2, 2, 3, 3 }, new uint[] { 3, 1, 1 }, new uint[] { 3, 5, 1 }, new uint[] { 5, 3, 2, 1 } },
				new uint[][] { new uint[] { 2, 10, 3, 3 }, new uint[] { 3, 9, 1 }, new uint[] { 3, 13, 1 }, new uint[] { 5, 11, 2, 1 } },
				new uint[][] { new uint[] { 4, 1, 3, 1 }, new uint[] { 5, 0, 1 }, new uint[] { 5, 2, 1 }, new uint[] { 7, 1, 2, 1 } },
				new uint[][] { new uint[] { 4, 5, 3, 1 }, new uint[] { 5, 4, 1 }, new uint[] { 5, 6, 1 }, new uint[] { 7, 5, 2, 1 } },
				new uint[][] { new uint[] { 4, 9, 3, 1 }, new uint[] { 5, 8, 1 }, new uint[] { 5, 10, 1 }, new uint[] { 7, 9, 2, 1 } },
				new uint[][] { new uint[] { 4, 13, 3, 1 }, new uint[] { 5, 12, 1 }, new uint[] { 5, 14, 1 }, new uint[] { 7, 13, 2, 1 } },
				new uint[][] { new uint[] { 6, 0, 3, 1 } },
				new uint[][] { new uint[] { 6, 2, 3, 1 } },
				new uint[][] { new uint[] { 6, 4, 3, 1 } },
				new uint[][] { new uint[] { 6, 6, 3, 1 } },
				new uint[][] { new uint[] { 6, 8, 3, 1 } },
				new uint[][] { new uint[] { 6, 10, 3, 1 } },
				new uint[][] { new uint[] { 6, 12, 3, 1 } },
				new uint[][] { new uint[] { 6, 14, 3, 1 } }
			};
			
			uint[][][] pos5 = new uint[][][] {
				new uint[][] { new uint[] {0, 10, 3, 11},  new uint[] {1, 5, 5},  new uint[] {1, 21, 5},  new uint[] {3, 13, 2, 5} }, 
				new uint[][] { new uint[] {2, 5, 3, 5},  new uint[] {3, 2, 3},  new uint[] {3, 10, 3},  new uint[] {5,  6, 2, 3} }, 
				new uint[][] { new uint[] {2, 21, 3, 5},  new uint[] {3, 18, 3},  new uint[] {3, 26, 3},  new uint[] {5, 22, 2, 3} }, 
				new uint[][] { new uint[] {4,  2, 3, 3},  new uint[] {5, 1, 1},  new uint[] {5, 5, 1},  new uint[] {7, 3, 2, 1} }, 
				new uint[][] { new uint[] {4, 10, 3, 3},  new uint[] {5, 9, 1},  new uint[] {5, 13, 1},  new uint[] {7, 11, 2, 1} }, 
				new uint[][] { new uint[] {4, 18, 3, 3},  new uint[] {5, 17, 1},  new uint[] {5, 21, 1},  new uint[] {7, 19, 2, 1} }, 
				new uint[][] { new uint[] {4, 26, 3, 3},  new uint[] {5, 25, 1},  new uint[] {5, 29, 1},  new uint[] {7, 27, 2, 1} }, 
				new uint[][] { new uint[] {6,  1, 3, 1},  new uint[] {7, 0, 1},  new uint[] {7, 2, 1},  new uint[] {9, 1, 2, 1} }, 
				new uint[][] { new uint[] {6,  5, 3, 1},  new uint[] {7, 4, 1},  new uint[] {7, 6, 1},  new uint[] {9, 5, 2, 1} }, 
				new uint[][] { new uint[] {6,  9, 3, 1},  new uint[] {7, 8, 1},  new uint[] {7, 10, 1},  new uint[] {9, 9, 2, 1} }, 
				new uint[][] { new uint[] {6, 13, 3, 1},  new uint[] {7, 12, 1},  new uint[] {7, 14, 1},  new uint[] {9, 13, 2, 1} }, 
				new uint[][] { new uint[] {6, 17, 3, 1},  new uint[] {7, 16, 1},  new uint[] {7, 18, 1},  new uint[] {9, 17, 2, 1} }, 
				new uint[][] { new uint[] {6, 21, 3, 1},  new uint[] {7, 20, 1},  new uint[] {7, 22, 1},  new uint[] {9, 21, 2, 1} }, 
				new uint[][] { new uint[] {6, 25, 3, 1},  new uint[] {7, 24, 1},  new uint[] {7, 26, 1},  new uint[] {9, 25, 2, 1} }, 
				new uint[][] { new uint[] {6, 29, 3, 1},  new uint[] {7, 28, 1},  new uint[] {7, 30, 1},  new uint[] {9, 29, 2, 1} }, 
				new uint[][] { new uint[] {8,  0, 3, 1}}, 
				new uint[][] { new uint[] {8,  2, 3, 1}}, 
				new uint[][] { new uint[] {8,  4, 3, 1}}, 
				new uint[][] { new uint[] {8,  6, 3, 1}}, 
				new uint[][] { new uint[] {8,  8, 3, 1}}, 
				new uint[][] { new uint[] {8, 10, 3, 1}}, 
				new uint[][] { new uint[] {8, 12, 3, 1}}, 
				new uint[][] { new uint[] {8, 14, 3, 1}}, 
				new uint[][] { new uint[] {8, 16, 3, 1}}, 
				new uint[][] { new uint[] {8, 18, 3, 1}}, 
				new uint[][] { new uint[] {8, 20, 3, 1}}, 
				new uint[][] { new uint[] {8, 22, 3, 1}}, 
				new uint[][] { new uint[] {8, 24, 3, 1}}, 
				new uint[][] { new uint[] {8, 26, 3, 1}}, 
				new uint[][] { new uint[] {8, 28, 3, 1}}, 
				new uint[][] { new uint[] {8, 30, 3, 1}}
			};
		
			GedcomFamilyLink[] lst = new GedcomFamilyLink[32];
			GedcomIndividualRecord indi = (GedcomIndividualRecord)_record;
			FindTree(indi, 0, 1, lst);
			
			_lines.Clear();
			_fatherID = string.Empty;
			_motherID = string.Empty;
			
			if (!string.IsNullOrEmpty(lst[0].Family))
			{
				GedcomFamilyRecord fam = (GedcomFamilyRecord)_database[lst[0].Family];
				
				_fatherID = fam.Husband;
				_motherID = fam.Wife;
			}
			
			Rebuild(_table2, pos2, indi, lst);
			Rebuild(_table3, pos3, indi, lst);
			Rebuild(_table4, pos4, indi, lst);
			Rebuild(_table5, pos5, indi, lst);
			
			this.ShowAll();
		}
		public void ChangeWife(GedcomIndividualRecord indi)
		{
			GedcomIndividualRecord husband = null; 
			GedcomIndividualRecord wife = null;
			
			if (!string.IsNullOrEmpty(_Husband))
			{
				husband = _database[_Husband] as GedcomIndividualRecord;
			}
			if (!string.IsNullOrEmpty(_Wife))
			{
				wife = _database[_Wife] as GedcomIndividualRecord;
			}
			
			if (string.IsNullOrEmpty(XRefID))
			{
				XRefID = _database.GenerateXref("FAM");
				_database.Add(XRefID,this);
			}
			
			if (wife != null)
			{
				GedcomFamilyLink link;
				if (wife.SpouseInFamily(XRefID,out link))
				{
					wife.SpouseIn.Remove(link);	
				}
			}
			
			wife = indi;
			_Wife = string.Empty;
			
			if (husband != null)
			{
				_Husband = husband.XRefID;
				
				if (!husband.SpouseInFamily(XRefID))
				{						
					GedcomFamilyLink link = new GedcomFamilyLink();
					link.Database = _database;
					link.Family = XRefID;
					link.Indi = _Husband;
					husband.SpouseIn.Add(link);
				}	
			}
			
			if (wife != null)
			{
				_Wife = wife.XRefID;
				
				if (!wife.SpouseInFamily(XRefID))
				{						
					GedcomFamilyLink link = new GedcomFamilyLink();
					link.Database = _database;
					link.Family = XRefID;
					link.Indi = _Wife;
					wife.SpouseIn.Add(link);
				}	
			}	
		}
		public bool AddChild(GedcomIndividualRecord indi)
		{
			bool added = false;
			
			if (indi != null && ! Children.Contains(indi.XRefID))
			{
				if (string.IsNullOrEmpty(XRefID))
				{
					XRefID = _database.GenerateXref("FAM");
					_database.Add(XRefID,this);						
				}
				
				if (!indi.ChildInFamily(XRefID))
				{
					GedcomFamilyLink link = new GedcomFamilyLink();
					link.Database = _database;
					link.Family = XRefID;
					link.Indi = indi.XRefID;
					link.Level = 1;
					indi.ChildIn.Add(link);
				}
				
				Children.Add(indi.XRefID);
				
				added = true;
			}
			
			return added;
		}
		/// <summary>
		/// Starts reading the specified gedcom file
		/// </summary>
		/// <param name="gedcomFile">Filename to read</param>
		/// <returns>bool indicating if the file was successfully read</returns>
		public bool ReadGedcom(string gedcomFile)
		{
			bool success = false;

			_gedcomFile = gedcomFile;
			
			_percent = 0;

			FileInfo info = new FileInfo(gedcomFile);
			long fileSize = info.Length;
			long read = 0;
			
			_missingReferences = new List<string>();
			_sourceCitations = new List<GedcomSourceCitation>();
			_repoCitations = new List<GedcomRepositoryCitation>();
			
			try
			{
				_stream = null;
				Encoding enc = Encoding.Default;
				
				using (FileStream fileStream = File.OpenRead(gedcomFile))
				{			
					ResetParse();
			
					byte[] bom = new byte[4];
				
					fileStream.Read(bom, 0, 4);
				
					// look for BOMs, if found we will ignore the CHAR tag
					// don't use .net look for bom as we also want to detect
					// unicode where there isn't a BOM, as far as the parser
					// is concerned the data is utf16le if we detect this way
					// as the conversion is already done
				
					if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF)
					{
						_Parser.Charset = GedcomCharset.UTF16LE;
						enc = Encoding.UTF8;
					}
					else if (bom[0] == 0xFE && bom[1] == 0xFF)
					{
						_Parser.Charset = GedcomCharset.UTF16LE;
						enc = Encoding.BigEndianUnicode;
					}
					else if (bom[0] == 0xFF && bom[1] == 0xFE && bom[2] == 0x00 && bom[3] == 0x00)
					{
						_Parser.Charset = GedcomCharset.UTF16LE;
						enc = Encoding.UTF32;
					}
					else if (bom[0] == 0xFF && bom[1] == 0xFE)
					{
						_Parser.Charset = GedcomCharset.UTF16LE;
						enc = Encoding.Unicode;
					}
					else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xFE && bom[3] == 0xFF)
					{
						_Parser.Charset = GedcomCharset.UTF16LE;
						enc = Encoding.UTF32;
					}
					else if (bom[0] == 0x00 && bom[2] == 0x00)
					{
						_Parser.Charset = GedcomCharset.UTF16LE;
						enc = Encoding.BigEndianUnicode;
					}
					else if (bom[1] == 0x00 && bom[3] == 0x00)
					{
						_Parser.Charset = GedcomCharset.UTF16LE;
						enc = Encoding.Unicode;
					}
				}
							
				_stream = new StreamReader(gedcomFile, enc);
					
				while (!_stream.EndOfStream)
				{
					string line = _stream.ReadLine();

					if (line != null)
					{
						// file may not have same newline as environment so this isn't 100% correct
						read += line.Length + Environment.NewLine.Length;
                        try
                        {
                            _Parser.GedcomParse(line);
                        }
                        catch { }
						line = null;
						
						// to allow for inaccuracy above
						int percentDone = (int)Math.Min(100, (read * 100.0F)/fileSize);
						if (percentDone != _percent)
						{
							_percent = percentDone;
							if (PercentageDone != null)
							{
								PercentageDone(this, EventArgs.Empty);
							}
						}
					}
				}
				Flush();
			}
			finally
			{
				if (_stream != null)
				{
					_stream.Dispose();	
				}
			}

			success = (_Parser.ErrorState == GedcomErrorState.NoError);
			
			if (success)
			{
				_percent = 100;

				// cleanup header record, don't want submitter record or content description in the main
				// database submitters / notes
				GedcomHeader header = Database.Header;
				
				if (header != null)
				{
					if (header.Notes.Count > 0)
					{
						string xref = header.Notes[0];

						// belongs in content description, not top level record notes
					    header.Notes.Remove(xref);
						header.ContentDescription = (GedcomNoteRecord)Database[xref];

						// fix up level, note is inline in the header + remove from database
						// list of notes
						header.ContentDescription.Level = 1;
						header.ContentDescription.XRefID = string.Empty;
						//Database.Remove(xref, header.ContentDescription);
						
					}

					// brothers keeper doesn't output a source name, so set the name to
					// the same as the ID if it is empty
					if (string.IsNullOrEmpty(header.ApplicationName) && !string.IsNullOrEmpty(header.ApplicationSystemID))
					{
						header.ApplicationName = header.ApplicationSystemID;
					}
				}
				
				// add any missing child in and spouse in linkage
				foreach (GedcomFamilyRecord family in Database.Families)
				{
					string husbandID = family.Husband;
					if (!string.IsNullOrEmpty(husbandID))
					{
						GedcomIndividualRecord husband = Database[husbandID] as GedcomIndividualRecord;
						if (husband != null)
						{
							GedcomFamilyLink famLink = null;
							
							if (!husband.SpouseInFamily(family.XRefID, out famLink))
							{
								famLink = new GedcomFamilyLink();
								famLink.Database = Database;
								famLink.Family = family.XRefID;
								famLink.Indi = husbandID;
								famLink.Level = 1;
								famLink.PreferedSpouse = (husband.SpouseIn.Count == 0);
								husband.SpouseIn.Add(famLink);
							}
						}
						else
						{
							System.Diagnostics.Debug.WriteLine("Husband in family points to non individual record");	
						}
					}
					
					string wifeID = family.Wife;
					if (!string.IsNullOrEmpty(wifeID))
					{
						GedcomIndividualRecord wife = Database[wifeID] as GedcomIndividualRecord;
						if (wife != null)
						{
							GedcomFamilyLink famLink = null;
							
							if (!wife.SpouseInFamily(family.XRefID, out famLink))
							{
								famLink = new GedcomFamilyLink();
								famLink.Database = Database;
								famLink.Family = family.XRefID;
								famLink.Indi = wifeID;
								famLink.Level = 1;
								wife.SpouseIn.Add(famLink);
							}
						}
						else
						{
							System.Diagnostics.Debug.WriteLine("Wife in family points to non individual record");	
						}
					}
					
					foreach (string childID in family.Children)
					{	
						GedcomIndividualRecord child = Database[childID] as GedcomIndividualRecord;
					
						if (child != null)
						{
							GedcomFamilyLink famLink = null;
							
							// add a family link record if one doesn't already exist
							if (!child.ChildInFamily(family.XRefID, out famLink))
							{		
								famLink = new GedcomFamilyLink();
								famLink.Database = Database;
								famLink.Family = family.XRefID;
								famLink.Indi = childID;
								famLink.Level = 1;
								famLink.Status = ChildLinkageStatus.Unknown;
								// pedigree now set below
								child.ChildIn.Add(famLink);
							}
							
							// set pedigree here to allow for ADOP/FOST in the FAM tag
							// FAM record overrides link status if they differ
							famLink.Pedigree = family.GetLinkageType(childID);
							famLink.FatherPedigree = family.GetHusbandLinkageType(childID);
							famLink.MotherPedigree = family.GetWifeLinkageType(childID);
							
							// check BIRT event for a FAMC record, check ADOP for FAMC / ADOP records
							foreach (GedcomIndividualEvent indiEv in child.Events)
							{
								if (indiEv.Famc == family.XRefID)
								{
									switch (indiEv.EventType)
									{
										case GedcomEvent.GedcomEventType.BIRT:
											// BIRT records do not state father/mother birth,
											// all we can say is both are natural
											famLink.Pedigree = PedegreeLinkageType.Birth;
											break;
										case GedcomEvent.GedcomEventType.ADOP:
											switch (indiEv.AdoptedBy)
											{
												case Gedcom.GedcomAdoptionType.Husband:
													famLink.FatherPedigree = PedegreeLinkageType.Adopted;
													break;
												case Gedcom.GedcomAdoptionType.Wife:
													famLink.MotherPedigree = PedegreeLinkageType.Adopted;
													break;
												case Gedcom.GedcomAdoptionType.HusbandAndWife:
												default:
													// default is both as well, has to be adopted by someone if
													// there is an event on the family.
													famLink.Pedigree = PedegreeLinkageType.Adopted;
													break;
											}
											break;
									}
								}
							}
						}
						else
						{
							System.Diagnostics.Debug.WriteLine("Child in family points to non individual record");	
						}
					}
					family.ClearLinkageTypes();
				}
			
				// look for any broken references / update ref counts
				foreach (string xref in _missingReferences)
				{
					GedcomRecord record = Database[xref];
					if (record != null)
					{
						switch (record.RecordType)
						{
							case GedcomRecordType.Individual:
								// FIXME: don't increase ref count on individuals,
								// a bit of a hack, only place where it may be
								// needed is on assocciations
								break;
							case GedcomRecordType.Family:
								// FIXME: don't increase ref count on families
								break;
							default:
								record.RefCount ++;
								break;
						}
					}
					else if (!_removedNotes.Contains(xref))
					{
						System.Diagnostics.Debug.WriteLine("Missing reference: " + xref);
					}
				}
				//System.Console.WriteLine("Removed " + _removedNotes.Count + " notes");
				_missingReferences = null;
			
				// link sources with citations which reference them
				foreach (GedcomSourceCitation citation in _sourceCitations)
				{
					GedcomSourceRecord source = Database[citation.Source] as GedcomSourceRecord;
					if (source != null)
					{
						source.Citations.Add(citation);
					}
					else
					{
						System.Diagnostics.Debug.WriteLine("Missing source reference: " + citation.Source);
					}
				}
				_sourceCitations = null;
				
				// link repos with citations which reference them
				foreach (GedcomRepositoryCitation citation in _repoCitations)
				{
					GedcomRepositoryRecord repo = Database[citation.Repository] as GedcomRepositoryRecord;
					if (repo != null)
					{
						repo.Citations.Add(citation);
					}
					else
					{
						System.Diagnostics.Debug.WriteLine("Missing repo reference: " + citation.Repository);
					}
				}
				_repoCitations = null;
	
				// find any sources without a title and give them one, happens with Database1.ged,
				// could be bad parsing, not sure, try and make up for it anyway
				int missingSourceTitleCount = 1;
				foreach (GedcomSourceRecord source in Database.Sources)
				{
					if (string.IsNullOrEmpty(source.Title))
					{
						source.Title = string.Format("Source {0}", missingSourceTitleCount ++);
					}
				}
				
				Database.Name = gedcomFile;

			}
			
			if (PercentageDone != null)
			{
				PercentageDone(this, EventArgs.Empty);
			}

			Database.Loading = false;
			
			return success;
			
		}
		private void ReadIndividualRecord()
		{
			GedcomIndividualRecord individualRecord;
			
			individualRecord = _ParseState.Records.Peek() as GedcomIndividualRecord;
			
			GedcomIndividualEvent individualEvent;

			// some custom tags we convert to generic facts/events
			// this means we have to set the line value to the type
			// they represent, so store the real line value and use
			// for the event classification.
			string customToGenericClassification = string.Empty;
			
			if (_tag.StartsWith("_"))
			{
				switch (_tag)
				{
					// we convert _MILT to EVEN Military Service
					case "_MILT":
						_tag = "EVEN";
						_lineValue = "Military Service";
						_lineValueType = GedcomLineValueType.DataType;
						break;
					// we convert _MDCL to FACT Medical
					case "_MDCL":
						_tag = "FACT";
						customToGenericClassification = _lineValue;
						_lineValue = "Medical";
						_lineValueType = GedcomLineValueType.DataType;
						break;
					// we convert _HEIG to FACT Height
					case "_HEIG":
						_tag = "FACT";
						customToGenericClassification = _lineValue;
						_lineValue = "Height";
						_lineValueType = GedcomLineValueType.DataType;
						break;
					// we convert _WEIG to FACT Weight
					case "_WEIG":
						_tag = "FACT";
						customToGenericClassification = _lineValue;
						_lineValue = "Weight";
						_lineValueType = GedcomLineValueType.DataType;
						break;
					default:
						GedcomCustomRecord custom = new GedcomCustomRecord();
						custom.Level = _level;
						custom.XRefID = _xrefID;
						custom.Tag = _tag;
									
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							custom.Classification = _lineValue;
						}
						
						// FIXME: may want to use customs at some point
						//individualRecord.Events.Add(custom);
		
						_ParseState.Records.Push(custom);
						break;
				}
			}
			if (_level == individualRecord.ParsingLevel + 1)
			{
				switch (_tag)
				{
					case "FAMC":
						if (_lineValueType == GedcomLineValueType.PointerType)
						{
							GedcomFamilyLink childIn = new GedcomFamilyLink();
							childIn.Level = _level;
							childIn.Family = _lineValue;
							childIn.Indi = individualRecord.XRefID;
							
							_missingReferences.Add(_lineValue);
							
							individualRecord.ChildIn.Add(childIn);
							_ParseState.Records.Push(childIn);
							
						}
						break;
					case "FAMS":
						if (_lineValueType == GedcomLineValueType.PointerType)
						{
							GedcomFamilyLink spouseIn = new GedcomFamilyLink();
							spouseIn.Level = _level;
							spouseIn.Family = _lineValue;
							spouseIn.Indi = individualRecord.XRefID;
							spouseIn.PreferedSpouse = (individualRecord.SpouseIn.Count == 0);
							
							_missingReferences.Add(_lineValue);
							
							individualRecord.SpouseIn.Add(spouseIn);
							_ParseState.Records.Push(spouseIn);
						}
						break;
					case "ASSO":
						if (_lineValueType == GedcomLineValueType.PointerType)
						{
							GedcomAssociation association = new GedcomAssociation();
							association.Level = _level;
							association.Individual = _lineValue;
							
							_missingReferences.Add(_lineValue);
							
							individualRecord.Associations.Add(association);
							_ParseState.Records.Push(association);
						}
						break;
					case "RESN":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							try
							{
								individualRecord.RestrictionNotice = EnumHelper.Parse<GedcomRestrictionNotice>(_lineValue,true);
							}
							catch
							{
								Debug.WriteLine("Invalid restriction type: " + _lineValue);
								
								// default to confidential to protect privacy
								individualRecord.RestrictionNotice = GedcomRestrictionNotice.Confidential;
							}
						}
						break;
					case "NAME":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							GedcomName name = new GedcomName();
							name.Database = _ParseState.Database;
							name.Level = _level;
							name.Name = _lineValue;
							name.PreferedName = (individualRecord.Names.Count == 0);

							individualRecord.Names.Add(name);
							_ParseState.Records.Push(name);
						}
						break;
					// Invalid, but seen from Family Origins, Family Tree Maker, Personal Ancestral File, and Legacy
					case "AKA":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							GedcomName name = new GedcomName();
							name.Database = _ParseState.Database;
							name.Level = _level;
							name.Name = _lineValue;
							name.Type = "aka";
							name.PreferedName = (individualRecord.Names.Count == 0);
							individualRecord.Names.Add(name);
						}
						break;
					case "SEX":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							switch (_lineValue)
							{
								case "M":
									individualRecord.Sex = GedcomSex.Male;
									break;
								case "F":
									individualRecord.Sex = GedcomSex.Female;
									break;
								// non standard
								case "B":
									individualRecord.Sex = GedcomSex.Both;
									break;
								// non standard
								case "N":
									individualRecord.Sex = GedcomSex.Neuter;
									break;
								// non standard
								case "U":
									individualRecord.Sex = GedcomSex.Undetermined;
									break;
							}
						}
						break;
					case "SUBM":
						if (_lineValueType == GedcomLineValueType.PointerType)
						{
							individualRecord.SubmitterRecords.Add(_lineValue);
							_missingReferences.Add(_lineValue);
						}
						else
						{
							GedcomSubmitterRecord submitter = new GedcomSubmitterRecord();
							submitter.Level = 0; // new top level submitter, always 0
							submitter.ParsingLevel = _level;
							submitter.XRefID = Database.GenerateXref("SUBM");
							
							_ParseState.Records.Push(submitter);
							
							individualRecord.SubmitterRecords.Add(submitter.XRefID);
						}
						break;
					case "ALIA":
						if (_lineValueType == GedcomLineValueType.PointerType)
						{
							individualRecord.Alia.Add(_lineValue);	
							_missingReferences.Add(_lineValue);
						}
						else if (_lineValueType == GedcomLineValueType.DataType)
						{
							// Family Tree Maker doing this?
							// ALIA is unsupported in gedcom 5.5 as a way of
							// adding multiple names, the spec
							// does say it should be a pointer to an individual
							// though, not another name.
							// spec allows multiple NAME though, so add one
							// with this name
							GedcomName name = new GedcomName();
							name.Database = _ParseState.Database;
							name.Level = _level;
							name.Name = _lineValue;
							name.Type = "aka";
							name.PreferedName = (individualRecord.Names.Count == 0);
							individualRecord.Names.Add(name);
						}
						break;
					case "ANCI":
						if (_lineValueType == GedcomLineValueType.PointerType)
						{
							individualRecord.Anci.Add(_lineValue);	
							_missingReferences.Add(_lineValue);
						}
						break;
					case "DESI":
						if (_lineValueType == GedcomLineValueType.PointerType)
						{
							individualRecord.Desi.Add(_lineValue);	
							_missingReferences.Add(_lineValue);
						}
						break;
					case "RFN":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualRecord.PermanentRecordFileNumber = _lineValue;	
						}
						break;
					case "AFN":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualRecord.AncestralFileNumber = _lineValue;	
						}
						break;
					case "REFN":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualRecord.UserReferenceNumber = _lineValue;
						}
						break;
					case "RIN":
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualRecord.AutomatedRecordID = _lineValue;
						}
						break;
					case "CHAN":
						GedcomChangeDate date = new GedcomChangeDate(Database);
						date.Level = _level;
						_ParseState.Records.Push(date);
						break;
					case "NOTE":
					    AddNoteRecord(individualRecord);
						break;
					case "SOUR":
					    AddSourceCitation(individualRecord);
						break;
					case "OBJE":
						AddMultimediaRecord(individualRecord);
						break;
					case "BIRT":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.BIRT;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "CHR":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.CHR;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "DEAT":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.DEAT;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "BURI":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.BURI;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "CREM":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.CREM;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "ADOP":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.ADOP;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "BAPM":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.BAPM;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "BARM":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.BARM;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "BASM":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.BASM;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "BLES":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.BLES;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "CHRA":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.CHRA;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "CONF":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.CONF;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "FCOM":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.FCOM;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "ORDN":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.ORDN;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "NATU":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.NATU;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "EMIG":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.EMIG;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "IMMI":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.IMMI;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "CENS":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.CENS;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "PROB":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.PROB;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;	
					case "WILL":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.WILL;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "GRAD":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.GRAD;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "RETI":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.RETI;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "EVEN":
						
						// event
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.GenericEvent;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Events.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "CAST":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.CASTFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "DSCR":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.DSCRFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;	
					case "EDUC":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.EDUCFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;					
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "IDNO":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.IDNOFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "NATI":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.NATIFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "NCHI":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.NCHIFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "NMR":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.NMRFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "OCCU":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.OCCUFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "PROP":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.PROPFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "RELI":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.RELIFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "RESI":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.RESIFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;	
					case "SSN":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.SSNFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;		
					case "TITL":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.TITLFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;
						}
					
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
					case "FACT":
						
						// fact
						individualEvent = new GedcomIndividualEvent();
						individualEvent.EventType = GedcomEvent.GedcomEventType.GenericFact;
						individualEvent.Level = _level;
						individualEvent.IndiRecord = individualRecord;
					
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualEvent.EventName = _lineValue;						
						}
						if (!string.IsNullOrEmpty(customToGenericClassification))
						{
							individualEvent.Classification = customToGenericClassification;
						}
						individualRecord.Attributes.Add(individualEvent);
					
						_ParseState.Records.Push(individualEvent);
					
						break;
										
					// Not according to the spec, but Family Tree Maker sticks
					// an address under an individual so we will support reading it
					case "ADDR":
						if (individualRecord.Address == null)
						{
							individualRecord.Address = new GedcomAddress();
							individualRecord.Address.Database = Database;
						}
						
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							individualRecord.Address.AddressLine = _lineValue;
						}
						
						break;
					case "PHON":
						if (individualRecord.Address == null)
						{
							individualRecord.Address = new GedcomAddress();	
							individualRecord.Address.Database = Database;
						}
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							if (string.IsNullOrEmpty(individualRecord.Address.Phone1))
							{
								individualRecord.Address.Phone1 = _lineValue;
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Phone2))
							{
								individualRecord.Address.Phone2 = _lineValue;	
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Phone3))
							{
								individualRecord.Address.Phone3 = _lineValue;
							}
							else
							{
								// should never occur only 3 phone numbers are allowed	
							}
						}
						break;
					case "EMAIL":
						if (individualRecord.Address == null)
						{
							individualRecord.Address = new GedcomAddress();	
							individualRecord.Address.Database = Database;
						}
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							if (string.IsNullOrEmpty(individualRecord.Address.Email1))
							{
								individualRecord.Address.Email1 = _lineValue;
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Email2))
							{
								individualRecord.Address.Email2 = _lineValue;	
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Email3))
							{
								individualRecord.Address.Email3 = _lineValue;	
							}
							else
							{
								// should never occur only 3 emails are allowed	
							}
						}
						break;
					case "FAX":
						if (individualRecord.Address == null)
						{
							individualRecord.Address = new GedcomAddress();	
							individualRecord.Address.Database = Database;
						}
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							if (string.IsNullOrEmpty(individualRecord.Address.Fax1))
							{
								individualRecord.Address.Fax1 = _lineValue;
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Fax2))
							{
								individualRecord.Address.Fax2 = _lineValue;	
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Fax3))
							{
								individualRecord.Address.Fax3 = _lineValue;	
							}
							else
							{
								// should never occur only 3 fax numbers are allowed	
							}
						}
						break;
					case "WWW":
						if (individualRecord.Address == null)
						{
							individualRecord.Address = new GedcomAddress();	
							individualRecord.Address.Database = Database;
						}
						if (_lineValueType == GedcomLineValueType.DataType)
						{
							if (string.IsNullOrEmpty(individualRecord.Address.Www1))
							{
								individualRecord.Address.Www1 = _lineValue;	
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Www2))
							{
								individualRecord.Address.Www2 = _lineValue;	
							}
							else if (string.IsNullOrEmpty(individualRecord.Address.Www3))
							{
								individualRecord.Address.Www3 = _lineValue;	
							}
							else
							{
								// should never occur only 3 urls are allowed	
							}
						}
						break;
				}
			}
			else if ( (!string.IsNullOrEmpty(_ParseState.PreviousTag)) &&
			            _level == _ParseState.PreviousLevel + 1)
			{
				string pTag = _ParseState.PreviousTag;
				
				if (pTag == "REFN" && _tag == "TYPE")
				{
					if (_lineValueType == GedcomLineValueType.DataType)
					{
						individualRecord.UserReferenceType = _lineValue;	
					}
				}
				else
				{
					AddressParse(individualRecord.Address, _tag, _lineValue, _lineValueType);	
				}
			}
			else if ( (!string.IsNullOrEmpty(_ParseState.PreviousTag)) &&
			            _level == _ParseState.PreviousLevel)
			{
				AddressParse(individualRecord.Address, _tag, _lineValue, _lineValueType);
			}
			else
			{
				// shouldn't be here
				Debug.WriteLine("Unknown state / tag parsing individual (" + individualRecord.XRefID + ") node: " + _tag + "\t at level: " + _level);
				System.Console.WriteLine("Unknown state / tag parsing individual (" + individualRecord.XRefID + ") node: " + _tag + "\t at level: " + _level);
				System.Console.WriteLine("Previous tag: " + _ParseState.PreviousTag + "\tPrevious Level: " + _ParseState.PreviousLevel);
			}
		}