2007-02-11 Zsolt Foldvari <zfoldvar@users.sourceforge.net>

* src/MarkupText.py: added
	* src/DisplayTabs/_NoteTab.py: handling 'rich text' notes
	* src/RelLib/_NoteBase.py: handling 'rich text' notes
	* src/RelLib/_Note.py: handling 'rich text' notes
	* src/GrampsDb/_GrampsBSDDB.py: handling 'rich text' notes
	* src/GrampsDbUtils/_ReadXML.py: handling 'rich text' notes
	* src/GrampsDbUtils/_WriteXML.py: handling 'rich text' notes
	* src/Merge/_MergeSource.py: handling 'rich text' notes
	* src/Merge/_MergePlace.py: handling 'rich text' notes
	* src/Merge/_MergePerson.py: handling 'rich text' notes
	* src/ReportBase/_ReportUtils.py: handling 'rich text' notes
	* src/ViewManager.py: cleanup



svn: r8081
This commit is contained in:
Zsolt Foldvari 2007-02-10 23:40:48 +00:00
parent da92a9431b
commit e54cbc8c51
13 changed files with 789 additions and 414 deletions

View File

@ -1,3 +1,17 @@
2007-02-11 Zsolt Foldvari <zfoldvar@users.sourceforge.net>
* src/MarkupText.py: added
* src/DisplayTabs/_NoteTab.py: handling 'rich text' notes
* src/RelLib/_NoteBase.py: handling 'rich text' notes
* src/RelLib/_Note.py: handling 'rich text' notes
* src/GrampsDb/_GrampsBSDDB.py: handling 'rich text' notes
* src/GrampsDbUtils/_ReadXML.py: handling 'rich text' notes
* src/GrampsDbUtils/_WriteXML.py: handling 'rich text' notes
* src/Merge/_MergeSource.py: handling 'rich text' notes
* src/Merge/_MergePlace.py: handling 'rich text' notes
* src/Merge/_MergePerson.py: handling 'rich text' notes
* src/ReportBase/_ReportUtils.py: handling 'rich text' notes
* src/ViewManager.py: cleanup
2007-02-09 Don Allingham <don@gramps-project.org>
* src/plugins/Check.py: fix illegal family events
* src/GrampsDbUtils/_GedcomParse.py: improve parsing

View File

