gramps/gramps2/src/Utils.py

666 lines
20 KiB
Python
Raw Normal View History

2002-10-20 19:55:16 +05:30
#
# Gramps - a GTK+/GNOME based genealogy program
#
2004-02-23 10:11:37 +05:30
# Copyright (C) 2000-2004 Donald N. Allingham
2002-10-20 19:55:16 +05:30
#
# 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$
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os
import locale
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# GNOME/GTK
#
#-------------------------------------------------------------------------
import gtk
2004-05-14 04:15:51 +05:30
import gnome
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
import const
import GrampsMime
2005-01-01 09:57:15 +05:30
import NameDisplay
import Date
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# internationalization
#
#-------------------------------------------------------------------------
2003-08-17 07:44:33 +05:30
from gettext import gettext as _
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# modified flag
#
#-------------------------------------------------------------------------
_history_brokenFlag = 0
2002-10-20 19:55:16 +05:30
def history_broken():
global _history_brokenFlag
_history_brokenFlag = 1
2003-04-04 11:18:25 +05:30
#-------------------------------------------------------------------------
#
# force_unicode
#
#-------------------------------------------------------------------------
_t = type(u'')
def force_unicode(n):
if type(n) != _t:
return (unicode(n).lower(),unicode(n))
else:
return (n.lower(),n)
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# 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
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
# Short hand function to return either the person's name, or an empty
# string if the person is None
#
#-------------------------------------------------------------------------
2004-02-23 10:11:37 +05:30
def family_name(family,db):
2002-10-20 19:55:16 +05:30
"""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)
2002-10-20 19:55:16 +05:30
if father and mother:
2005-01-01 09:57:15 +05:30
fname = NameDisplay.displayer.display(father)
mname = NameDisplay.displayer.display(mother)
2002-10-20 19:55:16 +05:30
name = _("%s and %s") % (fname,mname)
elif father:
2005-01-01 09:57:15 +05:30
name = NameDisplay.displayer.display(father)
2002-10-20 19:55:16 +05:30
else:
2005-01-01 09:57:15 +05:30
name = NameDisplay.displayer.display(mother)
2002-10-20 19:55:16 +05:30
return name
2004-02-23 10:11:37 +05:30
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()
2002-10-20 19:55:16 +05:30
else:
name = mother.get_primary_name().get_upper_name()
return name
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def destroy_passed_object(obj):
obj.destroy()
while gtk.events_pending():
gtk.main_iteration()
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def get_detail_text(obj,priv=1):
if obj.get_note() != "":
2002-10-20 19:55:16 +05:30
details = "%s" % _("Note")
else:
details = ""
if len(obj.get_source_references()) > 0:
2002-10-20 19:55:16 +05:30
if details == "":
details = _("Source")
else:
details = "%s, %s" % (details,_("Source"))
if priv and obj.get_privacy() == 1:
2002-10-20 19:55:16 +05:30
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:
2002-10-20 19:55:16 +05:30
col = 0
node = clist.append()
for data in func(obj):
clist.set_value(node,col,data)
2002-10-20 19:55:16 +05:30
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]
2002-10-20 19:55:16 +05:30
del list[index]
return 1
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def add_menuitem(menu,msg,obj,func):
item = gtk.MenuItem(msg)
2004-05-14 04:15:51 +05:30
item.set_data('o',obj)
2002-10-20 19:55:16 +05:30
item.connect("activate",func)
item.show()
menu.append(item)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def view_photo(photo):
mime_type = photo.get_mime_type()
try:
data = GrampsMime.get_application(mime_type)
prog = data[0]
except:
2002-11-03 02:49:58 +05:30
return
2002-10-20 19:55:16 +05:30
args = prog.split()
args.append(photo.get_path())
2002-10-20 19:55:16 +05:30
if os.fork() == 0:
os.execvp(args[0],args)
def nautilus_icon(icon,mime_type):
import GrampsKeys
theme = GrampsKeys.client.get_string("/desktop/gnome/file_views/icon_theme")
if icon :
newicon = "%s/%s/%s.png" % (const.nautdir,theme,icon)
if os.path.isfile(newicon):
return newicon
else:
newicon = "%s/document-icons/%s.png" % (const.pixdir,icon)
if os.path.isfile(newicon):
return newicon
return None
elif mime_type == "x-directory/":
if theme:
newicon = "%s/%s/i-directory.png" % (const.nautdir,theme)
else:
newicon = "%s/gnome-folder.png" % const.pixdir
if os.path.isfile(newicon):
return newicon
return None
else:
icontmp = mime_type.replace('/','-')
if theme:
newicon = "%s/%s/gnome-%s.png" % (const.nautdir,theme,icontmp)
if os.path.isfile(newicon):
return newicon
else:
newicon = "%s/document-icons/gnome-%s.png" % (const.nautdir,icontmp)
if os.path.isfile(newicon):
return newicon
return None
2002-10-20 19:55:16 +05:30
def find_icon(mtype):
n = nautilus_icon(None,mtype)
if n:
return n
else:
return const.icon
2002-10-20 19:55:16 +05:30
def get_mime_type(file):
try:
return gnome.vfs.get_mime_type(file)
except:
return "unknown"
2002-10-20 19:55:16 +05:30
def get_mime_description(mime_type):
try:
value = gnome.vfs.mime_get_description(mime_type)
if value:
return value
else:
return ''
except:
return ''
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
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)
2002-10-20 19:55:16 +05:30
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(gtk.TRUE)
column.set_visible(gtk.FALSE)
cnum = cnum + 1
tree.append_column(column)
#-------------------------------------------------------------------------
#
# Iterate over ancestors.
#
#-------------------------------------------------------------------------
def for_each_ancestor(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 = todo.pop()
pid = p.get_handle()
# Don't process the same Id 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(pid):
continue
doneIds[pid] = 1
if func(data,pid):
return 1
for fam, mrel, frel in p.get_parent_family_handle_list():
f = fam.get_father_handle()
m = fam.get_mother_handle()
if f: todo.append(f)
if m: todo.append(m)
return 0
2003-03-05 11:31:31 +05:30
def title(n):
return '<span weight="bold" size="larger">%s</span>' % n
def set_title_label(xmlobj,t):
title_label = xmlobj.get_widget('title')
title_label.set_text('<span weight="bold" size="larger">%s</span>' % t)
title_label.set_use_markup(gtk.TRUE)
2003-03-06 11:42:51 +05:30
def set_titles(window,title,t,msg=None):
title.set_text('<span weight="bold" size="larger">%s</span>' % t)
title.set_use_markup(gtk.TRUE)
if msg:
window.set_title('%s - GRAMPS' % msg)
else:
window.set_title('%s - GRAMPS' % t)
2003-05-10 11:47:07 +05:30
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:
2003-06-14 22:44:46 +05:30
return float(val.replace('.',','))
except:
2003-06-14 22:44:46 +05:30
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):
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
2003-05-10 11:47:07 +05:30
#-------------------------------------------------------------------------
#
# Roman numbers
#
#-------------------------------------------------------------------------
def roman(num):
""" Integer to Roman numeral converter for 0 < num < 4000 """
if type(num) != type(0):
return "?"
if not 0 < num < 4000:
return "?"
vals = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
nums = ( 'M','CM', 'D','CD', 'C','XC', 'L','XL', 'X','IX', 'V','IV', 'I')
retval = ""
for i in range(len(vals)):
amount = int(num / vals[i])
retval += nums[i] * amount
num -= vals[i] * amount
return retval
2004-12-22 07:26:37 +05:30
#-------------------------------------------------------------------------
#
# Convert points to cm and back
2004-12-22 07:26:37 +05:30
#
#-------------------------------------------------------------------------
def pt2cm(pt):
return (float(pt)/28.3465)
def cm2pt(cm):
return (float(cm)/2.54)*72
2004-12-22 07:26:37 +05:30
#-------------------------------------------------------------------------
#
# Change label apperance
#
#-------------------------------------------------------------------------
def bold_label(label):
* src/PlaceView.py: Make sure to add new place after edit * src/AddMedia.py: unicode conversion from gtk.Entry * src/AddSpouse.py: unicode conversion from gtk.Entry * src/AddrEdit.py: unicode conversion from gtk.Entry * src/AttrEdit.py: unicode conversion from gtk.Entry * src/AutoComp.py: unicode conversion from gtk.Entry * src/ChooseParents.py: unicode conversion from gtk.Entry * src/DateEdit.py: unicode conversion from gtk.Entry * src/EditPerson.py: unicode conversion from gtk.Entry * src/EditPlace.py: unicode conversion from gtk.Entry * src/EditSource.py: unicode conversion from gtk.Entry * src/EventEdit.py: unicode conversion from gtk.Entry * src/Find.py: unicode conversion from gtk.Entry * src/GrampsCfg.py: unicode conversion from gtk.Entry * src/ImageSelect.py: unicode conversion from gtk.Entry * src/LocEdit.py: unicode conversion from gtk.Entry * src/Marriage.py: unicode conversion from gtk.Entry * src/MergeData.py: unicode conversion from gtk.Entry * src/NameEdit.py: unicode conversion from gtk.Entry * src/PeopleView.py: unicode conversion from gtk.Entry * src/Report.py: unicode conversion from gtk.Entry * src/SelectChild.py: unicode conversion from gtk.Entry * src/Sources.py: unicode conversion from gtk.Entry * src/StartupDialog.py: unicode conversion from gtk.Entry * src/StyleEditor.py: unicode conversion from gtk.Entry * src/UrlEdit.py: unicode conversion from gtk.Entry * src/Utils.py: unicode conversion from gtk.Entry * src/VersionControl.py: unicode conversion from gtk.Entry * src/Witness.py: unicode conversion from gtk.Entry svn: r2534
2003-12-17 10:53:16 +05:30
text = unicode(label.get_text())
label.set_text("<b>%s</b>" % text )
label.set_use_markup(1)
def unbold_label(label):
* src/PlaceView.py: Make sure to add new place after edit * src/AddMedia.py: unicode conversion from gtk.Entry * src/AddSpouse.py: unicode conversion from gtk.Entry * src/AddrEdit.py: unicode conversion from gtk.Entry * src/AttrEdit.py: unicode conversion from gtk.Entry * src/AutoComp.py: unicode conversion from gtk.Entry * src/ChooseParents.py: unicode conversion from gtk.Entry * src/DateEdit.py: unicode conversion from gtk.Entry * src/EditPerson.py: unicode conversion from gtk.Entry * src/EditPlace.py: unicode conversion from gtk.Entry * src/EditSource.py: unicode conversion from gtk.Entry * src/EventEdit.py: unicode conversion from gtk.Entry * src/Find.py: unicode conversion from gtk.Entry * src/GrampsCfg.py: unicode conversion from gtk.Entry * src/ImageSelect.py: unicode conversion from gtk.Entry * src/LocEdit.py: unicode conversion from gtk.Entry * src/Marriage.py: unicode conversion from gtk.Entry * src/MergeData.py: unicode conversion from gtk.Entry * src/NameEdit.py: unicode conversion from gtk.Entry * src/PeopleView.py: unicode conversion from gtk.Entry * src/Report.py: unicode conversion from gtk.Entry * src/SelectChild.py: unicode conversion from gtk.Entry * src/Sources.py: unicode conversion from gtk.Entry * src/StartupDialog.py: unicode conversion from gtk.Entry * src/StyleEditor.py: unicode conversion from gtk.Entry * src/UrlEdit.py: unicode conversion from gtk.Entry * src/Utils.py: unicode conversion from gtk.Entry * src/VersionControl.py: unicode conversion from gtk.Entry * src/Witness.py: unicode conversion from gtk.Entry svn: r2534
2003-12-17 10:53:16 +05:30
text = unicode(label.get_text())
text = text.replace('<b>','')
text = text.replace('</b>','')
label.set_text(text)
label.set_use_markup(0)
#-------------------------------------------------------------------------
#
# create_id
#
#-------------------------------------------------------------------------
import random
import time
rand = random.Random(time.time())
def create_id():
s = ""
for val in [ int(time.time()*10000) & 0x7fffffff,
rand.randint(0,0x7fffffff),
rand.randint(0,0x7fffffff)]:
while val != 0:
rem = val % 36
if rem <= 9:
s += chr(48+rem)
else:
s += chr(rem+55)
val = int(val/36)
return s
def probably_alive(person,db):
"""Returns true if the person may be alive."""
if person.death_handle:
return False
# Look for Cause Of Death, Burial or Cremation events.
# These are fairly good indications that someone's not alive.
for ev_handle in person.event_list:
ev = db.get_event_from_handle(ev_handle)
if ev and ev.name in ["Cause Of Death", "Burial", "Cremation"]:
return False
if person.birth_handle:
birth = db.get_event_from_handle(person.birth_handle)
if birth.get_date_object().get_start_date() != Date.EMPTY:
return not_too_old(birth.get_date_object())
# Neither birth nor death events are available. Try looking
# for descendants that were born more than a lifespan ago.
min_generation = 13
max_generation = 60
max_age_difference = 60
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_handle:
child_birth = db.get_event_from_handle(child.birth_handle)
dobj = child_birth.get_date_object()
if dobj.get_start_date() != Date.EMPTY:
d = Date.Date(dobj)
val = d.get_start_date()
val = d.get_year() - years
d.set_year(val)
if not not_too_old (d):
return True
if child.death_handle:
child_death = db.get_event_from_handle(child.death_handle)
dobj = child_death.get_date_object()
if dobj.get_start_date() != Date.EMPTY:
if not not_too_old (dobj):
return True
if descendants_too_old (child, years + min_generation):
return True
if descendants_too_old (person, min_generation):
return False
return False
# What about their parents?
def parents_too_old (person, age_difference):
family_handle = person.get_main_parents_family_handle()
if family_handle:
family = db.get_family_from_handle(family_handle)
for parent_id in [family.get_father_handle(), family.get_mother_handle()]:
if not parent_id:
continue
parent = db.get_person_from_handle(parent_id)
if parent.birth_handle:
parent_birth = db.get_event_from_handle(parent.birth_handle)
if parent_birth.get_date():
d = SingleDate (parent_birth.get_date_object().get_start_date())
d.set_year (d.get_year() + max_generation + age_difference)
if not not_too_old (d):
return True
if parent.death_handle:
parent_death = db.get_event_from_handle(parent.death_handle)
if parent_death.get_date() != "":
d = SingleDate (parent_death.get_date_object().get_start_date())
d.set_year (d.get_year() + age_difference)
if not not_too_old (d):
return True
if parents_too_old (person, 0):
return False
# As a last resort, trying seeing if their spouse's age gives
# any clue.
for family_handle in person.get_family_handle_list():
family = db.get_family_from_handle(family_handle)
for spouse_id in [family.get_father_handle(), family.get_mother_handle()]:
if not spouse_id or spouse_id == person.handle:
continue
spouse = db.get_person_from_handle(spouse_id)
sp_birth_handle = spouse.get_birth_handle()
sp_death_handle = spouse.get_death_handle()
if sp_birth_handle:
spouse_birth = db.get_event_from_handle(sp_birth_handle)
if not spouse_birth.get_date().is_empty():
d = Date.Date(spouse_birth.get_date_object())
d.set_year(d.get_year() + max_age_difference)
if not not_too_old (d):
return False
if sp_death_handle:
spouse_death = db.get_event_from_handle(sp_death_handle)
if spouse_death.get_date() != "":
d = Date.Date(spouse_death.get_date_object())
d.set_year (d.get_year() - min_generation)
if not not_too_old (d):
return False
if parents_too_old (spouse, max_age_difference):
return False
return True
def not_too_old(date):
time_struct = time.localtime(time.time())
current_year = time_struct[0]
year = date.get_year()
return not( year != 0 and current_year - year > 110)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
_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))
2004-12-22 07:26:37 +05:30
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