From e54cbc8c51f6e2c0ecd25aab2243897b7a2668de Mon Sep 17 00:00:00 2001 From: Zsolt Foldvari Date: Sat, 10 Feb 2007 23:40:48 +0000 Subject: [PATCH] 2007-02-11 Zsolt Foldvari * 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 --- ChangeLog | 14 + src/DisplayTabs/_NoteTab.py | 385 ++----------------- src/GrampsDb/_GrampsBSDDB.py | 4 +- src/GrampsDbUtils/_ReadXML.py | 8 +- src/GrampsDbUtils/_WriteXML.py | 4 +- src/MarkupText.py | 663 +++++++++++++++++++++++++++++++++ src/Merge/_MergePerson.py | 32 +- src/Merge/_MergePlace.py | 11 +- src/Merge/_MergeSource.py | 13 +- src/RelLib/_Note.py | 44 ++- src/RelLib/_NoteBase.py | 4 +- src/ReportBase/_ReportUtils.py | 4 +- src/ViewManager.py | 17 +- 13 files changed, 789 insertions(+), 414 deletions(-) create mode 100644 src/MarkupText.py diff --git a/ChangeLog b/ChangeLog index 321200272..373aa1d23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-02-11 Zsolt Foldvari + * 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 * src/plugins/Check.py: fix illegal family events * src/GrampsDbUtils/_GedcomParse.py: improve parsing diff --git a/src/DisplayTabs/_NoteTab.py b/src/DisplayTabs/_NoteTab.py index f780208cf..8008073fe 100644 --- a/src/DisplayTabs/_NoteTab.py +++ b/src/DisplayTabs/_NoteTab.py @@ -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,'italic'), - ('gtk-bold',True,'bold'), - ('gtk-underline',True,'underline'), - #('Blue',True,'blue'), - #('Red',False,'smallcaps'), - ]: - 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,'italic'), + ('Bold',gtk.STOCK_BOLD,'bold'), + ('Underline',gtk.STOCK_UNDERLINE,'underline'), + ]: + 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 = "" - - 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_ 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) diff --git a/src/GrampsDb/_GrampsBSDDB.py b/src/GrampsDb/_GrampsBSDDB.py index d1de9f483..3c96e55da 100644 --- a/src/GrampsDb/_GrampsBSDDB.py +++ b/src/GrampsDb/_GrampsBSDDB.py @@ -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) diff --git a/src/GrampsDbUtils/_ReadXML.py b/src/GrampsDbUtils/_ReadXML.py index d56dc2060..5649dc810 100644 --- a/src/GrampsDbUtils/_ReadXML.py +++ b/src/GrampsDbUtils/_ReadXML.py @@ -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: diff --git a/src/GrampsDbUtils/_WriteXML.py b/src/GrampsDbUtils/_WriteXML.py index 55c9b83ef..26acc93a3 100644 --- a/src/GrampsDbUtils/_WriteXML.py +++ b/src/GrampsDbUtils/_WriteXML.py @@ -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() diff --git a/src/MarkupText.py b/src/MarkupText.py new file mode 100644 index 000000000..4c2a8d15f --- /dev/null +++ b/src/MarkupText.py @@ -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. , ). + + """ + 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 = "" + ##stag = "<%s>" % self.tag_markup[tag] + ##etag = "" % 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,'italic'), + ('Bold',gtk.STOCK_BOLD,'bold'), + ('Underline',gtk.STOCK_UNDERLINE,'underline'), + ]: + 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)) diff --git a/src/Merge/_MergePerson.py b/src/Merge/_MergePerson.py index 21549bb97..2edf33c5c 100644 --- a/src/Merge/_MergePerson.py +++ b/src/Merge/_MergePerson.py @@ -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: diff --git a/src/Merge/_MergePlace.py b/src/Merge/_MergePlace.py index cba7f121d..c5887836a 100644 --- a/src/Merge/_MergePlace.py +++ b/src/Merge/_MergePlace.py @@ -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: diff --git a/src/Merge/_MergeSource.py b/src/Merge/_MergeSource.py index ded4a0bcd..9aacf2c5d 100644 --- a/src/Merge/_MergeSource.py +++ b/src/Merge/_MergeSource.py @@ -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(): diff --git a/src/RelLib/_Note.py b/src/RelLib/_Note.py index 32785c734..52178b592 100644 --- a/src/RelLib/_Note.py +++ b/src/RelLib/_Note.py @@ -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('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') + + 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() diff --git a/src/RelLib/_NoteBase.py b/src/RelLib/_NoteBase.py index c95c9b1e5..aee4603ab 100644 --- a/src/RelLib/_NoteBase.py +++ b/src/RelLib/_NoteBase.py @@ -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): diff --git a/src/ReportBase/_ReportUtils.py b/src/ReportBase/_ReportUtils.py index 315b192db..72829d3be 100644 --- a/src/ReportBase/_ReportUtils.py +++ b/src/ReportBase/_ReportUtils.py @@ -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 \ No newline at end of file + return str diff --git a/src/ViewManager.py b/src/ViewManager.py index 07017e013..83c12ced8 100644 --- a/src/ViewManager.py +++ b/src/ViewManager.py @@ -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