#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os
import sys
import locale
import sets
from TransUtils import sgettext as _
#-------------------------------------------------------------------------
#
# GNOME/GTK
#
#-------------------------------------------------------------------------
import gtk
import gtk.gdk
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
import const
import Mime
import NameDisplay
import RelLib
import Errors
from QuestionDialog import WarningDialog
#-------------------------------------------------------------------------
#
# Integer to String mappings for constants
#
#-------------------------------------------------------------------------
gender = {
RelLib.Person.MALE : _("male"),
RelLib.Person.FEMALE : _("female"),
RelLib.Person.UNKNOWN : _("unknown"),
}
child_relations = {
RelLib.Person.CHILD_NONE : _("None"),
RelLib.Person.CHILD_BIRTH : _("Birth"),
RelLib.Person.CHILD_ADOPTED : _("Adopted"),
RelLib.Person.CHILD_STEPCHILD : _("Stepchild"),
RelLib.Person.CHILD_SPONSORED : _("Sponsored"),
RelLib.Person.CHILD_FOSTER : _("Foster"),
RelLib.Person.CHILD_UNKNOWN : _("Unknown"),
RelLib.Person.CHILD_CUSTOM : _("Custom"),
}
confidence = {
RelLib.SourceRef.CONF_VERY_HIGH : _("Very High"),
RelLib.SourceRef.CONF_HIGH : _("High"),
RelLib.SourceRef.CONF_NORMAL : _("Normal"),
RelLib.SourceRef.CONF_LOW : _("Low"),
RelLib.SourceRef.CONF_VERY_LOW : _("Very Low"),
}
family_events = {
RelLib.Event.UNKNOWN : _("Unknown"),
RelLib.Event.CUSTOM : _("Custom"),
RelLib.Event.MARRIAGE : _("Marriage"),
RelLib.Event.MARR_SETTL : _("Marriage Settlement"),
RelLib.Event.MARR_LIC : _("Marriage License"),
RelLib.Event.MARR_CONTR : _("Marriage Contract"),
RelLib.Event.MARR_BANNS : _("Marriage Banns"),
RelLib.Event.ENGAGEMENT : _("Engagement"),
RelLib.Event.DIVORCE : _("Divorce"),
RelLib.Event.DIV_FILING : _("Divorce Filing"),
RelLib.Event.ANNULMENT : _("Annulment"),
RelLib.Event.MARR_ALT : _("Alternate Marriage"),
}
personal_events = {
RelLib.Event.UNKNOWN : _("Unknown"),
RelLib.Event.CUSTOM : _("Custom"),
RelLib.Event.ADOPT : _("Adopted"),
RelLib.Event.BIRTH : _("Birth"),
RelLib.Event.DEATH : _("Death"),
RelLib.Event.ADULT_CHRISTEN : _("Adult Christening"),
RelLib.Event.BAPTISM : _("Baptism"),
RelLib.Event.BAR_MITZVAH : _("Bar Mitzvah"),
RelLib.Event.BAS_MITZVAH : _("Bas Mitzvah"),
RelLib.Event.BLESS : _("Blessing"),
RelLib.Event.BURIAL : _("Burial"),
RelLib.Event.CAUSE_DEATH : _("Cause Of Death"),
RelLib.Event.CENSUS : _("Census"),
RelLib.Event.CHRISTEN : _("Christening"),
RelLib.Event.CONFIRMATION : _("Confirmation"),
RelLib.Event.CREMATION : _("Cremation"),
RelLib.Event.DEGREE : _("Degree"),
RelLib.Event.DIV_FILING : _("Divorce Filing"),
RelLib.Event.EDUCATION : _("Education"),
RelLib.Event.ELECTED : _("Elected"),
RelLib.Event.EMIGRATION : _("Emigration"),
RelLib.Event.FIRST_COMMUN : _("First Communion"),
RelLib.Event.IMMIGRATION : _("Immigration"),
RelLib.Event.GRADUATION : _("Graduation"),
RelLib.Event.MED_INFO : _("Medical Information"),
RelLib.Event.MILITARY_SERV : _("Military Service"),
RelLib.Event.NATURALIZATION : _("Naturalization"),
RelLib.Event.NOB_TITLE : _("Nobility Title"),
RelLib.Event.NUM_MARRIAGES : _("Number of Marriages"),
RelLib.Event.OCCUPATION : _("Occupation"),
RelLib.Event.ORDINATION : _("Ordination"),
RelLib.Event.PROBATE : _("Probate"),
RelLib.Event.PROPERTY : _("Property"),
RelLib.Event.RELIGION : _("Religion"),
RelLib.Event.RESIDENCE : _("Residence"),
RelLib.Event.RETIREMENT : _("Retirement"),
RelLib.Event.WILL : _("Will")
}
personal_attributes = {
RelLib.Attribute.UNKNOWN : _("Unknown"),
RelLib.Attribute.CUSTOM : _("Custom"),
RelLib.Attribute.CASTE : _("Caste"),
RelLib.Attribute.DESCRIPTION : _("Description"),
RelLib.Attribute.ID : _("Identification Number"),
RelLib.Attribute.NATIONAL : _("National Origin"),
RelLib.Attribute.NUM_CHILD : _("Number of Children"),
RelLib.Attribute.SSN : _("Social Security Number"),
}
family_attributes = {
RelLib.Attribute.UNKNOWN : _("Unknown"),
RelLib.Attribute.CUSTOM : _("Custom"),
RelLib.Attribute.NUM_CHILD : _("Number of Children"),
}
family_relations = {
RelLib.Family.MARRIED : _("Married"),
RelLib.Family.UNMARRIED : _("Unmarried"),
RelLib.Family.CIVIL_UNION : _("Civil Union"),
RelLib.Family.UNKNOWN : _("Unknown"),
RelLib.Family.CUSTOM : _("Other"),
}
family_rel_descriptions = {
RelLib.Family.MARRIED : _("A legal or common-law relationship "
"between a husband and wife"),
RelLib.Family.UNMARRIED : _("No legal or common-law relationship "
"between man and woman"),
RelLib.Family.CIVIL_UNION : _("An established relationship between "
"members of the same sex"),
RelLib.Family.UNKNOWN : _("Unknown relationship between a man "
"and woman"),
RelLib.Family.CUSTOM : _("An unspecified relationship "
"a man and woman"),
}
name_types = {
RelLib.Name.UNKNOWN : _("Unknown"),
RelLib.Name.CUSTOM : _("Custom"),
RelLib.Name.AKA : _("Also Known As"),
RelLib.Name.BIRTH : _("Birth Name"),
RelLib.Name.MARRIED : _("Married Name"),
}
web_types = {
RelLib.Url.UNKNOWN : _("Unknown"),
RelLib.Url.CUSTOM : _("Custom"),
RelLib.Url.EMAIL : _("E-mail"),
RelLib.Url.WEB_HOME : _("Web Home"),
RelLib.Url.WEB_SEARCH : _("Web Search"),
RelLib.Url.WEB_FTP : _("FTP"),
}
source_media_types = {
RelLib.RepoRef.UNKNOWN : _("Unknown"),
RelLib.RepoRef.CUSTOM : _("Custom"),
RelLib.RepoRef.AUDIO : _("Audio"),
RelLib.RepoRef.BOOK : _("Book"),
RelLib.RepoRef.CARD : _("Card"),
RelLib.RepoRef.ELECTRONIC : _("Electronic"),
RelLib.RepoRef.FICHE : _("Fiche"),
RelLib.RepoRef.FILM : _("Film"),
RelLib.RepoRef.MAGAZINE : _("Magazine"),
RelLib.RepoRef.MANUSCRIPT : _("Manuscript"),
RelLib.RepoRef.MAP : _("Map"),
RelLib.RepoRef.NEWSPAPER : _("Newspaper"),
RelLib.RepoRef.PHOTO : _("Photo"),
RelLib.RepoRef.TOMBSTONE : _("Tombstone"),
RelLib.RepoRef.VIDEO : _("Video"),
}
event_roles = {
RelLib.EventRef.UNKNOWN : _("Unknown"),
RelLib.EventRef.CUSTOM : _("Custom"),
RelLib.EventRef.PRIMARY : _("Primary"),
RelLib.EventRef.CLERGY : _("Clergy"),
RelLib.EventRef.CELEBRANT : _("Celebrant"),
RelLib.EventRef.AIDE : _("Aide"),
RelLib.EventRef.BRIDE : _("Bride"),
RelLib.EventRef.GROOM : _("Groom"),
RelLib.EventRef.WITNESS : _("Witness"),
}
family_event_roles = {
RelLib.EventRef.UNKNOWN : _("Unknown"),
RelLib.EventRef.CUSTOM : _("Custom"),
RelLib.EventRef.FAMILY : _("Family"),
}
repository_types = {
RelLib.Repository.UNKNOWN : _("Unknown"),
RelLib.Repository.CUSTOM : _("Custom"),
RelLib.Repository.LIBRARY : _("Library"),
RelLib.Repository.CEMETERY : _("Cemetery"),
RelLib.Repository.CHURCH : _("Church"),
RelLib.Repository.ARCHIVE : _("Archive"),
RelLib.Repository.ALBUM : _("Album"),
RelLib.Repository.WEBSITE : _("Web site"),
RelLib.Repository.BOOKSTORE : _("Bookstore"),
RelLib.Repository.COLLECTION : _("Collection"),
RelLib.Repository.SAFE : _("Safe"),
}
marker_types = {
RelLib.PrimaryObject.MARKER_NONE : "",
RelLib.PrimaryObject.MARKER_CUSTOM : _("Custom"),
RelLib.PrimaryObject.MARKER_COMPLETE : _("Information is complete"),
RelLib.PrimaryObject.MARKER_TODO : _("TODO"),
}
#-------------------------------------------------------------------------
#
# Integer to GEDCOM tag mappings for constants
#
#-------------------------------------------------------------------------
familyConstantEvents = {
RelLib.Event.ANNULMENT : "ANUL",
RelLib.Event.DIV_FILING : "DIVF",
RelLib.Event.DIVORCE : "DIV",
RelLib.Event.ENGAGEMENT : "ENGA",
RelLib.Event.MARR_BANNS : "MARB",
RelLib.Event.MARR_CONTR : "MARC",
RelLib.Event.MARR_LIC : "MARL",
RelLib.Event.MARR_SETTL : "MARS",
RelLib.Event.MARRIAGE : "MARR"
}
personalConstantEvents = {
RelLib.Event.ADOPT : "ADOP",
RelLib.Event.ADULT_CHRISTEN : "CHRA",
RelLib.Event.BIRTH : "BIRT",
RelLib.Event.DEATH : "DEAT",
RelLib.Event.BAPTISM : "BAPM",
RelLib.Event.BAR_MITZVAH : "BARM",
RelLib.Event.BAS_MITZVAH : "BASM",
RelLib.Event.BLESS : "BLES",
RelLib.Event.BURIAL : "BURI",
RelLib.Event.CAUSE_DEATH : "CAUS",
RelLib.Event.ORDINATION : "ORDI",
RelLib.Event.CENSUS : "CENS",
RelLib.Event.CHRISTEN : "CHR" ,
RelLib.Event.CONFIRMATION : "CONF",
RelLib.Event.CREMATION : "CREM",
RelLib.Event.DEGREE : "_DEG",
RelLib.Event.DIV_FILING : "DIVF",
RelLib.Event.EDUCATION : "EDUC",
RelLib.Event.ELECTED : "",
RelLib.Event.EMIGRATION : "EMIG",
RelLib.Event.FIRST_COMMUN : "FCOM",
RelLib.Event.GRADUATION : "GRAD",
RelLib.Event.MED_INFO : "_MDCL",
RelLib.Event.MILITARY_SERV : "_MILT",
RelLib.Event.NATURALIZATION : "NATU",
RelLib.Event.NOB_TITLE : "TITL",
RelLib.Event.NUM_MARRIAGES : "NMR",
RelLib.Event.IMMIGRATION : "IMMI",
RelLib.Event.OCCUPATION : "OCCU",
RelLib.Event.PROBATE : "PROB",
RelLib.Event.PROPERTY : "PROP",
RelLib.Event.RELIGION : "RELI",
RelLib.Event.RESIDENCE : "RESI",
RelLib.Event.RETIREMENT : "RETI",
RelLib.Event.WILL : "WILL",
}
familyConstantAttributes = {
RelLib.Attribute.NUM_CHILD : "NCHI",
}
personalConstantAttributes = {
RelLib.Attribute.CASTE : "CAST",
RelLib.Attribute.DESCRIPTION : "DSCR",
RelLib.Attribute.ID : "IDNO",
RelLib.Attribute.NATIONAL : "NATI",
RelLib.Attribute.NUM_CHILD : "NCHI",
RelLib.Attribute.SSN : "SSN",
}
#-------------------------------------------------------------------------
#
# modified flag
#
#-------------------------------------------------------------------------
_history_brokenFlag = 0
def history_broken():
global _history_brokenFlag
_history_brokenFlag = 1
data_recover_msg = _('The data can only be recovered by Undo operation '
'or by quitting with abandoning changes.')
def fix_encoding(value):
import locale
if type(value) != unicode:
try:
return unicode(value)
except:
codeset = locale.getpreferredencoding()
if codeset == 'UTF-8':
codeset = 'latin1'
return unicode(value,codeset)
else:
return value
import locale
def xml_lang():
(loc,enc) = locale.getlocale()
if loc == None:
return ""
else:
return loc.replace('_','-')
#-------------------------------------------------------------------------
#
# force_unicode
#
#-------------------------------------------------------------------------
def force_unicode(n):
if type(n) != unicode:
return (unicode(n).lower(),unicode(n))
else:
return (n.lower(),n)
#-------------------------------------------------------------------------
#
# Clears the modified flag. Should be called after data is saved.
#
#-------------------------------------------------------------------------
def clearHistory_broken():
global _history_brokenFlag
_history_brokenFlag = 0
def wasHistory_broken():
return _history_brokenFlag
#-------------------------------------------------------------------------
#
# Short hand function to return either the person's name, or an empty
# string if the person is None
#
#-------------------------------------------------------------------------
def family_name(family,db):
"""Builds a name for the family from the parents names"""
father_handle = family.get_father_handle()
mother_handle = family.get_mother_handle()
father = db.get_person_from_handle(father_handle)
mother = db.get_person_from_handle(mother_handle)
if father and mother:
fname = NameDisplay.displayer.display(father)
mname = NameDisplay.displayer.display(mother)
name = _("%(father)s and %(mother)s") % {
"father" : fname,
"mother" : mname}
elif father:
name = NameDisplay.displayer.display(father)
elif mother:
name = NameDisplay.displayer.display(mother)
else:
name = _("unknown")
return name
def family_upper_name(family,db):
"""Builds a name for the family from the parents names"""
father_handle = family.get_father_handle()
mother_handle = family.get_mother_handle()
father = db.get_person_from_handle(father_handle)
mother = db.get_person_from_handle(mother_handle)
if father and mother:
fname = father.get_primary_name().get_upper_name()
mname = mother.get_primary_name().get_upper_name()
name = _("%s and %s") % (fname,mname)
elif father:
name = father.get_primary_name().get_upper_name()
else:
name = mother.get_primary_name().get_upper_name()
return name
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def destroy_passed_object(obj):
obj.destroy()
while gtk.events_pending():
gtk.main_iteration()
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def get_detail_text(obj,priv=1):
if obj.get_note() != "":
details = "%s" % _("Note")
else:
details = ""
if len(obj.get_source_references()) > 0:
if details == "":
details = _("Source")
else:
details = "%s, %s" % (details,_("Source"))
if priv and obj.get_privacy() == 1:
if details == "":
details = _("Private")
else:
details = "%s, %s" % (details,_("Private"))
return details
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def redraw_list(dlist,clist,func):
clist.clear()
index = 0
for obj in dlist:
col = 0
node = clist.append()
for data in func(obj):
clist.set_value(node,col,data)
col = col + 1
index = index + 1
return index
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def delete_selected(obj,list):
sel = obj.get_selection()
model,node = sel.get_selected()
if node:
index = model.get_path(node)[0]
del list[index]
return 1
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def add_menuitem(menu,msg,obj,func):
item = gtk.MenuItem(msg)
item.set_data('o',obj)
item.connect("activate",func)
item.show()
menu.append(item)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def view_photo(photo):
mime_type = photo.get_mime_type()
try:
data = Mime.get_application(mime_type)
prog = data[0]
except:
return
args = prog.split()
args.append(photo.get_path())
if os.fork() == 0:
os.execvp(args[0],args)
def find_file( filename):
# try the filename we got
try:
fname = filename
if os.path.isfile( filename):
return( filename)
except:
pass
# Build list of elternate encodings
encodings = [sys.getfilesystemencoding(), locale.getpreferredencoding(), 'UTF-8', 'ISO-8859-1']
encodings = list(sets.Set(encodings))
for enc in encodings:
try:
fname = filename.encode(enc)
if os.path.isfile( fname):
return fname
except:
pass
# not found
return ''
def find_folder( filename):
# try the filename we got
try:
fname = filename
if os.path.isdir( filename):
return( filename)
except:
pass
# Build list of elternate encodings
encodings = [sys.getfilesystemencoding(), locale.getpreferredencoding(), 'UTF-8', 'ISO-8859-1']
encodings = list(sets.Set(encodings))
for enc in encodings:
try:
fname = filename.encode(enc)
if os.path.isdir( fname):
return fname
except:
pass
# not found
return ''
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def build_string_optmenu(mapping, start_val):
index = 0
start_index = 0
keys = mapping.keys()
keys.sort()
myMenu = gtk.Menu()
for key in keys:
if key == "default":
menuitem = gtk.MenuItem(_("default"))
else:
menuitem = gtk.MenuItem(key)
menuitem.set_data("d", mapping[key])
menuitem.set_data("l", key)
menuitem.show()
myMenu.append(menuitem)
if key == start_val:
start_index = index
index = index + 1
if start_index:
myMenu.set_active(start_index)
return myMenu
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def build_columns(tree,list):
cnum = 0
for name in list:
renderer = gtk.CellRendererText()
renderer.set_fixed_height_from_font(1)
column = gtk.TreeViewColumn(name[0],renderer,text=cnum)
column.set_min_width(name[1])
if name[2] >= 0:
column.set_sort_column_id(name[2])
if name[0] == '':
column.set_clickable(True)
column.set_visible(False)
cnum = cnum + 1
tree.append_column(column)
#-------------------------------------------------------------------------
#
# Iterate over ancestors.
#
#-------------------------------------------------------------------------
def for_each_ancestor(db, start, func, data):
"""
Recursively iterate (breadth-first) over ancestors of
people listed in start.
Call func(data,pid) for the Id of each person encountered.
Exit and return 1, as soon as func returns true.
Return 0 otherwise.
"""
todo = start
doneIds = {}
while len(todo):
p_handle = todo.pop()
p = db.get_person_from_handle(p_handle)
# Don't process the same handle twice. This can happen
# if there is a cycle in the database, or if the
# initial list contains X and some of X's ancestors.
if doneIds.has_key(p_handle):
continue
doneIds[p_handle] = 1
if func(data,p_handle):
return 1
for fam_handle, mrel, frel in p.get_parent_family_handle_list():
fam = db.get_family_from_handle(fam_handle)
if fam:
f_handle = fam.get_father_handle()
m_handle = fam.get_mother_handle()
if f_handle: todo.append(f_handle)
if m_handle: todo.append(m_handle)
return 0
def title(n):
return '%s' % n
def set_title_label(xmlobj,t):
title_label = xmlobj.get_widget('title')
title_label.set_text('%s' % t)
title_label.set_use_markup(True)
def set_titles(window,title,t,msg=None):
if title:
title.set_text('%s' % t)
title.set_use_markup(True)
if msg:
window.set_title('%s - GRAMPS' % msg)
else:
window.set_title('%s - GRAMPS' % t)
window.set_icon_from_file(const.icon)
def gfloat(val):
"""Converts to floating number, taking care of possible locale differences.
Useful for reading float values from text entry fields
while under non-English locale.
"""
try:
return float(val)
except:
try:
return float(val.replace('.',','))
except:
return float(val.replace(',','.'))
return 0.0
def gformat(val):
"""Performs ('%.3f' % val) formatting with the resulting string always
using dot ('.') as a decimal point.
Useful for writing float values into XML when under non-English locale.
"""
decimal_point = locale.localeconv()['decimal_point']
return_val = "%.3f" % val
return return_val.replace(decimal_point,'.')
def search_for(name):
name = name.split()[0]
for i in os.environ['PATH'].split(':'):
fname = os.path.join(i,name)
if os.access(fname,os.X_OK) and not os.path.isdir(fname):
return 1
return 0
#-------------------------------------------------------------------------
#
# Change label appearance
#
#-------------------------------------------------------------------------
def bold_label(label,widget=None):
if label.__class__ == gtk.Label:
text = unicode(label.get_text())
text = text.replace('','')
text = text.replace('','')
label.set_text("%s" % text )
label.set_use_markup(True)
else:
clist = label.get_children()
text = unicode(clist[1].get_text())
text = text.replace('','')
text = text.replace('','')
clist[0].show()
clist[1].set_text("%s" % text )
clist[1].set_use_markup(True)
if widget:
widget.window.set_cursor(None)
def unbold_label(label,widget=None):
if label.__class__ == gtk.Label:
text = unicode(label.get_text())
text = text.replace('','')
text = text.replace('','')
text = text.replace('','')
text = text.replace('','')
label.set_text(text)
label.set_use_markup(False)
else:
clist = label.get_children()
text = unicode(clist[1].get_text())
text = text.replace('','')
text = text.replace('','')
text = text.replace('','')
text = text.replace('','')
clist[0].hide()
clist[1].set_text(text)
clist[1].set_use_markup(False)
if widget:
widget.window.set_cursor(None)
def temp_label(label,widget=None):
if label.__class__ == gtk.Label:
text = unicode(label.get_text())
text = text.replace('','')
text = text.replace('','')
label.set_text("%s" % text )
label.set_use_markup(True)
else:
clist = label.get_children()
text = unicode(clist[1].get_text())
text = text.replace('','')
text = text.replace('','')
clist[0].hide()
clist[1].set_text("%s" % text )
clist[1].set_use_markup(True)
if widget:
widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
#-------------------------------------------------------------------------
#
# create_id
#
#-------------------------------------------------------------------------
import random
import time
from sys import maxint
rand = random.Random(time.time())
def create_id():
return "%08x%08x" % ( int(time.time()*10000),
rand.randint(0,maxint))
def probably_alive(person,db,current_year=None,limit=0):
"""Returns true if the person may be alive.
This works by a process of emlimination. If we can't find a good
reason to believe that someone is dead then we assume they must
be alive.
"""
if not current_year:
time_struct = time.localtime(time.time())
current_year = time_struct[0]
death_year = None
# If the recorded death year is before current year then
# things are simple.
if person.death_ref:
death = db.get_event_from_handle(person.death_ref.ref)
if death.get_date_object().get_start_date() != RelLib.Date.EMPTY:
death_year = death.get_date_object().get_year()
if death_year - limit < current_year:
return False
# Look for Cause Of Death, Burial or Cremation events.
# These are fairly good indications that someone's not alive.
for ev_ref in person.event_ref_list:
ev = db.get_event_from_handle(ev_ref.ref)
if ev and ev.get_type()[0] in [RelLib.Event.CAUSE_DEATH, RelLib.Event.BURIAL,
RelLib.Event.CREMATION]:
if not death_year:
death_year = ev.get_date_object().get_year()
if ev.get_date_object().get_start_date() != RelLib.Date.EMPTY:
if ev.get_date_object().get_year() - limit < current_year:
return False
# For any other event of this person, check whether it happened
# too long ago. If so then the person is likely dead now.
elif ev and too_old(ev.get_date_object(),current_year):
return False
birth_year = None
# If they were born within 100 years before current year then
# assume they are alive (we already know they are not dead).
if person.birth_ref:
birth = db.get_event_from_handle(person.birth_ref.ref)
if birth.get_date_object().get_start_date() != RelLib.Date.EMPTY:
if not birth_year:
birth_year = birth.get_date_object().get_year()
# Check whether the birth event is too old because the
# code above did not look at birth, only at other events
if too_old(birth.get_date_object(),current_year):
return False
if not_too_old(birth.get_date_object(),current_year):
return True
if not birth_year and death_year:
if death_year > current_year + 110:
# person died more tha 110 after current year
return False
# Neither birth nor death events are available. Try looking
# for descendants that were born more than a lifespan ago.
min_generation = 13
def descendants_too_old (person, years):
for family_handle in person.get_family_handle_list():
family = db.get_family_from_handle(family_handle)
for child_handle in family.get_child_handle_list():
child = db.get_person_from_handle(child_handle)
if child.birth_ref:
child_birth = db.get_event_from_handle(child.birth_ref.ref)
dobj = child_birth.get_date_object()
if dobj.get_start_date() != RelLib.Date.EMPTY:
d = RelLib.Date(dobj)
val = d.get_start_date()
val = d.get_year() - years
d.set_year(val)
if not not_too_old (d,current_year):
return True
if child.death_ref:
child_death = db.get_event_from_handle(child.death_ref.ref)
dobj = child_death.get_date_object()
if dobj.get_start_date() != RelLib.Date.EMPTY:
if not not_too_old (dobj,current_year):
return True
if descendants_too_old (child, years + min_generation):
return True
return False
# If there are descendants that are too old for the person to have
# been alive in the current year then they must be dead.
try:
if descendants_too_old (person, min_generation):
return False
except RuntimeError:
raise Errors.DatabaseError(
_("Database error: %s is defined as his or her own ancestor") %
NameDisplay.displayer.display(person))
average_generation_gap = 20
def ancestors_too_old (person, year):
family_handle = person.get_main_parents_family_handle()
if family_handle:
family = db.get_family_from_handle(family_handle)
father_handle = family.get_father_handle()
if father_handle:
father = db.get_person_from_handle(father_handle)
if father.birth_ref:
father_birth = db.get_event_from_handle(father.birth_ref.ref)
dobj = father_birth.get_date_object()
if dobj.get_start_date() != RelLib.Date.EMPTY:
if not not_too_old (dobj,year - average_generation_gap):
#print father.get_primary_name().get_name(), " father of ", person.get_primary_name().get_name(), " is too old by birth. birth year ", dobj.get_year(), " test year ", year - average_generation_gap
return True
#else:
#print father.get_primary_name().get_name(), " father of ", person.get_primary_name().get_name(), " is NOT too old by birth. birth year ", dobj.get_year(), " test year ", year - average_generation_gap
if father.death_ref:
father_death = db.get_event_from_handle(father.death_ref.ref)
dobj = father_death.get_date_object()
if dobj.get_start_date() != RelLib.Date.EMPTY:
if dobj.get_year() < year - average_generation_gap:
#print father.get_primary_name().get_name(), " father of ", person.get_primary_name().get_name(), " is too old by death."
return True
if ancestors_too_old (father, year - average_generation_gap):
return True
mother_handle = family.get_mother_handle()
if mother_handle:
mother = db.get_person_from_handle(mother_handle)
if mother.birth_ref:
mother_birth = db.get_event_from_handle(mother.birth_ref.ref)
dobj = mother_birth.get_date_object()
if dobj.get_start_date() != RelLib.Date.EMPTY:
if not not_too_old (dobj,year - average_generation_gap):
#print mother.get_primary_name().get_name(), " mother of ", person.get_primary_name().get_name(), " is too old by birth. birth year ", dobj.get_year(), " test year ", year - average_generation_gap
return True
#else:
#print mother.get_primary_name().get_name(), " mother of ", person.get_primary_name().get_name(), " is NOT too old by birth. birth year ", dobj.get_year(), " test year ", year - average_generation_gap
if mother.death_ref:
mother_death = db.get_event_from_handle(mother.death_ref.ref)
dobj = mother_death.get_date_object()
if dobj.get_start_date() != RelLib.Date.EMPTY:
if dobj.get_year() < year - average_generation_gap:
#print mother.get_primary_name().get_name(), " mother of ", person.get_primary_name().get_name(), " is too old by death."
return True
if ancestors_too_old (mother, year - average_generation_gap):
return True
return False
# If there are ancestors that would be too old in the current year
# then assume our person must be dead too.
if ancestors_too_old (person, current_year):
#print person.get_primary_name().get_name(), " is dead because ancestors are too old."
return False
# If we can't find any reason to believe that they are dead we
# must assume they are alive.
#print person.get_primary_name().get_name(), " is probably alive."
return True
def not_too_old(date,current_year=None):
if not current_year:
time_struct = time.localtime(time.time())
current_year = time_struct[0]
year = date.get_year()
if year > current_year:
return False
return (year != 0 and current_year - year < 110)
def too_old(date,current_year=None):
if current_year:
the_current_year = current_year
else:
time_struct = time.localtime(time.time())
the_current_year = time_struct[0]
year = date.get_year()
if year > the_current_year:
return True
return (year != 0 and the_current_year - year > 150)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def get_source_referents(source_handle,db):
"""
Find objects that refer the source.
This function finds all primary objects that refer (directly or through
secondary child-objects) to a given source handle in a given database.
"""
# Persons
person_list = [ handle \
for handle in db.get_person_handles(sort_handles=False) \
if db.get_person_from_handle(handle).has_source_reference(source_handle)
]
# Families
family_list = [ handle for handle in db.get_family_handles() \
if db.get_family_from_handle(handle).has_source_reference(source_handle)
]
# Events
event_list = [ handle for handle in db.get_event_handles() \
if db.get_event_from_handle(handle).has_source_reference(source_handle)
]
# Places
place_list = [ handle for handle in db.get_place_handles() \
if db.get_place_from_handle(handle).has_source_reference(source_handle)
]
# Sources
source_list = [ handle for handle in db.get_source_handles() \
if db.get_source_from_handle(handle).has_source_reference(source_handle)
]
# Media Objects
media_list = [ handle for handle in db.get_media_object_handles() \
if db.get_object_from_handle(handle).has_source_reference(source_handle)
]
return (person_list,family_list,event_list,
place_list,source_list,media_list)
def get_media_referents(media_handle,db):
"""
Find objects that refer the media object.
This function finds all primary objects that refer
to a given media handle in a given database.
"""
# Persons
person_list = [ handle \
for handle in db.get_person_handles(sort_handles=False) \
if media_handle in \
[photo.get_reference_handle() for photo \
in db.get_person_from_handle(handle).get_media_list()]
]
# Families
family_list = [ handle for handle in db.get_family_handles() \
if media_handle in \
[photo.get_reference_handle() for photo \
in db.get_family_from_handle(handle).get_media_list()]
]
# Events
event_list = [ handle for handle in db.get_event_handles() \
if media_handle in \
[photo.get_reference_handle() for photo \
in db.get_event_from_handle(handle).get_media_list()]
]
# Places
place_list = [ handle for handle in db.get_place_handles() \
if media_handle in \
[photo.get_reference_handle() for photo \
in db.get_place_from_handle(handle).get_media_list()]
]
# Sources
source_list = [ handle for handle in db.get_source_handles() \
if media_handle in \
[photo.get_reference_handle() for photo \
in db.get_source_from_handle(handle).get_media_list()]
]
return (person_list,family_list,event_list,place_list,source_list)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
_NEW_NAME_PATTERN = '%sUntitled_%d.%s'
def get_new_filename(ext,folder='~/'):
ix = 1
while os.path.isfile(os.path.expanduser(_NEW_NAME_PATTERN % (folder,ix,ext) )):
ix = ix + 1
return os.path.expanduser(_NEW_NAME_PATTERN % (folder,ix,ext))
def get_type_converter(val):
"""
Returns function that converts strings into the type of val.
"""
val_type = type(val)
if val_type in (str,unicode):
return unicode
elif val_type == int:
return int
elif val_type == float:
return float
elif val_type in (list,tuple):
return list
def type_name(val):
"""
Returns the name the type of val.
Only numbers and strings are supported.
The rest becomes strings (unicode).
"""
val_type = type(val)
if val_type == int:
return 'int'
elif val_type == float:
return 'float'
elif val_type in (str,unicode):
return 'unicode'
return 'unicode'
def get_type_converter_by_name(val_str):
"""
Returns function that converts strings into the type given by val_str.
Only numbers and strings are supported.
The rest becomes strings (unicode).
"""
if val_str == 'int':
return int
elif val_str == 'float':
return float
elif val_str in ('str','unicode'):
return unicode
return unicode
def relative_path(original, base):
if not os.path.exists(original) or not os.path.isdir(base):
return original
base_list = (os.path.abspath(base)).split(os.sep)
target_list = (os.path.abspath(original)).split(os.sep)
# Starting from the filepath root, work out how much of the filepath is
# shared by base and target.
for i in range(min(len(base_list), len(target_list))):
if base_list[i] <> target_list[i]: break
else:
i+=1
rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:]
return os.path.join(*rel_list)
class ProgressMeter:
"""
Progress meter class for GRAMPS.
"""
def __init__(self,title,header=''):
"""
Specify the title and the current pass header.
"""
self.ptop = gtk.Dialog()
self.ptop.connect('delete_event',self.warn)
self.ptop.set_has_separator(False)
self.ptop.set_title(title)
self.ptop.set_border_width(12)
self.ptop.vbox.set_spacing(10)
lbl = gtk.Label('%s' % title)
lbl.set_use_markup(True)
self.lbl = gtk.Label(header)
self.lbl.set_use_markup(True)
self.ptop.vbox.add(lbl)
self.ptop.vbox.add(self.lbl)
self.ptop.vbox.set_border_width(24)
self.pbar = gtk.ProgressBar()
self.ptop.set_size_request(350,125)
self.ptop.vbox.add(self.pbar)
self.ptop.show_all()
if header == '':
self.lbl.hide()
def set_pass(self,header,total):
"""
Reset for another pass. Provide a new header and define number
of steps to be used.
"""
if header == '':
self.lbl.hide()
else:
self.lbl.show()
self.pbar_max = total
self.pbar_index = 0.0
self.lbl.set_text(header)
self.pbar.set_fraction(0.0)
while gtk.events_pending():
gtk.main_iteration()
def step(self):
"""Click the progress bar over to the next value. Be paranoid
and insure that it doesn't go over 100%."""
self.pbar_index = self.pbar_index + 1.0
if (self.pbar_index > self.pbar_max):
self.pbar_index = self.pbar_max
val = self.pbar_index/self.pbar_max
self.pbar.set_text("%d of %d (%.1f%%)" % (self.pbar_index,self.pbar_max,(val*100)))
self.pbar.set_fraction(val)
while gtk.events_pending():
gtk.main_iteration()
def warn(self,obj,obj2):
WarningDialog(
_("Attempt to force closing the dialog"),
_("Please do not force closing this important dialog."),
self.ptop)
return True
def close(self):
"""
Close the progress meter
"""
self.ptop.destroy()