@ -26,7 +26,6 @@
#
#-------------------------------------------------------------------------
from gettext import gettext as _
import xml.sax.saxutils
#-------------------------------------------------------------------------
#
@ -43,14 +42,14 @@ import pango
#-------------------------------------------------------------------------
import Spell
from _GrampsTab import GrampsTab
from DisplayTabs import log
from MarkupText import EditorBuffer
#-------------------------------------------------------------------------
#
# NoteTab
#
#-------------------------------------------------------------------------
FORMATTED_NOTE = False
class NoteTab(GrampsTab):
def __init__(self, dbstate, uistate, track, note_obj, title=_('Note')):
@ -117,28 +116,27 @@ class NoteTab(GrampsTab):
vbox.pack_start(hbox, False)
self.pack_start(vbox, True)
if FORMATTED_NOTE:
self.buf = EditorBuffer()
self.text.set_buffer(self.buf)
for lab,stock,font in [('gtk-italic',True,'<i>italic</i>'),
('gtk-bold',True,'<b>bold</b>'),
('gtk-underline',True,'<u>underline</u>'),
#('Blue',True,'<span foreground="blue">blue</span>'),
#('Red',False,'<span foreground="red">smallcaps</span>'),
]:
button = gtk.ToggleButton(lab)
hbox.pack_start(button, False)
if stock:
button.set_use_stock(True)
self.buf.setup_widget_from_pango(button,font)
else:
self.buf = self.text.get_buffer()
self.buf = EditorBuffer()
self.text.set_buffer(self.buf)
tooltips = gtk.Tooltips()
for tip,stock,font in [('Italic',gtk.STOCK_ITALIC,'<i>italic</i>'),
('Bold',gtk.STOCK_BOLD,'<b>bold</b>'),
('Underline',gtk.STOCK_UNDERLINE,'<u>underline</u>'),
]:
button = gtk.ToggleButton()
image = gtk.Image()
image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
button.set_image(image)
tooltips.set_tip(button, tip)
button.set_relief(gtk.RELIEF_NONE)
self.buf.setup_widget_from_pango(button,font)
hbox.pack_start(button, False)
##self.buf = self.text.get_buffer()
if self.note_obj:
self.empty = False
if FORMATTED_NOTE:
self.buf.set_text(self.note_obj.get())
else:
self.buf.insert_at_cursor(self.note_obj.get())
self.buf.set_text(self.note_obj.get(markup=True))
log.debug("Text: %s" % self.buf.get_text())
##self.buf.insert_at_cursor(self.note_obj.get())
else:
self.empty = True
@ -171,344 +169,3 @@ class NoteTab(GrampsTab):
def cancel(self):
self.note_obj.unserialize(self.original)
class FormattedBuffer (gtk.TextBuffer):
desc_to_attr_table = {
'family':[pango.AttrFamily,""],
'style':[pango.AttrStyle,pango.STYLE_NORMAL],
'variant':[pango.AttrVariant,pango.VARIANT_NORMAL],
'weight':[pango.AttrWeight,pango.WEIGHT_NORMAL],
'stretch':[pango.AttrStretch,pango.STRETCH_NORMAL],
}
pango_translation_properties={
# pango ATTR TYPE : (pango attr property / tag property)
pango.ATTR_SIZE : 'size',
pango.ATTR_WEIGHT: 'weight',
pango.ATTR_UNDERLINE: 'underline',
pango.ATTR_STRETCH: 'stretch',
pango.ATTR_VARIANT: 'variant',
pango.ATTR_STYLE: 'style',
pango.ATTR_SCALE: 'scale',
pango.ATTR_STRIKETHROUGH: 'strikethrough',
pango.ATTR_RISE: 'rise',
}
attval_to_markup={
'underline':{pango.UNDERLINE_SINGLE:'single',
pango.UNDERLINE_DOUBLE:'double',
pango.UNDERLINE_LOW:'low',
pango.UNDERLINE_NONE:'none'},
'stretch':{pango.STRETCH_ULTRA_EXPANDED:'ultraexpanded',
pango.STRETCH_EXPANDED:'expanded',
pango.STRETCH_EXTRA_EXPANDED:'extraexpanded',
pango.STRETCH_EXTRA_CONDENSED:'extracondensed',
pango.STRETCH_ULTRA_CONDENSED:'ultracondensed',
pango.STRETCH_CONDENSED:'condensed',
pango.STRETCH_NORMAL:'normal',
},
'variant':{pango.VARIANT_NORMAL:'normal',
pango.VARIANT_SMALL_CAPS:'smallcaps',
},
'style':{pango.STYLE_NORMAL:'normal',
pango.STYLE_OBLIQUE:'oblique',
pango.STYLE_ITALIC:'italic',
},
'stikethrough':{1:'true',
True:'true',
0:'false',
False:'false'},
}
def __init__ (self):
self.tagdict = {}
self.tags = {}
#self.buf = buf
#self.set_text(txt)
gtk.TextBuffer.__init__(self)
def set_text (self, txt):
gtk.TextBuffer.set_text(self,"")
try:
self.parsed,self.txt,self.separator = pango.parse_markup(txt,u'\x00')
except:
log.debug('Escaping text, we seem to have a problem here!')
txt=xml.sax.saxutils.escape(txt)
self.parsed,self.txt,self.separator = pango.parse_markup(txt,u'\x00')
self.attrIter = self.parsed.get_iterator()
self.add_iter_to_buffer()
while self.attrIter.next():
self.add_iter_to_buffer()
def add_iter_to_buffer (self):
range=self.attrIter.range()
font,lang,attrs = self.attrIter.get_font()
tags = self.get_tags_from_attrs(font,lang,attrs)
text = self.txt[range[0]:range[1]]
if tags: self.insert_with_tags(self.get_end_iter(),text,*tags)
else: self.insert_with_tags(self.get_end_iter(),text)
def get_tags_from_attrs (self, font,lang,attrs):
tags = []
if font:
font,fontattrs = self.fontdesc_to_attrs(font)
fontdesc = font.to_string()
if fontattrs:
attrs.extend(fontattrs)
if fontdesc and fontdesc!='Normal':
if not self.tags.has_key(font.to_string()):
tag=self.create_tag()
tag.set_property('font-desc',font)
if not self.tagdict.has_key(tag): self.tagdict[tag]={}
self.tagdict[tag]['font_desc']=font.to_string()
self.tags[font.to_string()]=tag
tags.append(self.tags[font.to_string()])
if lang:
if not self.tags.has_key(lang):
tag = self.create_tag()
tag.set_property('language',lang)
self.tags[lang]=tag
tags.append(self.tags[lang])
if attrs:
for a in attrs:
if a.type == pango.ATTR_FOREGROUND:
gdkcolor = self.pango_color_to_gdk(a.color)
key = 'foreground%s'%self.color_to_hex(gdkcolor)
if not self.tags.has_key(key):
self.tags[key]=self.create_tag()
self.tags[key].set_property('foreground-gdk',gdkcolor)
self.tagdict[self.tags[key]]={}
self.tagdict[self.tags[key]]['foreground']="#%s"\
%self.color_to_hex(gdkcolor)
tags.append(self.tags[key])
if a.type == pango.ATTR_BACKGROUND:
gdkcolor = self.pango_color_to_gdk(a.color)
key = 'background%s'%self.color_to_hex(gdkcolor)
if not self.tags.has_key(key):
self.tags[key]=self.create_tag()
self.tags[key].set_property('background-gdk',gdkcolor)
self.tagdict[self.tags[key]]={}
self.tagdict[self.tags[key]]['background']="#%s"\
%self.color_to_hex(gdkcolor)
tags.append(self.tags[key])
if self.pango_translation_properties.has_key(a.type):
prop=self.pango_translation_properties[a.type]
##log.debug('setting property %s of %s (type: %s)'\
##%(prop,a,a.type))
val=getattr(a,'value')
#tag.set_property(prop,val)
mval = val
if self.attval_to_markup.has_key(prop):
#print 'converting ',prop,' in ',val
if self.attval_to_markup[prop].has_key(val):
mval = self.attval_to_markup[prop][val]
else:
log.debug("hmmm, didn't know what to do"
" with value %s"%val,0)
key="%s%s"%(prop,val)
if not self.tags.has_key(key):
self.tags[key]=self.create_tag()
self.tags[key].set_property(prop,val)
self.tagdict[self.tags[key]]={}
self.tagdict[self.tags[key]][prop]=mval
tags.append(self.tags[key])
else:
##log.debug("Don't know what to do with attr %s"%a,1)
pass
return tags
def get_tags (self):
tagdict = {}
for pos in range(self.get_char_count()):
iter=self.get_iter_at_offset(pos)
for tag in iter.get_tags():
if tagdict.has_key(tag):
if tagdict[tag][-1][1] == pos - 1:
tagdict[tag][-1] = (tagdict[tag][-1][0],pos)
else:
tagdict[tag].append((pos,pos))
else:
tagdict[tag]=[(pos,pos)]
return tagdict
def get_text (self, start=None, end=None, include_hidden_chars=True):
tagdict=self.get_tags()
if not start: start=self.get_start_iter()
if not end: end=self.get_end_iter()
txt = unicode(gtk.TextBuffer.get_text(self,start,end))
cuts = {}
for k,v in tagdict.items():
stag,etag = self.tag_to_markup(k)
for st,e in v:
#add start tags second
if cuts.has_key(st): cuts[st].append(stag)
else: cuts[st]=[stag]
#add end tags first
if cuts.has_key(e+1): cuts[e+1]=[etag]+cuts[e+1]
else: cuts[e+1]=[etag]
last_pos = 0
outbuff = ""
cut_indices = cuts.keys()
cut_indices.sort()
soffset = start.get_offset()
eoffset = end.get_offset()
cut_indices = filter(lambda i: eoffset >= i >= soffset, cut_indices)
for c in cut_indices:
if not last_pos==c:
outbuff += xml.sax.saxutils.escape(txt[last_pos:c])
last_pos = c
for tag in cuts[c]:
outbuff += tag
outbuff += xml.sax.saxutils.escape(txt[last_pos:])
return outbuff
def tag_to_markup (self, tag):
stag = "<span"
for k,v in self.tagdict[tag].items():
stag += ' %s="%s"'%(k,v)
stag += ">"
return stag,"</span>"
def fontdesc_to_attrs (self,font):
nicks = font.get_set_fields().value_nicks
attrs = []
for n in nicks:
if self.desc_to_attr_table.has_key(n):
Attr,norm = self.desc_to_attr_table[n]
# create an attribute with our current value
attrs.append(Attr(getattr(font,'get_%s'%n)()))
# unset our font's value
getattr(font,'set_%s'%n)(norm)
return font,attrs
def pango_color_to_gdk (self, pc):
return gtk.gdk.Color(pc.red,pc.green,pc.blue)
def color_to_hex (self, color):
hexstring = ""
for col in 'red','green','blue':
hexfrag = hex(getattr(color,col)/(16*16)).split("x")[1]
if len(hexfrag)<2: hexfrag = "0" + hexfrag
hexstring += hexfrag
return hexstring
def apply_font_and_attrs (self, font, attrs):
tags = self.get_tags_from_attrs(font,None,attrs)
for t in tags: self.apply_tag_to_selection(t)
def remove_font_and_attrs (self, font, attrs):
tags = self.get_tags_from_attrs(font,None,attrs)
for t in tags: self.remove_tag_from_selection(t)
def setup_default_tags (self):
self.italics = self.get_tags_from_attrs(None,None,
[pango.AttrStyle('italic')])[0]
self.bold = self.get_tags_from_attrs(None,None,
[pango.AttrWeight('bold')])[0]
self.underline = self.get_tags_from_attrs(None,None,
[pango.AttrUnderline('single')])[0]
def get_selection (self):
bounds = self.get_selection_bounds()
if not bounds:
iter=self.get_iter_at_mark(self.insert)
if iter.inside_word():
start_pos = iter.get_offset()
iter.forward_word_end()
word_end = iter.get_offset()
iter.backward_word_start()
word_start = iter.get_offset()
iter.set_offset(start_pos)
bounds = (self.get_iter_at_offset(word_start),
self.get_iter_at_offset(word_end+1))
else:
bounds = (iter,self.get_iter_at_offset(iter.get_offset()+1))
return bounds
def apply_tag_to_selection (self, tag):
selection = self.get_selection()
if selection:
self.apply_tag(tag,*selection)
def remove_tag_from_selection (self, tag):
selection = self.get_selection()
if selection:
self.remove_tag(tag,*selection)
def remove_all_tags (self):
selection = self.get_selection()
if selection:
for t in self.tags.values():
self.remove_tag(t,*selection)
class EditorBuffer(FormattedBuffer):
def __init__ (self, normal_button=None, toggle_widget_alist=[]):
"""An interactive interface to allow marking up a gtk.TextBuffer.
normal_button is a widget whose clicked signal will make us normal
toggle_widget_alist is a list that looks like this:
[(widget, (font,attr)),
(widget2, (font,attr))]
"""
FormattedBuffer.__init__(self)
if normal_button:
normal_button.connect('clicked',lambda *args: self.remove_all_tags())
self.tag_widgets = {}
self.internal_toggle = False
self.insert = self.get_insert()
# FIXME: try to register class and use on_<signal> methods
self.connect('mark-set',self._mark_set_cb)
self.connect('changed',self._changed_cb)
for w,tup in toggle_widget_alist:
self.setup_widget(w,*tup)
def setup_widget_from_pango (self, widg, markupstring):
"""Setup widget from a pango markup string"""
#font = pango.FontDescription(fontstring)
a,t,s = pango.parse_markup(markupstring,u'\x00')
ai=a.get_iterator()
font,lang,attrs=ai.get_font()
return self.setup_widget(widg,font,attrs)
def setup_widget (self, widg, font, attr):
tags=self.get_tags_from_attrs(font,None,attr)
self.tag_widgets[tuple(tags)]=widg
return widg.connect('toggled',self._toggle,tags)
def _toggle (self, widget, tags):
if self.internal_toggle: return
if widget.get_active():
for t in tags: self.apply_tag_to_selection(t)
else:
for t in tags: self.remove_tag_from_selection(t)
def _mark_set_cb (self, buffer, iter, mark, *params):
# Every time the cursor moves, update our widgets that reflect
# the state of the text.
if hasattr(self,'_in_mark_set') and self._in_mark_set: return
self._in_mark_set = True
if mark.get_name()=='insert':
for tags,widg in self.tag_widgets.items():
active = True
for t in tags:
if not iter.has_tag(t):
active=False
self.internal_toggle=True
widg.set_active(active)
self.internal_toggle=False
if hasattr(self,'last_mark'):
self.move_mark(self.last_mark,iter)
else:
self.last_mark = self.create_mark('last',iter,left_gravity=True)
self._in_mark_set = False
def _changed_cb (self, tb):
if not hasattr(self,'last_mark'): return
# If our insertion point has a mark, we want to apply the tag
# each time the user types...
old_itr = self.get_iter_at_mark(self.last_mark)
insert_itr = self.get_iter_at_mark(self.insert)
if old_itr!=insert_itr:
# Use the state of our widgets to determine what
# properties to apply...
for tags,w in self.tag_widgets.items():
if w.get_active():
#print 'apply tags...',tags
for t in tags: self.apply_tag(t,old_itr,insert_itr)

