#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2009 Gary Burton
# Copyright (C) 2011 Tim G L Lyons
#
# 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$
"""
Non GUI/GTK related utility functions
"""
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os
import sys
import locale
import random
import time
import uuid
import logging
LOG = logging.getLogger(".")
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from gen.display.name import displayer as name_displayer
import gen.lib
from gen.errors import DatabaseError
from gen.datehandler import displayer as date_displayer, codeset
from gen.config import config
from const import GRAMPS_UUID, IMAGE_DIR
from gen.constfunc import mac, win
from gen.ggettext import sgettext as _
from gen.utils.name import family_name
#-------------------------------------------------------------------------
#
# Integer to String mappings for constants
#
#-------------------------------------------------------------------------
gender = {
gen.lib.Person.MALE : _("male"),
gen.lib.Person.FEMALE : _("female"),
gen.lib.Person.UNKNOWN : _("gender|unknown"),
}
def format_gender( type):
return gender.get(type[0], _("Invalid"))
confidence = {
gen.lib.Citation.CONF_VERY_HIGH : _("Very High"),
gen.lib.Citation.CONF_HIGH : _("High"),
gen.lib.Citation.CONF_NORMAL : _("Normal"),
gen.lib.Citation.CONF_LOW : _("Low"),
gen.lib.Citation.CONF_VERY_LOW : _("Very Low"),
}
family_rel_descriptions = {
gen.lib.FamilyRelType.MARRIED : _("A legal or common-law relationship "
"between a husband and wife"),
gen.lib.FamilyRelType.UNMARRIED : _("No legal or common-law relationship "
"between man and woman"),
gen.lib.FamilyRelType.CIVIL_UNION : _("An established relationship between "
"members of the same sex"),
gen.lib.FamilyRelType.UNKNOWN : _("Unknown relationship between a man "
"and woman"),
gen.lib.FamilyRelType.CUSTOM : _("An unspecified relationship between "
"a man and woman"),
}
#-------------------------------------------------------------------------
#
# 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, errors='strict'):
# The errors argument specifies the response when the input string can't be
# converted according to the encoding's rules. Legal values for this
# argument are 'strict' (raise a UnicodeDecodeError exception), 'replace'
# (add U+FFFD, 'REPLACEMENT CHARACTER'), or 'ignore' (just leave the
# character out of the Unicode result).
if not isinstance(value, unicode):
try:
return unicode(value)
except:
try:
if mac():
codeset = locale.getlocale()[1]
else:
codeset = locale.getpreferredencoding()
except:
codeset = "UTF-8"
return unicode(value, codeset, errors)
else:
return value
def xml_lang():
loc = locale.getlocale()
if loc[0] is None:
return ""
else:
return loc[0].replace('_', '-')
#-------------------------------------------------------------------------
#
# force_unicode
#
#-------------------------------------------------------------------------
def force_unicode(n):
if not isinstance(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
#-------------------------------------------------------------------------
#
# String Encoding functions
#
#-------------------------------------------------------------------------
def encodingdefs():
"""
4 functions are defined to obtain a byte string that can be used as
sort key and is locale aware. Do not print the sortkey, it is a sortable
string, but is not a human readable correct string!
When gtk is defined, one can avoid some function calls as then the default
python encoding is not ascii but utf-8, so use the gtk functions in those
cases.
conv_utf8_tosrtkey: convert a utf8 encoded string to sortkey usable string
conv_unicode_tosrtkey: convert a unicode object to sortkey usable string
conv_utf8_tosrtkey_ongtk: convert a utf8 encoded string to sortkey usable
string when gtk is loaded or utf-8 is default python encoding
conv_unicode_tosrtkey_ongtk: convert a unicode object to sortkey usable
string when gtk is loaded or utf-8 is default python encoding
"""
pass
if win():
# python encoding is ascii, but C functions need to receive the
# windows codeset, so convert over to it
conv_utf8_tosrtkey = lambda x: locale.strxfrm(x.decode("utf-8").encode(
codeset))
conv_unicode_tosrtkey = lambda x: locale.strxfrm(x.encode(codeset))
#when gtk is imported the python defaultencoding is utf-8,
#so no need to specify it
conv_utf8_tosrtkey_ongtk = lambda x: locale.strxfrm(unicode(x).encode(
codeset))
conv_unicode_tosrtkey_ongtk = lambda x: locale.strxfrm(x.encode(codeset,'replace'))
else:
# on unix C functions need to receive utf-8. Default conversion would
# use ascii, so it is needed to be explicit about the resulting encoding
conv_utf8_tosrtkey = lambda x: locale.strxfrm(x)
conv_unicode_tosrtkey = lambda x: locale.strxfrm(x.encode("utf-8"))
# when gtk loaded, default encoding (sys.getdefaultencoding ) is utf-8,
# so default conversion happens with utf-8
conv_utf8_tosrtkey_ongtk = lambda x: locale.strxfrm(x)
conv_unicode_tosrtkey_ongtk = lambda x: locale.strxfrm(x)
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)
from warnings import warn
def set_titles(window, title, t, msg=None):
warn('The Utils.set_titles is deprecated. Use ManagedWindow methods')
#-------------------------------------------------------------------------
#
# create_id
#
#-------------------------------------------------------------------------
rand = random.Random(time.time())
def create_id():
return "%08x%08x" % ( int(time.time()*10000),
rand.randint(0, sys.maxint))
def create_uid(self, handle=None):
if handle:
uid = uuid.uuid5(GRAMPS_UUID, handle)
else:
uid = uuid.uuid4()
return uid.hex.upper()
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def profile(func, *args):
import hotshot.stats
prf = hotshot.Profile('mystats.profile')
print "Start"
prf.runcall(func, *args)
print "Finished"
prf.close()
print "Loading profile"
stats = hotshot.stats.load('mystats.profile')
print "done"
stats.strip_dirs()
stats.sort_stats('time', 'calls')
stats.print_stats(100)
stats.print_callers(100)
#-------------------------------------------------------------------------
#
# Config-based functions
#
#-------------------------------------------------------------------------
def get_researcher():
"""
Return a new database owner with the default values from the config file.
"""
name = config.get('researcher.researcher-name')
address = config.get('researcher.researcher-addr')
locality = config.get('researcher.researcher-locality')
city = config.get('researcher.researcher-city')
state = config.get('researcher.researcher-state')
country = config.get('researcher.researcher-country')
post_code = config.get('researcher.researcher-postal')
phone = config.get('researcher.researcher-phone')
email = config.get('researcher.researcher-email')
owner = gen.lib.Researcher()
owner.set_name(name)
owner.set_address(address)
owner.set_locality(locality)
owner.set_city(city)
owner.set_state(state)
owner.set_country(country)
owner.set_postal_code(post_code)
owner.set_phone(phone)
owner.set_email(email)
return owner
#-------------------------------------------------------------------------
#
# Function to return the name of the main participant of an event
#
#-------------------------------------------------------------------------
def get_participant_from_event(db, event_handle, all_=False):
"""
Obtain the first primary or family participant to an event we find in the
database. Note that an event can have more than one primary or
family participant, only one is returned, adding ellipses if there are
more. If the all_ parameter is true a comma-space separated string with
the names of all primary participants is returned and no ellipses is used.
"""
participant = ""
ellipses = False
result_list = list(db.find_backlink_handles(event_handle,
include_classes=['Person', 'Family']))
#obtain handles without duplicates
people = set([x[1] for x in result_list if x[0] == 'Person'])
families = set([x[1] for x in result_list if x[0] == 'Family'])
for personhandle in people:
person = db.get_person_from_handle(personhandle)
if not person:
continue
for event_ref in person.get_event_ref_list():
if event_handle == event_ref.ref and \
event_ref.get_role().is_primary():
if participant:
if all_:
participant += ', %s' % name_displayer.display(person)
else:
ellipses = True
else:
participant = name_displayer.display(person)
break
if ellipses:
break
if ellipses:
return _('%s, ...') % participant
for familyhandle in families:
family = db.get_family_from_handle(familyhandle)
for event_ref in family.get_event_ref_list():
if event_handle == event_ref.ref and \
event_ref.get_role().is_family():
if participant:
if all_:
participant += ', %s' % family_name(family, db)
else:
ellipses = True
else:
participant = family_name(family, db)
break
if ellipses:
break
if ellipses:
return _('%s, ...') % participant
else:
return participant
#-------------------------------------------------------------------------
#
# Function to return a label to display the active object in the status bar
# and to describe bookmarked objects.
#
#-------------------------------------------------------------------------
def navigation_label(db, nav_type, handle):
label = None
obj = None
if nav_type == 'Person':
obj = db.get_person_from_handle(handle)
if obj:
label = name_displayer.display(obj)
elif nav_type == 'Family':
obj = db.get_family_from_handle(handle)
if obj:
label = family_name(obj, db)
elif nav_type == 'Event':
obj = db.get_event_from_handle(handle)
if obj:
try:
who = get_participant_from_event(db, handle)
except:
# get_participants_from_event fails when called during a magic
# batch transaction because find_backlink_handles tries to
# access the reference_map_referenced_map which doesn't exist
# under those circumstances. Since setting the navigation_label
# is inessential, just accept this and go on.
who = ''
desc = obj.get_description()
label = obj.get_type()
if desc:
label = '%s - %s' % (label, desc)
if who:
label = '%s - %s' % (label, who)
elif nav_type == 'Place':
obj = db.get_place_from_handle(handle)
if obj:
label = obj.get_title()
elif nav_type == 'Source':
obj = db.get_source_from_handle(handle)
if obj:
label = obj.get_title()
elif nav_type == 'Citation':
obj = db.get_citation_from_handle(handle)
if obj:
label = obj.get_page()
src = db.get_source_from_handle(obj.get_reference_handle())
if src:
label = src.get_title() + " " + label
elif nav_type == 'Repository':
obj = db.get_repository_from_handle(handle)
if obj:
label = obj.get_name()
elif nav_type == 'Media' or nav_type == 'MediaObject':
obj = db.get_object_from_handle(handle)
if obj:
label = obj.get_description()
elif nav_type == 'Note':
obj = db.get_note_from_handle(handle)
if obj:
label = obj.get()
# When strings are cut, make sure they are unicode
#otherwise you may end of with cutting within an utf-8 sequence
label = unicode(label)
label = " ".join(label.split())
if len(label) > 40:
label = label[:40] + "..."
if label and obj:
label = '[%s] %s' % (obj.get_gramps_id(), label)
return (label, obj)