diff --git a/src/BasicUtils/NameDisplay.py b/src/BasicUtils/NameDisplay.py
index ee65eb7e2..eed406706 100644
--- a/src/BasicUtils/NameDisplay.py
+++ b/src/BasicUtils/NameDisplay.py
@@ -1,7 +1,7 @@
#
# 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
# it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@ import re
#
#-------------------------------------------------------------------------
from RelLib import Name
+from Errors import NameDisplayError
try:
import Config
@@ -243,19 +244,19 @@ class NameDisplay:
def _gen_raw_func(self, format_str):
- """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
- going to some length to optimise it as much as possible.
+ """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
+ going to some length to optimise it as much as possible.
- This method constructs a new function that is specifically written
- to format a name given a particualar format string. This is worthwhile
- 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
- same format string again we can be as quick as possible.
+ This method constructs a new function that is specifically written
+ to format a name given a particualar format string. This is worthwhile
+ 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
+ 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],
raw_data[_FIRSTNAME],
raw_data[_PREFIX],
@@ -264,15 +265,15 @@ class NameDisplay:
"""
- # we need the names of each of the variables or methods that are
- # called to fill in each format flag.
+ # we need the names of each of the variables or methods that are
+ # called to fill in each format flag.
d = {"%t":"raw_data[_TITLE]",
"%f":"raw_data[_FIRSTNAME]",
"%p":"raw_data[_PREFIX]",
"%l":"raw_data[_SURNAME]",
"%s":"raw_data[_SUFFIX]",
"%y":"raw_data[_PATRONYM]",
- "%c":"raw_data[_CALL]",
+ "%c":"raw_data[_CALL]",
"%T":"raw_data[_TITLE].upper()",
"%F":"raw_data[_FIRSTNAME].upper()",
"%P":"raw_data[_PREFIX].upper()",
@@ -284,8 +285,8 @@ class NameDisplay:
new_fmt = format_str
- # replace the specific format string flags with a
- # flag that works in standard python format strings.
+ # replace the specific format string flags with a
+ # flag that works in standard python format strings.
new_fmt = new_fmt.replace("%t","%s")
new_fmt = new_fmt.replace("%f","%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("%%",'%')
- # find each format flag in the original format string
- # 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
- # generate the replacement tuple.
+ # find each format flag in the original format string
+ # 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
+ # generate the replacement tuple.
pat = re.compile("%.")
param = ()
@@ -317,37 +318,37 @@ class NameDisplay:
s = 'def fn(raw_data):\n'\
' return "%s" %% (%s)' % (new_fmt,",".join(param))
- exec(s)
+ exec(s)
return fn
def _gen_cooked_func(self, format_str):
- """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
- going to some length to optimise it as much as possible.
+ """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
+ going to some length to optimise it as much as possible.
- This method constructs a new function that is specifically written
- to format a name given a particualar format string. This is worthwhile
- 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
- same format string again we can be as quick as possible.
+ This method constructs a new function that is specifically written
+ to format a name given a particualar format string. This is worthwhile
+ 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
+ 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)
"""
- # we need the names of each of the variables or methods that are
- # called to fill in each format flag.
+ # we need the names of each of the variables or methods that are
+ # called to fill in each format flag.
d = {"%t":"title",
"%f":"first",
"%p":"prefix",
"%l":"surname",
"%s":"suffix",
"%y":"patronymic",
- "%c":"call",
+ "%c":"call",
"%T":"title.upper()",
"%F":"first.upper()",
"%P":"prefix.upper()",
@@ -360,8 +361,8 @@ class NameDisplay:
new_fmt = format_str
- # replace the specific format string flags with a
- # flag that works in standard python format strings.
+ # replace the specific format string flags with a
+ # flag that works in standard python format strings.
new_fmt = new_fmt.replace("%t","%s")
new_fmt = new_fmt.replace("%f","%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("%%",'%')
- # find each format flag in the original format string
- # 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
- # generate the replacement tuple.
- pat = re.compile("%.")
+ # find each format flag in the original format string
+ # 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
+ # generate the replacement tuple.
+ pat = re.compile('|'.join(d.keys()))
param = ()
mat = pat.search(format_str)
@@ -393,7 +394,7 @@ class NameDisplay:
s = 'def fn(first,surname,prefix,suffix,patronymic,title,call,):\n'\
' return "%s" %% (%s)' % (new_fmt,",".join(param))
- exec(s)
+ exec(s)
return fn
@@ -403,19 +404,19 @@ class NameDisplay:
name.call,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
- 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
- method call and we need all the speed we can squeeze out of this.
- """
+ """
+ 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.
+
+ 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.
+ """
func = self.__class__.raw_format_funcs.get(format_str)
if func == None:
func = self._gen_raw_func(format_str)
self.__class__.raw_format_funcs[format_str] = func
- s = func(raw_data)
+ s = func(raw_data)
return ' '.join(s.split())
@@ -443,10 +444,14 @@ class NameDisplay:
func = self._gen_cooked_func(format_str)
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())
- #-------------------------------------------------------------------------
+ #-------------------------------------------------------------------------
def sort_string(self,name):
return u"%-25s%-30s%s" % (name.surname,name.first_name,name.suffix)
diff --git a/src/Editors/_EditFamily.py b/src/Editors/_EditFamily.py
index 083e76651..56184851a 100644
--- a/src/Editors/_EditFamily.py
+++ b/src/Editors/_EditFamily.py
@@ -1,7 +1,7 @@
#
# 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
# 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,
family, dbstate.db.get_family_from_handle)
- self.in_save = False
-
# look for the scenerio of a child and no parents on a new
# family
@@ -433,7 +431,7 @@ class EditFamily(EditPrimary):
def check_for_family_change(self, handles):
# 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.reload_people()
@@ -802,7 +800,6 @@ class EditFamily(EditPrimary):
def __do_save(self):
self.ok_button.set_sensitive(False)
- self.in_save = True
if not self.added:
original = self.db.get_family_from_handle(self.obj.handle)
@@ -834,7 +831,21 @@ class EditFamily(EditPrimary):
self.ok_button.set_sensitive(True)
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():
trans = self.db.transaction_begin()
@@ -861,12 +872,6 @@ class EditFamily(EditPrimary):
self.db.add_family(self.obj,trans)
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():
trans = self.db.transaction_begin()
self.db.remove_family(self.obj.handle,trans)
@@ -901,7 +906,6 @@ class EditFamily(EditPrimary):
self.db.commit_family(self.obj,trans)
self.db.transaction_commit(trans,_("Edit Family"))
- self.in_save = False
self.close()
def _cleanup_on_exit(self):
diff --git a/src/Errors.py b/src/Errors.py
index 5b9d5a4b6..139548138 100644
--- a/src/Errors.py
+++ b/src/Errors.py
@@ -1,7 +1,7 @@
#
# 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
# it under the terms of the GNU General Public License as published by
@@ -151,3 +151,14 @@ class DbError(Exception):
def __str__(self):
"Return string representation"
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
diff --git a/src/GrampsCfg.py b/src/GrampsCfg.py
index f6664a560..3e8ceb2b4 100644
--- a/src/GrampsCfg.py
+++ b/src/GrampsCfg.py
@@ -26,6 +26,7 @@
#
#-------------------------------------------------------------------------
from gettext import gettext as _
+from xml.sax.saxutils import escape
#-------------------------------------------------------------------------
#
@@ -46,6 +47,7 @@ from RelLib import Name
import ManagedWindow
from GrampsWidgets import *
import QuestionDialog
+from Errors import NameDisplayError
#-------------------------------------------------------------------------
#
@@ -379,12 +381,12 @@ class GrampsPreferences(ManagedWindow.ManagedWindow):
Name format editor Edit button callback
"""
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)
(res,name,fmt) = dlg.run()
- if name != self.selected_fmt[COL_NAME] or \
- fmt != self.selected_fmt[COL_FMT]:
+ if res == gtk.RESPONSE_OK and (name != self.selected_fmt[COL_NAME] or
+ fmt != self.selected_fmt[COL_FMT]):
exmpl = _nd.format_str(self.examplename,fmt)
self.fmt_model.set(self.iter,COL_NAME,name,
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_str = fmt_str
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')
ManagedWindow.set_titles(self.dlg, None, _('Name Format Editor'))
@@ -601,7 +604,7 @@ class NameFormatEditDlg:
self.nameentry.set_text(self.fmt_name)
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)
def run(self):
@@ -614,7 +617,15 @@ class NameFormatEditDlg:
self.fmt_str = self.formatentry.get_text()
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
elif (self.fmt_name == '') ^ (self.fmt_str == ''):
QuestionDialog.ErrorDialog(
@@ -625,11 +636,15 @@ class NameFormatEditDlg:
self.dlg.destroy()
return (self.response, self.fmt_name, self.fmt_str)
- def cb_format_changed(self,obj):
+ def cb_format_changed(self, obj):
try:
- t = (_nd.format_str(self.name,obj.get_text()))
- except ValueError, msg:
- t = _("Invalid format string: %s") % msg
- self.examplelabel.set_text(
- '%s' % t)
+ t = (_nd.format_str(self.name, escape(obj.get_text())))
+ sample = '%s' % t
+ self.valid = True
+ except NameDisplayError:
+ t = _("Invalid or incomplete format definition")
+ sample = '%s' % t
+ self.valid = False
+
+ self.examplelabel.set_text(sample)
self.examplelabel.set_use_markup(True)