View File

@ -1924,7 +1924,7 @@ class GrampsBSDDB(GrampsDbBase,UpdateCallback):
for witness in witness_list:
if witness.type == 0: # witness name recorded
# Add name and comment to the event note
note_text = event.get_note() + "\n" + \
note_text = event.get_note(markup=True) + "\n" + \
_("Witness name: %s") % witness.val
if witness.comment:
note_text += "\n" + _("Witness comment: %s") \
@ -1947,7 +1947,7 @@ class GrampsBSDDB(GrampsDbBase,UpdateCallback):
else:
# Broken witness: dangling witness handle
# with no corresponding person in the db
note_text = event.get_note() + "\n" + \
note_text = event.get_note(markup=True) + "\n" + \
_("Broken witness reference detected "
"while upgrading database to version 9.")
event.set_note(note_text)

View File

@ -715,7 +715,7 @@ class GrampsParser(UpdateCallback):
self.in_witness = True
self.witness_comment = ""
if attrs.has_key('name'):
note_text = self.event.get_note() + "\n" + \
note_text = self.event.get_note(markup=True) + "\n" + \
_("Witness name: %s") % attrs['name']
self.event.set_note(note_text)
return
@ -1434,11 +1434,11 @@ class GrampsParser(UpdateCallback):
def stop_witness(self,tag):
# Parse witnesses created by older gramps
if self.witness_comment:
note_text = self.event.get_note() + "\n" + \
note_text = self.event.get_note(markup=True) + "\n" + \
_("Witness comment: %s") % self.witness_comment
self.event.set_note(note_text)
elif tag.strip():
note_text = self.event.get_note() + "\n" + \
note_text = self.event.get_note(markup=True) + "\n" + \
_("Witness comment: %s") % tag
self.event.set_note(note_text)
self.in_witness = False
@ -1540,7 +1540,7 @@ class GrampsParser(UpdateCallback):
def stop_name(self,tag):
if self.in_witness:
# Parse witnesses created by older gramps
note_text = self.event.get_note() + "\n" + \
note_text = self.event.get_note(markup=True) + "\n" + \
_("Witness name: %s") % tag
self.event.set_note(note_text)
elif self.alt_name:

