# # 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)