svn: r8622

This commit is contained in:
Alex Roitman 2007-06-22 05:57:48 +00:00
parent c70a3c2852
commit cc26961713
4 changed files with 113 additions and 78 deletions

View File

@ -1,7 +1,7 @@
# #
# Gramps - a GTK+/GNOME based genealogy program # Gramps - a GTK+/GNOME based genealogy program
# #
# Copyright (C) 2004-2006 Donald N. Allingham # Copyright (C) 2004-2007 Donald N. Allingham
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -38,6 +38,7 @@ import re
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from RelLib import Name from RelLib import Name
from Errors import NameDisplayError
try: try:
import Config import Config
@ -243,19 +244,19 @@ class NameDisplay:
def _gen_raw_func(self, format_str): def _gen_raw_func(self, format_str):
"""The job of building the name from a format string is rather """The job of building the name from a format string is rather
expensive and it is called lots and lots of times. So it is worth expensive and it is called lots and lots of times. So it is worth
going to some length to optimise it as much as possible. going to some length to optimise it as much as possible.
This method constructs a new function that is specifically written This method constructs a new function that is specifically written
to format a name given a particualar format string. This is worthwhile to format a name given a particualar format string. This is worthwhile
because the format string itself rarely changes, so by caching the new because the format string itself rarely changes, so by caching the new
function and calling it directly when asked to format a name to the function and calling it directly when asked to format a name to the
same format string again we can be as quick as possible. same format string again we can be as quick as possible.
The new function is of the form: The new function is of the form:
def fn(raw_data): def fn(raw_data):
return "%s %s %s %s %s" % (raw_data[_TITLE], return "%s %s %s %s %s" % (raw_data[_TITLE],
raw_data[_FIRSTNAME], raw_data[_FIRSTNAME],
raw_data[_PREFIX], raw_data[_PREFIX],
@ -264,15 +265,15 @@ class NameDisplay:
""" """
# we need the names of each of the variables or methods that are # we need the names of each of the variables or methods that are
# called to fill in each format flag. # called to fill in each format flag.
d = {"%t":"raw_data[_TITLE]", d = {"%t":"raw_data[_TITLE]",
"%f":"raw_data[_FIRSTNAME]", "%f":"raw_data[_FIRSTNAME]",
"%p":"raw_data[_PREFIX]", "%p":"raw_data[_PREFIX]",
"%l":"raw_data[_SURNAME]", "%l":"raw_data[_SURNAME]",
"%s":"raw_data[_SUFFIX]", "%s":"raw_data[_SUFFIX]",
"%y":"raw_data[_PATRONYM]", "%y":"raw_data[_PATRONYM]",
"%c":"raw_data[_CALL]", "%c":"raw_data[_CALL]",
"%T":"raw_data[_TITLE].upper()", "%T":"raw_data[_TITLE].upper()",
"%F":"raw_data[_FIRSTNAME].upper()", "%F":"raw_data[_FIRSTNAME].upper()",
"%P":"raw_data[_PREFIX].upper()", "%P":"raw_data[_PREFIX].upper()",
@ -284,8 +285,8 @@ class NameDisplay:
new_fmt = format_str new_fmt = format_str
# replace the specific format string flags with a # replace the specific format string flags with a
# flag that works in standard python format strings. # flag that works in standard python format strings.
new_fmt = new_fmt.replace("%t","%s") new_fmt = new_fmt.replace("%t","%s")
new_fmt = new_fmt.replace("%f","%s") new_fmt = new_fmt.replace("%f","%s")
new_fmt = new_fmt.replace("%p","%s") new_fmt = new_fmt.replace("%p","%s")
@ -303,10 +304,10 @@ class NameDisplay:
new_fmt = new_fmt.replace("%C","%s") new_fmt = new_fmt.replace("%C","%s")
new_fmt = new_fmt.replace("%%",'%') new_fmt = new_fmt.replace("%%",'%')
# find each format flag in the original format string # find each format flag in the original format string
# for each one we find the variable name that is needed to # for each one we find the variable name that is needed to
# replace it and add this to a list. This list will be used # replace it and add this to a list. This list will be used
# generate the replacement tuple. # generate the replacement tuple.
pat = re.compile("%.") pat = re.compile("%.")
param = () param = ()
@ -317,37 +318,37 @@ class NameDisplay:
s = 'def fn(raw_data):\n'\ s = 'def fn(raw_data):\n'\
' return "%s" %% (%s)' % (new_fmt,",".join(param)) ' return "%s" %% (%s)' % (new_fmt,",".join(param))
exec(s) exec(s)
return fn return fn
def _gen_cooked_func(self, format_str): def _gen_cooked_func(self, format_str):
"""The job of building the name from a format string is rather """The job of building the name from a format string is rather
expensive and it is called lots and lots of times. So it is worth expensive and it is called lots and lots of times. So it is worth
going to some length to optimise it as much as possible. going to some length to optimise it as much as possible.
This method constructs a new function that is specifically written This method constructs a new function that is specifically written
to format a name given a particualar format string. This is worthwhile to format a name given a particualar format string. This is worthwhile
because the format string itself rarely changes, so by caching the new because the format string itself rarely changes, so by caching the new
function and calling it directly when asked to format a name to the function and calling it directly when asked to format a name to the
same format string again we can be as quick as possible. same format string again we can be as quick as possible.
The new function is of the form: The new function is of the form:
def fn(first,surname,prefix,suffix,patronymic,title,call,): def fn(first,surname,prefix,suffix,patronymic,title,call,):
return "%s %s %s %s %s" % (first,surname,prefix,suffix,patronymic) return "%s %s %s %s %s" % (first,surname,prefix,suffix,patronymic)
""" """
# we need the names of each of the variables or methods that are # we need the names of each of the variables or methods that are
# called to fill in each format flag. # called to fill in each format flag.
d = {"%t":"title", d = {"%t":"title",
"%f":"first", "%f":"first",
"%p":"prefix", "%p":"prefix",
"%l":"surname", "%l":"surname",
"%s":"suffix", "%s":"suffix",
"%y":"patronymic", "%y":"patronymic",
"%c":"call", "%c":"call",
"%T":"title.upper()", "%T":"title.upper()",
"%F":"first.upper()", "%F":"first.upper()",
"%P":"prefix.upper()", "%P":"prefix.upper()",
@ -360,8 +361,8 @@ class NameDisplay:
new_fmt = format_str new_fmt = format_str
# replace the specific format string flags with a # replace the specific format string flags with a
# flag that works in standard python format strings. # flag that works in standard python format strings.
new_fmt = new_fmt.replace("%t","%s") new_fmt = new_fmt.replace("%t","%s")
new_fmt = new_fmt.replace("%f","%s") new_fmt = new_fmt.replace("%f","%s")
new_fmt = new_fmt.replace("%p","%s") new_fmt = new_fmt.replace("%p","%s")
@ -379,11 +380,11 @@ class NameDisplay:
new_fmt = new_fmt.replace("%C","%s") new_fmt = new_fmt.replace("%C","%s")
new_fmt = new_fmt.replace("%%",'%') new_fmt = new_fmt.replace("%%",'%')
# find each format flag in the original format string # find each format flag in the original format string
# for each one we find the variable name that is needed to # for each one we find the variable name that is needed to
# replace it and add this to a list. This list will be used # replace it and add this to a list. This list will be used
# generate the replacement tuple. # generate the replacement tuple.
pat = re.compile("%.") pat = re.compile('|'.join(d.keys()))
param = () param = ()
mat = pat.search(format_str) mat = pat.search(format_str)
@ -393,7 +394,7 @@ class NameDisplay:
s = 'def fn(first,surname,prefix,suffix,patronymic,title,call,):\n'\ s = 'def fn(first,surname,prefix,suffix,patronymic,title,call,):\n'\
' return "%s" %% (%s)' % (new_fmt,",".join(param)) ' return "%s" %% (%s)' % (new_fmt,",".join(param))
exec(s) exec(s)
return fn return fn
@ -403,19 +404,19 @@ class NameDisplay:
name.call,format_str) name.call,format_str)
def format_str_raw(self,raw_data,format_str): def format_str_raw(self,raw_data,format_str):
""" """
Format a name from the raw name list. To make this as fast as possible Format a name from the raw name list. To make this as fast as possible
this uses _gen_raw_func to generate a new method for each new format_string. this uses _gen_raw_func to generate a new method for each new format_string.
Is does not call _format_str_base because it would introduce an extra Is does not call _format_str_base because it would introduce an extra
method call and we need all the speed we can squeeze out of this. method call and we need all the speed we can squeeze out of this.
""" """
func = self.__class__.raw_format_funcs.get(format_str) func = self.__class__.raw_format_funcs.get(format_str)
if func == None: if func == None:
func = self._gen_raw_func(format_str) func = self._gen_raw_func(format_str)
self.__class__.raw_format_funcs[format_str] = func self.__class__.raw_format_funcs[format_str] = func
s = func(raw_data) s = func(raw_data)
return ' '.join(s.split()) return ' '.join(s.split())
@ -443,10 +444,14 @@ class NameDisplay:
func = self._gen_cooked_func(format_str) func = self._gen_cooked_func(format_str)
self.__class__.format_funcs[format_str] = func self.__class__.format_funcs[format_str] = func
s = func(first,surname,prefix,suffix,patronymic,title,call) try:
s = func(first,surname,prefix,suffix,patronymic,title,call)
except (ValueError,TypeError,):
raise NameDisplayError, "Incomplete format string"
return ' '.join(s.split()) return ' '.join(s.split())
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
def sort_string(self,name): def sort_string(self,name):
return u"%-25s%-30s%s" % (name.surname,name.first_name,name.suffix) return u"%-25s%-30s%s" % (name.surname,name.first_name,name.suffix)

View File

@ -1,7 +1,7 @@
# #
# Gramps - a GTK+/GNOME based genealogy program # Gramps - a GTK+/GNOME based genealogy program
# #
# Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2000-2007 Donald N. Allingham
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -383,8 +383,6 @@ class EditFamily(EditPrimary):
EditPrimary.__init__(self, dbstate, uistate, track, EditPrimary.__init__(self, dbstate, uistate, track,
family, dbstate.db.get_family_from_handle) family, dbstate.db.get_family_from_handle)
self.in_save = False
# look for the scenerio of a child and no parents on a new # look for the scenerio of a child and no parents on a new
# family # family
@ -433,7 +431,7 @@ class EditFamily(EditPrimary):
def check_for_family_change(self, handles): def check_for_family_change(self, handles):
# check to see if the handle matches the current object # check to see if the handle matches the current object
if not self.in_save and self.obj.get_handle() in handles: if self.obj.get_handle() in handles:
self.obj = self.dbstate.db.get_family_from_handle(self.obj.get_handle()) self.obj = self.dbstate.db.get_family_from_handle(self.obj.get_handle())
self.reload_people() self.reload_people()
@ -802,7 +800,6 @@ class EditFamily(EditPrimary):
def __do_save(self): def __do_save(self):
self.ok_button.set_sensitive(False) self.ok_button.set_sensitive(False)
self.in_save = True
if not self.added: if not self.added:
original = self.db.get_family_from_handle(self.obj.handle) original = self.db.get_family_from_handle(self.obj.handle)
@ -834,7 +831,21 @@ class EditFamily(EditPrimary):
self.ok_button.set_sensitive(True) self.ok_button.set_sensitive(True)
return return
if not original and self.object_is_empty():
QuestionDialog.ErrorDialog(
_("Cannot save family"),
_("No data exists for this family. "
"Please enter data or cancel the edit."))
self.ok_button.set_sensitive(True)
return
# We disconnect the callbacks to all signals we connected earlier.
# This prevents the signals originating in any of the following
# commits from being caught by us again.
for key in self.signal_keys:
self.db.disconnect(key)
self.signal_keys = []
if not original and not self.object_is_empty(): if not original and not self.object_is_empty():
trans = self.db.transaction_begin() trans = self.db.transaction_begin()
@ -861,12 +872,6 @@ class EditFamily(EditPrimary):
self.db.add_family(self.obj,trans) self.db.add_family(self.obj,trans)
self.db.transaction_commit(trans,_("Add Family")) self.db.transaction_commit(trans,_("Add Family"))
elif not original and self.object_is_empty():
QuestionDialog.ErrorDialog(_("Cannot save family"),
_("No data exists for this family. Please "
"enter data or cancel the edit."))
self.ok_button.set_sensitive(True)
return
elif original and self.object_is_empty(): elif original and self.object_is_empty():
trans = self.db.transaction_begin() trans = self.db.transaction_begin()
self.db.remove_family(self.obj.handle,trans) self.db.remove_family(self.obj.handle,trans)
@ -901,7 +906,6 @@ class EditFamily(EditPrimary):
self.db.commit_family(self.obj,trans) self.db.commit_family(self.obj,trans)
self.db.transaction_commit(trans,_("Edit Family")) self.db.transaction_commit(trans,_("Edit Family"))
self.in_save = False
self.close() self.close()
def _cleanup_on_exit(self): def _cleanup_on_exit(self):

View File

@ -1,7 +1,7 @@
# #
# Gramps - a GTK+/GNOME based genealogy program # Gramps - a GTK+/GNOME based genealogy program
# #
# Copyright (C) 2003-2006 Donald N. Allingham # Copyright (C) 2003-2007 Donald N. Allingham
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -151,3 +151,14 @@ class DbError(Exception):
def __str__(self): def __str__(self):
"Return string representation" "Return string representation"
return self.value return self.value
class NameDisplayError(Exception):
"""
Error used to report that the name display format string is invalid.
"""
def __init__(self,value):
Exception.__init__(self)
self.value = value
def __str__(self):
return self.value

View File

@ -26,6 +26,7 @@
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gettext import gettext as _ from gettext import gettext as _
from xml.sax.saxutils import escape
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -46,6 +47,7 @@ from RelLib import Name
import ManagedWindow import ManagedWindow
from GrampsWidgets import * from GrampsWidgets import *
import QuestionDialog import QuestionDialog
from Errors import NameDisplayError
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -379,12 +381,12 @@ class GrampsPreferences(ManagedWindow.ManagedWindow):
Name format editor Edit button callback Name format editor Edit button callback
""" """
num,name,fmt = self.selected_fmt[COL_NUM:COL_EXPL] num,name,fmt = self.selected_fmt[COL_NUM:COL_EXPL]
dlg = NameFormatEditDlg(name,fmt,self.examplename) dlg = NameFormatEditDlg(name, fmt, self.examplename)
dlg.dlg.set_transient_for(self.window) dlg.dlg.set_transient_for(self.window)
(res,name,fmt) = dlg.run() (res,name,fmt) = dlg.run()
if name != self.selected_fmt[COL_NAME] or \ if res == gtk.RESPONSE_OK and (name != self.selected_fmt[COL_NAME] or
fmt != self.selected_fmt[COL_FMT]: fmt != self.selected_fmt[COL_FMT]):
exmpl = _nd.format_str(self.examplename,fmt) exmpl = _nd.format_str(self.examplename,fmt)
self.fmt_model.set(self.iter,COL_NAME,name, self.fmt_model.set(self.iter,COL_NAME,name,
COL_FMT,fmt, COL_FMT,fmt,
@ -586,12 +588,13 @@ class NameFormatEditDlg:
""" """
""" """
def __init__(self,fmt_name,fmt_str,name): def __init__(self, fmt_name, fmt_str, name):
self.fmt_name = fmt_name self.fmt_name = fmt_name
self.fmt_str = fmt_str self.fmt_str = fmt_str
self.name = name self.name = name
self.valid = True
self.top = gtk.glade.XML(const.gladeFile,'namefmt_edit','gramps') self.top = gtk.glade.XML(const.gladeFile, 'namefmt_edit','gramps')
self.dlg = self.top.get_widget('namefmt_edit') self.dlg = self.top.get_widget('namefmt_edit')
ManagedWindow.set_titles(self.dlg, None, _('Name Format Editor')) ManagedWindow.set_titles(self.dlg, None, _('Name Format Editor'))
@ -601,7 +604,7 @@ class NameFormatEditDlg:
self.nameentry.set_text(self.fmt_name) self.nameentry.set_text(self.fmt_name)
self.formatentry = self.top.get_widget('format_entry') self.formatentry = self.top.get_widget('format_entry')
self.formatentry.connect('changed',self.cb_format_changed) self.formatentry.connect('changed', self.cb_format_changed)
self.formatentry.set_text(self.fmt_str) self.formatentry.set_text(self.fmt_str)
def run(self): def run(self):
@ -614,7 +617,15 @@ class NameFormatEditDlg:
self.fmt_str = self.formatentry.get_text() self.fmt_str = self.formatentry.get_text()
if self.response == gtk.RESPONSE_OK: if self.response == gtk.RESPONSE_OK:
if self.fmt_name == '' and self.fmt_str == '': if not self.valid:
q = QuestionDialog.QuestionDialog2(
_('The format definition is invalid'),
_('What would you like to do?'),
_('_Continue anyway'), _('_Modify format'),
parent=self.dlg)
running = not q.run()
self.response = gtk.RESPONSE_CANCEL
elif self.fmt_name == '' and self.fmt_str == '':
self.response = gtk.RESPONSE_CANCEL self.response = gtk.RESPONSE_CANCEL
elif (self.fmt_name == '') ^ (self.fmt_str == ''): elif (self.fmt_name == '') ^ (self.fmt_str == ''):
QuestionDialog.ErrorDialog( QuestionDialog.ErrorDialog(
@ -625,11 +636,15 @@ class NameFormatEditDlg:
self.dlg.destroy() self.dlg.destroy()
return (self.response, self.fmt_name, self.fmt_str) return (self.response, self.fmt_name, self.fmt_str)
def cb_format_changed(self,obj): def cb_format_changed(self, obj):
try: try:
t = (_nd.format_str(self.name,obj.get_text())) t = (_nd.format_str(self.name, escape(obj.get_text())))
except ValueError, msg: sample = '<span weight="bold" style="italic">%s</span>' % t
t = _("Invalid format string: %s") % msg self.valid = True
self.examplelabel.set_text( except NameDisplayError:
'<span weight="bold" style="italic">%s</span>' % t) t = _("Invalid or incomplete format definition")
sample = '<span foreground="#FF0000">%s</span>' % t
self.valid = False
self.examplelabel.set_text(sample)
self.examplelabel.set_use_markup(True) self.examplelabel.set_use_markup(True)