View File

@ -382,7 +382,7 @@ class XmlWriter(UpdateCallback):
def write_note(self,val,noteobj,indent=0):
if not noteobj:
return
text = noteobj.get()
text = noteobj.get(markup=True)
if not text:
return
if indent != 0:
@ -638,7 +638,7 @@ class XmlWriter(UpdateCallback):
source_ref.get_reference_handle())
if source:
p = source_ref.get_page()
c = source_ref.get_note()
c = source_ref.get_note(markup=True)
t = source_ref.get_text()
d = source_ref.get_date_object()
q = source_ref.get_confidence_level()

663
src/MarkupText.py Normal file
View File

@ -0,0 +1,663 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 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
# 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$
#-------------------------------------------------------------------------
#
# Python classes
#
#-------------------------------------------------------------------------
from xml.sax import saxutils, xmlreader
from cStringIO import StringIO
#-------------------------------------------------------------------------
#
# set up logging
#
#-------------------------------------------------------------------------
import logging
log = logging.getLogger(".MarkupText")
#-------------------------------------------------------------------------
#
# GTK libraries
#
#-------------------------------------------------------------------------
import gtk
import pango
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
(EVENT_START,
EVENT_END) = range(2)
class NoteXMLWriter(saxutils.XMLGenerator):
"""Generate XML markup text for Notes.
Provides additional feature of accounting opened tags and closing them
properly in case of partialy overlapping markups.
It is assumed that 'start name' and 'end name' are equal (e.g. <b>, </b>).
"""
def __init__(self, output, encoding):
saxutils.XMLGenerator.__init__(self, output, encoding)
self.attrs = xmlreader.AttributesImpl({})
#saxutils.XMLGenerator.startElement(self, u'gramps', self.attrs)
self.open_elements = []
def startElement(self, name, attrs=None):
if not attrs:
attrs = self.attrs
saxutils.XMLGenerator.startElement(self, name, attrs)
self.open_elements.append(name)
def endElement(self, name):
if not len(self.open_elements):
log.debug("Trying to close element '%s' when non is open" % name)
return
tmp_list = []
elem = ''
# close all open elements until we reach to the requested one
while elem != name:
try:
elem = self.open_elements.pop()
saxutils.XMLGenerator.endElement(self, elem)
if elem != name:
tmp_list.append(elem)
except:
# we need to do something smart here...
log.debug("Trying to close non open element '%s'" % name)
break
# open all other elements again
while True:
try:
elem = tmp_list.pop()
self.startElement(elem)
except:
break
def close(self):
#saxutils.XMLGenerator.endElement(self, u'gramps')
self.endDocument()
class MarkupBuffer(gtk.TextBuffer):
fontdesc_to_attr_table = {
'family': [pango.AttrFamily, ""],
'style': [pango.AttrStyle, pango.STYLE_NORMAL],
'variant': [pango.AttrVariant, pango.VARIANT_NORMAL],
'weight': [pango.AttrWeight, pango.WEIGHT_NORMAL],
'stretch': [pango.AttrStretch, pango.STRETCH_NORMAL],
}
pango_translation_properties = {
# pango ATTR TYPE : (pango attr property / tag property)
pango.ATTR_SIZE : 'size',
pango.ATTR_WEIGHT: 'weight',
pango.ATTR_UNDERLINE: 'underline',
pango.ATTR_STRETCH: 'stretch',
pango.ATTR_VARIANT: 'variant',
pango.ATTR_STYLE: 'style',
pango.ATTR_SCALE: 'scale',
pango.ATTR_STRIKETHROUGH: 'strikethrough',
pango.ATTR_RISE: 'rise',
}
attval_to_markup = {
'underline': {pango.UNDERLINE_SINGLE: 'single',
pango.UNDERLINE_DOUBLE: 'double',
pango.UNDERLINE_LOW: 'low',
pango.UNDERLINE_NONE: 'none'
},
'stretch': {pango.STRETCH_ULTRA_EXPANDED: 'ultraexpanded',
pango.STRETCH_EXPANDED: 'expanded',
pango.STRETCH_EXTRA_EXPANDED: 'extraexpanded',
pango.STRETCH_EXTRA_CONDENSED: 'extracondensed',
pango.STRETCH_ULTRA_CONDENSED: 'ultracondensed',
pango.STRETCH_CONDENSED: 'condensed',
pango.STRETCH_NORMAL: 'normal',
},
'variant': {pango.VARIANT_NORMAL: 'normal',
pango.VARIANT_SMALL_CAPS: 'smallcaps',
},
'style': {pango.STYLE_NORMAL: 'normal',
pango.STYLE_OBLIQUE: 'oblique',
pango.STYLE_ITALIC: 'italic',
},
'stikethrough': {1: 'true',
True: 'true',
0: 'false',
False: 'false'
},
}
# This is an ugly workaround until we get rid of pango
# only these markups are curently supported
pango_shortcut = {
'style2': 'i',
'weight700': 'b',
'underline1': 'u',
}
def __init__(self):
self.tagdict = {}
self.tags = {}
self.tag_markup = {}
gtk.TextBuffer.__init__(self)
def set_text(self, pango_text):
try:
attrlist, text, accel = pango.parse_markup(pango_text, u'\x00')
except:
log.debug('Escaping text, we seem to have a problem here!')
escaped_text = saxutils.escape(pango_text)
attrlist, text, accel = pango.parse_markup(escaped_text, u'\x00')
gtk.TextBuffer.set_text(self, text)
attriter = attrlist.get_iterator()
self.add_iter_to_buffer(attriter)
while attriter.next():
self.add_iter_to_buffer(attriter)
def add_iter_to_buffer(self, attriter):
"""Insert attributes into the buffer.
Convert the pango.Attribute at the received pango.AttrIterator
to gtk.TextTag and apply them on the buffer at the proper indices
"""
range = attriter.range()
start_iter = self.get_iter_at_offset(range[0])
end_iter = self.get_iter_at_offset(range[1])
font, lang, attrs = attriter.get_font()
tags = self.get_tags_from_attrs(font, lang, attrs)
for tag in tags:
self.apply_tag(tag, start_iter, end_iter)
def get_tags_from_attrs(self, font, lang, attrs):
"""Convert pango.Attribute to gtk.TextTag."""
tags = []
if font:
font, fontattrs = self.fontdesc_to_attrs(font)
fontdesc = font.to_string()
if fontattrs:
attrs.extend(fontattrs)
##if fontdesc and fontdesc != 'Normal':
##if not self.tags.has_key(font.to_string()):
##tag = self.create_tag()
##tag.set_property('font-desc', font)
##if not self.tagdict.has_key(tag):
##self.tagdict[tag] = {}
##self.tagdict[tag]['font_desc'] = font.to_string()
##self.tags[font.to_string()] = tag
##tags.append(self.tags[font.to_string()])
##if lang:
##if not self.tags.has_key(lang):
##tag = self.create_tag()
##tag.set_property('language', lang)
##self.tags[lang] = tag
##tags.append(self.tags[lang])
if attrs:
for a in attrs:
##if a.type == pango.ATTR_FOREGROUND:
##gdkcolor = self.pango_color_to_gdk(a.color)
##key = 'foreground%s' % self.color_to_hex(gdkcolor)
##if not self.tags.has_key(key):
##self.tags[key] = self.create_tag()
##self.tags[key].set_property('foreground-gdk', gdkcolor)
##self.tagdict[self.tags[key]] = {}
##self.tagdict[self.tags[key]]['foreground'] = "#%s"\
##% self.color_to_hex(gdkcolor)
##tags.append(self.tags[key])
##continue
##if a.type == pango.ATTR_BACKGROUND:
##gdkcolor = self.pango_color_to_gdk(a.color)
##key = 'background%s' % self.color_to_hex(gdkcolor)
##if not self.tags.has_key(key):
##self.tags[key] = self.create_tag()
##self.tags[key].set_property('background-gdk', gdkcolor)
##self.tagdict[self.tags[key]] = {}
##self.tagdict[self.tags[key]]['background'] = "#%s"\
##% self.color_to_hex(gdkcolor)
##tags.append(self.tags[key])
##continue
if self.pango_translation_properties.has_key(a.type):
prop = self.pango_translation_properties[a.type]
log.debug('setting property %s of %s '
'(type: %s)' % (prop, a, a.type))
val = getattr(a, 'value')
mval = val
if self.attval_to_markup.has_key(prop):
log.debug("converting %s in %s" % (prop,val))
if self.attval_to_markup[prop].has_key(val):
mval = self.attval_to_markup[prop][val]
else:
log.debug("hmmm, didn't know what to do"
" with value %s" % val)
key = "%s%s" % (prop, val)
if not self.tags.has_key(key):
self.tags[key] = self.create_tag()
self.tags[key].set_property(prop,val)
self.tagdict[self.tags[key]] = {}
self.tagdict[self.tags[key]][prop] = mval
self.tag_markup[self.tags[key]] = self.pango_shortcut[key]
tags.append(self.tags[key])
else:
log.debug("Don't know what to do with attr %s" % a)
return tags
def get_text(self, start=None, end=None, include_hidden_chars=True):
"""Returns the buffer text with Pango markup tags."""
tagdict = self.get_tags()
eventlist = self.get_event_list(tagdict)
if not start:
start = self.get_start_iter()
if not end:
end = self.get_end_iter()
txt = unicode(gtk.TextBuffer.get_text(self, start, end))
output = StringIO()
note_xml = NoteXMLWriter(output, 'utf-8')
last_pos = 0
indices = eventlist.keys()
indices.sort()
for index in indices:
note_xml.characters(txt[last_pos:index])
for tag, event_type, p in eventlist[index]:
if event_type == EVENT_START:
note_xml.startElement(self.tag_markup[tag][EVENT_START])
elif event_type == EVENT_END:
note_xml.endElement(self.tag_markup[tag][EVENT_START])
last_pos = index
note_xml.characters(txt[last_pos:])
note_xml.close()
##cuts = {}
##for text_tag, range in tagdict.items():
##stag, etag = self.tag_to_markup(text_tag)
##for st, e in range:
### insert start tag
##if cuts.has_key(st):
##cuts[st].append(stag)
##else:
##cuts[st] = [stag]
### insert end tag
##if cuts.has_key(e + 1):
##cuts[e + 1] = [etag] + cuts[e + 1]
##else:
##cuts[e + 1] = [etag]
##last_pos = 0
##outbuff = ""
##cut_indices = cuts.keys()
##cut_indices.sort()
##soffset = start.get_offset()
##eoffset = end.get_offset()
##cut_indices = filter(lambda i: eoffset >= i >= soffset, cut_indices)
##for c in cut_indices:
##if not last_pos == c:
##outbuff += saxutils.escape(txt[last_pos:c])
##last_pos = c
##for tag in cuts[c]:
##outbuff += tag
##outbuff += saxutils.escape(txt[last_pos:])
##return outbuff
return output.getvalue()
def get_tags(self):
"""Extract TextTags from buffer.
@return: tagdict
@rtype: {TextTag: [(start, end),]}
"""
tagdict = {}
for pos in range(self.get_char_count()):
iter = self.get_iter_at_offset(pos)
for tag in iter.get_tags():
if tagdict.has_key(tag):
if tagdict[tag][-1][1] == pos - 1:
tagdict[tag][-1] = (tagdict[tag][-1][0], pos)
else:
tagdict[tag].append((pos, pos))
else:
tagdict[tag]=[(pos, pos)]
return tagdict
def get_event_list(self, tagdict):
"""Create an event list for XML writer.
@param tagdict: tag dict to convert from
@param type: {TextTag: [(start, end),]}
@return: eventlist
@rtype: {index: [(TextTag, EVENT_TYPE, pair_index),]}
"""
eventlist = {}
for text_tag, indices in tagdict.items():
for start_idx, end_idx in indices:
# end element goes after the last markup'ed char
end_idx += 1
# insert START events
if eventlist.has_key(start_idx):
eventlist[start_idx].append((text_tag, EVENT_START, end_idx))
else:
eventlist[start_idx] = [(text_tag, EVENT_START, end_idx)]
# insert END events
if eventlist.has_key(end_idx):
eventlist[end_idx].append((text_tag, EVENT_END, start_idx))
else:
eventlist[end_idx] = [(text_tag, EVENT_END, start_idx)]
# sort events at the same index
indices = eventlist.keys()
#indices.sort()
for idx in indices:
if len(eventlist[idx]) > 1:
eventlist[idx].sort(self.sort_events)
return eventlist
def sort_events(self, event_a, event_b):
"""Sort events that are at the same index.
Sorting with the following rules:
1. END event goes always before START event;
2. from two START events the one goes first, which has it's own END
event later;
3. from two END events the one goes first, which has it's own START
event later.
"""
tag_a, type_a, pair_a = event_a
tag_b, type_b, pair_b = event_b
if (type_a + type_b) == (EVENT_START + EVENT_END):
return type_b - type_a
else:
return pair_b - pair_a
##def tag_to_markup(self, tag):
##"""Convert a gtk.TextTag to Pango markup tags."""
##stag = "<span"
##for k, v in self.tagdict[tag].items():
##stag += ' %s="%s"' % (k, v)
##stag += ">"
##return stag, "</span>"
##stag = "<%s>" % self.tag_markup[tag]
##etag = "</%s>" % self.tag_markup[tag]
##return stag,etag
def fontdesc_to_attrs(self, font):
"""Convert pango.FontDescription to gtk.Attribute."""
nicks = font.get_set_fields().value_nicks
attrs = []
for n in nicks:
if self.fontdesc_to_attr_table.has_key(n):
Attr, norm = self.fontdesc_to_attr_table[n]
# create an attribute with our current value
attrs.append(Attr(getattr(font, 'get_%s'%n)()))
# unset our font's value
getattr(font,'set_%s'%n)(norm)
return font, attrs
def pango_color_to_gdk(self, pc):
return gtk.gdk.Color(pc.red, pc.green, pc.blue)
def color_to_hex(self, color):
hexstring = ""
for col in 'red', 'green', 'blue':
hexfrag = hex(getattr(color, col) / (16 * 16)).split("x")[1]
if len(hexfrag) < 2:
hexfrag = "0" + hexfrag
hexstring += hexfrag
return hexstring
##def apply_font_and_attrs(self, font, attrs):
##tags = self.get_tags_from_attrs(font,None,attrs)
##for t in tags: self.apply_tag_to_selection(t)
##def remove_font_and_attrs(self, font, attrs):
##tags = self.get_tags_from_attrs(font,None,attrs)
##for t in tags: self.remove_tag_from_selection(t)
##def setup_default_tags(self):
##self.italics = self.get_tags_from_attrs(None,None,
##[pango.AttrStyle('italic')])[0]
##self.bold = self.get_tags_from_attrs(None,None,
##[pango.AttrWeight('bold')])[0]
##self.underline = self.get_tags_from_attrs(None,None,
##[pango.AttrUnderline('single')])[0]
def get_selection(self):
bounds = self.get_selection_bounds()
if not bounds:
iter = self.get_iter_at_mark(self.insert)
if iter.inside_word():
start_pos = iter.get_offset()
iter.forward_word_end()
word_end = iter.get_offset()
iter.backward_word_start()
word_start = iter.get_offset()
iter.set_offset(start_pos)
bounds = (self.get_iter_at_offset(word_start),
self.get_iter_at_offset(word_end + 1))
else:
bounds = (iter, self.get_iter_at_offset(iter.get_offset() + 1))
return bounds
def apply_tag_to_selection(self, tag):
selection = self.get_selection()
if selection:
self.apply_tag(tag, *selection)
def remove_tag_from_selection(self, tag):
selection = self.get_selection()
if selection:
self.remove_tag(tag, *selection)
def remove_all_tags(self):
selection = self.get_selection()
if selection:
for t in self.tags.values():
self.remove_tag(t, *selection)
class EditorBuffer(MarkupBuffer):
"""An interactive interface to allow marking up a gtk.TextBuffer.
normal_button is a widget whose clicked signal will make us normal
toggle_widget_alist is a list that looks like this:
[(widget, (font,attr)),
(widget2, (font,attr))]
"""
__gtype_name__ = 'EditorBuffer'
def __init__(self, normal_button=None, toggle_widget_alist=[]):
MarkupBuffer.__init__(self)
if normal_button:
normal_button.connect('clicked',lambda *args: self.remove_all_tags())
self.tag_widgets = {}
self.internal_toggle = False
self.insert = self.get_insert()
for w, tup in toggle_widget_alist:
self.setup_widget(w, *tup)
# Virtual methods
def do_changed(self):
if not hasattr(self,'last_mark'):
return
# If our insertion point has a mark, we want to apply the tag
# each time the user types...
old_itr = self.get_iter_at_mark(self.last_mark)
insert_itr = self.get_iter_at_mark(self.insert)
if old_itr != insert_itr:
# Use the state of our widgets to determine what
# properties to apply...
for tags, w in self.tag_widgets.items():
if w.get_active():
#print 'apply tags...',tags
for t in tags:
self.apply_tag(t, old_itr, insert_itr)
def do_mark_set(self, iter, mark):
# Every time the cursor moves, update our widgets that reflect
# the state of the text.
if hasattr(self, '_in_mark_set') and self._in_mark_set:
return
self._in_mark_set = True
if mark.get_name() == 'insert':
for tags,widg in self.tag_widgets.items():
active = True
for t in tags:
if not iter.has_tag(t):
active = False
self.internal_toggle = True
widg.set_active(active)
self.internal_toggle = False
if hasattr(self, 'last_mark'):
self.move_mark(self.last_mark, iter)
else:
self.last_mark = self.create_mark('last', iter, left_gravity=True)
self._in_mark_set = False
# Private
def _toggle(self, widget, tags):
if self.internal_toggle: return
if widget.get_active():
for t in tags: self.apply_tag_to_selection(t)
else:
for t in tags: self.remove_tag_from_selection(t)
log.debug("Text: %s" % self.get_text())
# Public API
def setup_widget_from_pango(self, widg, markupstring):
"""Setup widget from a pango markup string."""
a, t, s = pango.parse_markup(markupstring, u'\x00')
ai = a.get_iterator()
# we're gonna use only the first attr from the attrlist
font, lang, attrs = ai.get_font()
return self.setup_widget(widg, font, attrs)
def setup_widget(self, widg, font, attr):
tags = self.get_tags_from_attrs(font, None, attr)
self.tag_widgets[tuple(tags)] = widg
return widg.connect('toggled', self._toggle, tags)
if gtk.pygtk_version < (2,8,0):
gobject.type_register(EditorBuffer)
def main(args):
win = gtk.Window()
win.set_title('MarkupBuffer test window')
win.set_position(gtk.WIN_POS_CENTER)
def cb(window, event):
gtk.main_quit()
win.connect('delete-event', cb)
vbox = gtk.VBox()
win.add(vbox)
text = gtk.TextView()
text.set_accepts_tab(True)
flowed = gtk.RadioButton(None, 'Flowed')
format = gtk.RadioButton(flowed, 'Formatted')
#if self.note_obj and self.note_obj.get_format():
#self.format.set_active(True)
#self.text.set_wrap_mode(gtk.WRAP_NONE)
#else:
#self.flowed.set_active(True)
#self.text.set_wrap_mode(gtk.WRAP_WORD)
#self.spellcheck = Spell.Spell(self.text)
#flowed.connect('toggled', flow_changed)
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.add(text)
vbox.pack_start(scroll, True)
vbox.set_spacing(6)
vbox.set_border_width(6)
hbox = gtk.HBox()
hbox.set_spacing(12)
hbox.set_border_width(6)
hbox.pack_start(flowed, False)
hbox.pack_start(format, False)
vbox.pack_start(hbox, False)
#self.pack_start(vbox, True)
buf = EditorBuffer()
text.set_buffer(buf)
tooltips = gtk.Tooltips()
for tip,stock,font in [('Italic',gtk.STOCK_ITALIC,'<i>italic</i>'),
('Bold',gtk.STOCK_BOLD,'<b>bold</b>'),
('Underline',gtk.STOCK_UNDERLINE,'<u>underline</u>'),
]:
button = gtk.ToggleButton()
image = gtk.Image()
image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
button.set_image(image)
tooltips.set_tip(button, tip)
button.set_relief(gtk.RELIEF_NONE)
buf.setup_widget_from_pango(button,font)
hbox.pack_start(button, False)
win.show_all()
gtk.main()
if __name__ == '__main__':
import sys
stderrh = logging.StreamHandler(sys.stderr)
stderrh.setLevel(logging.DEBUG)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
log.addHandler(stderrh)
sys.exit(main(sys.argv))

View File

@ -345,13 +345,13 @@ class MergePeople:
self.p2 = person2
def copy_note(self,one,two):
text1 = one.get_note()
text2 = two.get_note()
text1 = one.get_note(markup=True)
text2 = two.get_note(markup=True)
if text1 and text1 != text2:
one.set_note("%s\n\n%s" % (text1,text2))
else:
one.set_note(two.get_note())
one.set_note(two.get_note(markup=True))
def copy_sources(self,one,two):
slist = one.get_source_references()[:]
@ -441,7 +441,7 @@ class MergePeople:
# copy urls
new.set_url_list(self.p1.get_url_list() + self.p2.get_url_list())
# merge LDS
# merge LDS
new.set_lds_ord_list(self.p1.get_lds_ord_list() + self.p2.get_lds_ord_list())
# privacy
@ -625,8 +625,8 @@ class MergePeople:
Merges the relationships associated with the merged people.
"""
if __debug__:
print "********Merging Relationships********"
if __debug__:
print "********Merging Relationships********"
family_num = 0
family_list = self.p1.get_family_handle_list()
@ -647,16 +647,16 @@ class MergePeople:
# The target family is already a family in the person's
# family list.
if tgt_family.get_handle() in self.p1.get_family_handle_list():
if __debug__:
print "Merging existing family"
if __debug__:
print "Merging existing family"
self.merge_existing_family(new, src_family, tgt_family, trans)
continue
# This is the case the family is not already in the person's
# family list.
else:
if __debug__:
print "Merging family pair"
if __debug__:
print "Merging family pair"
self.merge_family_pair(tgt_family,src_family,trans)
# change parents of the family to point to the new
@ -791,7 +791,7 @@ class MergePeople:
if __debug__:
print "Remove parent family %s from %s" \
% (src_family_handle,child_handle)
child.add_parent_family_handle(tgt_family.handle)
child.add_parent_family_handle(tgt_family.handle)
self.db.commit_person(child,trans)
# delete the old source family
@ -839,11 +839,11 @@ class MergePeople:
# merge family notes
if src_family.get_note() != "":
old_note = tgt_family.get_note()
if src_family.get_note(markup=True) != "":
old_note = tgt_family.get_note(markup=True)
if old_note:
old_note = old_note + "\n\n"
tgt_family.set_note(old_note + src_family.get_note())
tgt_family.set_note(old_note + src_family.get_note(markup=True))
# merge family top-level sources
@ -919,8 +919,8 @@ class MergePeople:
print "Deleted empty family %s" % family_handle
def merge_notes(self, note1, note2):
t1 = note1.get()
t2 = note2.get()
t1 = note1.get(markup=True)
t2 = note2.get(markup=True)
if not t2:
return note1
elif not t1:

View File

@ -81,7 +81,8 @@ class MergePlaces(ManagedWindow.ManagedWindow):
self.note_merge = self.glade.get_widget('note_merge')
self.note_title = self.glade.get_widget('note_title')
self.note_conflict = self.p1.get_note() and self.p2.get_note()
self.note_conflict = (self.p1.get_note(markup=True) and
self.p2.get_note(markup=True))
if self.note_conflict:
self.note_title.show()
self.note_p1.show()
@ -137,15 +138,15 @@ class MergePlaces(ManagedWindow.ManagedWindow):
# Add notes from P2 to P1
if self.note_conflict:
note1 = self.p1.get_note()
note2 = self.p2.get_note()
note1 = self.p1.get_note(markup=True)
note2 = self.p2.get_note(markup=True)
if self.note_p2.get_active():
self.p1.set_note(note2)
elif self.note_merge.get_active():
self.p1.set_note("%s\n\n%s" % (note1,note2))
else:
note = self.p2.get_note()
if note != "" and self.p1.get_note() == "":
note = self.p2.get_note(markup=True)
if note != "" and self.p1.get_note(markup=True) == "":
self.p1.set_note(note)
if t2active:

View File

@ -100,7 +100,8 @@ class MergeSources(ManagedWindow.ManagedWindow):
self.note_merge = self.glade.get_widget('note_merge')
self.note_title = self.glade.get_widget('note_title')
self.note_conflict = self.s1.get_note() and self.s2.get_note()
self.note_conflict = (self.s1.get_note(markup=True) and
self.s2.get_note(markup=True))
if self.note_conflict:
self.note_title.show()
self.note_s1.show()
@ -151,15 +152,15 @@ class MergeSources(ManagedWindow.ManagedWindow):
# Add notes from S2 to S1
if self.note_conflict:
note1 = self.s1.get_note()
note2 = self.s2.get_note()
note1 = self.s1.get_note(markup=True)
note2 = self.s2.get_note(markup=True)
if self.note_s2.get_active():
self.s1.set_note(note2)
elif self.note_merge.get_active():
self.s1.set_note("%s\n\n%s" % (note1,note2))
else:
note = self.s2.get_note()
if note != "" and self.s1.get_note() == "":
note = self.s2.get_note(markup=True)
if note != "" and self.s1.get_note(markup=True) == "":
self.s1.set_note(note)
src2_map = self.s2.get_data_map()
@ -195,7 +196,7 @@ class MergeSources(ManagedWindow.ManagedWindow):
event = self.db.get_event_from_handle(handle)
if event.has_source_reference(self.old_handle):
event.replace_source_references(self.old_handle,self.new_handle)
self.db.commit_event(event,trans)
self.db.commit_event(event,trans)
# sources
for handle in self.db.get_source_handles():

View File

@ -26,6 +26,8 @@ Note class for GRAMPS
__revision__ = "$Revision$"
import re
#-------------------------------------------------------------------------
#
# GRAMPS modules
@ -76,7 +78,7 @@ class Note(SecondaryObject):
@return: Returns the list of all textual attributes of the object.
@rtype: list
"""
return [self.text]
return [self.delete_tags(self.text)]
def set(self, text):
"""
@ -87,13 +89,39 @@ class Note(SecondaryObject):
"""
self.text = text
def get(self):
def get(self, markup=False):
"""
Return the text string associated with the note.
@param markup: If note should be returned with markup or plain text
@type markup: boolean
@returns: Returns the text string defining the note contents.
@rtype: str
"""
return self.text
text = self.text
if not markup:
text = self.delete_tags(text)
return text
def delete_tags(self, markup_text):
"""
Creates a plain text version of the note text by removing all
pango markup tags.
@param markup_text: Pango style markup text
@type markup_text: str
@return: Plain text
@rtype: str
"""
text = re.sub(r'(<.*?>)', '', markup_text)
text = text.replace('&amp;', '&')
text = text.replace('&lt;', '<')
text = text.replace('&gt;', '>')
return text
def append(self, text):
"""
@ -124,3 +152,13 @@ class Note(SecondaryObject):
"""
return self.format
if __name__ == "__main__":
import hotshot
prof = hotshot.Profile("note.profile")
f = open("notetest3_10.txt")
note = Note(f.read())
for i in range(100000):
prof.runcall(note.get)
prof.close()

View File

@ -82,7 +82,7 @@ class NoteBase:
self.note = Note()
self.note.set(text)
def get_note(self):
def get_note(self, markup=False):
"""
Returns the text of the current note.
@ -90,7 +90,7 @@ class NoteBase:
@rtype: str
"""
if self.note:
return self.note.get()
return self.note.get(markup)
return ""
def set_note_format(self, val):

View File

@ -1217,7 +1217,7 @@ def sanitize_person(db,person):
lds_type == RelLib.LdsOrd.SEAL_TO_SPOUSE :
new_person.add_lds_ord( lds_ord )
new_person.set_note(person.get_note())
new_person.set_note(person.get_note(markup=True))
return new_person
@ -2342,4 +2342,4 @@ def get_endnotes(sref_map,obj):
msg.write("%d" % the_key)
str = msg.getvalue()
msg.close()
return str
return str

View File

@ -796,9 +796,9 @@ class ViewManager:
# FIXME: This used to work, but now DnD switches views
# and messes this up
# If the click is on the same view we're in,
# restore the button state to active
if not button.get_active():
# If the click is on the same view we're in,
# restore the button state to active
if not button.get_active():
button.set_active(True)
self.vb_handlers_unblock()
@ -877,9 +877,9 @@ class ViewManager:
self.page_is_changing = False
def import_pkg(self, filename):
import ReadPkg
ReadPkg.impData(self.state.db, filename, self.uistate.pulse_progressbar)
self.post_load()
import ReadPkg
ReadPkg.impData(self.state.db, filename, self.uistate.pulse_progressbar)
self.post_load()
def import_data(self, obj):
if self.state.db.db_is_open:
@ -943,9 +943,10 @@ class ViewManager:
self.uistate.window.set_title(msg)
self.actiongroup.set_sensitive(True)
# apply preferred researcher if loaded file has none
res = self.state.db.get_researcher()
owner = GrampsCfg.get_researcher()
if res.get_name() == "" and owner.get_name():
if res.get_name() == "" and owner.get_name() != "":
self.state.db.set_researcher(owner)
self.setup_bookmarks()
@ -969,7 +970,7 @@ class ViewManager:
self.change_page(None, None)
self.actiongroup.set_visible(True)
self.readonlygroup.set_visible(True)
self.readonlygroup.set_visible(True)
self.file_loaded = True