From 4f390df00d8e9d0dff2e76912894d12ea5f222df Mon Sep 17 00:00:00 2001 From: Zsolt Foldvari Date: Mon, 12 Feb 2007 19:53:30 +0000 Subject: [PATCH] 2007-02-12 Zsolt Foldvari * src/MarkupText.py: improvements * src/RelLib/_Note.py (get): try to remove tags only if note is formatted * src/DisplayTabs/_NoteTab.py: cleanup svn: r8092 --- ChangeLog | 25 +- src/DisplayTabs/_NoteTab.py | 12 +- src/MarkupText.py | 774 +++++++++++++++++------------------- src/RelLib/_Note.py | 2 +- 4 files changed, 366 insertions(+), 447 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4d4fa3732..3a9ae3f86 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2007-02-12 Zsolt Foldvari + * src/MarkupText.py: improvements + * src/RelLib/_Note.py (get): try to remove tags only if note is + formatted + * src/DisplayTabs/_NoteTab.py: cleanup + 2007-02-11 Brian Matherly * src/BaseDoc.py: remove write_at * src/docgen/PSDrawDoc.py: remove write_at @@ -28,25 +34,6 @@ * src/GrampsDbUtils/Makefile.am: new GEDCOM parser module * src/ArgHandler.py: new GEDCOM parser module * example/gedcom/sample.ged: added test cases -2007-02-11 Don Allingham - * src/Merge/_MergePlace.py (MergePlaces.merge): fix typo on - add_source_reference - -2007-02-07 Zsolt Foldvari - * src/DisplayTabs/_NoteTab.py: move "rich text" notes to 2.3 branch. - -2007-02-06 Brian Matherly - * src/docgen/PdfDoc.py: provide a more useful error when reportlab crashes - -2007-02-06 Douglas Blank - * src/ReportBase/_ReportDialog.py: 0000905: Typo in catching exceptions in - buggy reports - * src/plugins/holidays.xml: 0000906: Canada holidays patch - -2007-02-06 Don Allingham - * src/GrampsDb/_ReadGedcom.py: fix cal/est parsing - * src/DisplayTabs/_ButtonTab.py: catch window already being open - * example/gedcom/sample.ged: Add est/cal samples 2007-02-10 Brian Matherly * src/BaseDoc.py: remove unused functions diff --git a/src/DisplayTabs/_NoteTab.py b/src/DisplayTabs/_NoteTab.py index 8008073fe..3c039fdd3 100644 --- a/src/DisplayTabs/_NoteTab.py +++ b/src/DisplayTabs/_NoteTab.py @@ -119,9 +119,9 @@ class NoteTab(GrampsTab): 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'), + for tip,stock,font in [('Italic',gtk.STOCK_ITALIC,'i'), + ('Bold',gtk.STOCK_BOLD,'b'), + ('Underline',gtk.STOCK_UNDERLINE,'u'), ]: button = gtk.ToggleButton() image = gtk.Image() @@ -129,14 +129,12 @@ class NoteTab(GrampsTab): button.set_image(image) tooltips.set_tip(button, tip) button.set_relief(gtk.RELIEF_NONE) - self.buf.setup_widget_from_pango(button,font) + self.buf.setup_widget_from_xml(button,font) hbox.pack_start(button, False) - ##self.buf = self.text.get_buffer() if self.note_obj: self.empty = False 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()) + #log.debug("Text: %s" % self.buf.get_text()) else: self.empty = True diff --git a/src/MarkupText.py b/src/MarkupText.py index 4c2a8d15f..20d5973a5 100644 --- a/src/MarkupText.py +++ b/src/MarkupText.py @@ -25,8 +25,13 @@ # Python classes # #------------------------------------------------------------------------- -from xml.sax import saxutils, xmlreader -from cStringIO import StringIO +from xml.sax import saxutils, xmlreader, ContentHandler +from xml.sax import parseString, SAXParseException +import re +try: + from cStringIO import StringIO +except: + from StringIO import StringIO #------------------------------------------------------------------------- # @@ -49,31 +54,142 @@ import pango # Constants # #------------------------------------------------------------------------- -(EVENT_START, - EVENT_END) = range(2) +class MarkupParser(ContentHandler): + """A simple ContentHandler class to parse Gramps markup'ed text. + + Use it with xml.sax.parse() or xml.sax.parseString(). + Parsing result can be obtained via it's public attributes. + + @attr content: clean text + @attr type: str + @attr elements: list of markup elements + @attr type: list[tuple((start, end), name, attrs),] + + """ + (EVENT_START, + EVENT_END) = range(2) -class NoteXMLWriter(saxutils.XMLGenerator): + def startDocument(self): + self._open_document = False + self._open_elements = [] + self.elements = [] + self.content = "" + + def endDocument(self): + self._open_document = False + + def startElement(self, name, attrs): + if not self._open_document: + if name == 'gramps': + self._open_document = True + else: + raise SAXParseException('Root tag missing') + else: + self._open_elements.append({'name': name, + 'attrs': attrs.copy(), + 'start': len(self.content), + }) + + def endElement(self, name): + # skip root element + if name == 'gramps': + return + + for e in self._open_elements: + if e['name'] == name: + self.elements.append(((e['start'], len(self.content)), + e['name'], e['attrs'])) + + self._open_elements.remove(e) + return + + def characters (self, chunk): + self.content += chunk + +class MarkupWriter: """Generate XML markup text for Notes. Provides additional feature of accounting opened tags and closing them - properly in case of partialy overlapping markups. + properly in case of partially 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 __init__(self, encoding='utf-8'): + self._output = StringIO() + self._encoding = encoding + self._writer = saxutils.XMLGenerator(self._output, self._encoding) - def endElement(self, name): - if not len(self.open_elements): + self._attrs = xmlreader.AttributesImpl({}) + + self._open_elements = [] + self.content = '' + + # Private + + def _elements_to_events(self, elements): + """Create an event list for XML writer. + + @param tagdict: dictionary of tags with start and end indices + @param type: {TextTag: [(start, end),]} + @return: eventdict + @rtype: {index: [(TextTag, event_type, pair_index),]} + index: place of the event + TextTag: tag to apply + event_type: START or END event + pair_index: index of the pair event, used for sorting + + """ + eventdict = {} + for (start, end), name, attrs in elements: + # insert START events + if eventdict.has_key(start): + eventdict[start].append((name, MarkupParser.EVENT_START, end)) + else: + eventdict[start] = [(name, MarkupParser.EVENT_START, end)] + # insert END events + if eventdict.has_key(end): + eventdict[end].append((name, MarkupParser.EVENT_END, start)) + else: + eventdict[end] = [(name, MarkupParser.EVENT_END, start)] + + # sort events at the same index + indices = eventdict.keys() + for idx in indices: + if len(eventdict[idx]) > 1: + eventdict[idx].sort(self._sort_events) + + return eventdict + + 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) == (MarkupParser.EVENT_START + + MarkupParser.EVENT_END): + return type_b - type_a + else: + return pair_b - pair_a + + def _startElement(self, name, attrs=None): + """Insert start tag.""" + if not attrs: + attrs = self._attrs + self._writer.startElement(name, attrs) + self._open_elements.append(name) + + def _endElement(self, name): + """Insert end tag.""" + if not len(self._open_elements): log.debug("Trying to close element '%s' when non is open" % name) return @@ -83,8 +199,8 @@ class NoteXMLWriter(saxutils.XMLGenerator): # 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) + elem = self._open_elements.pop() + self._writer.endElement(elem) if elem != name: tmp_list.append(elem) except: @@ -96,245 +212,133 @@ class NoteXMLWriter(saxutils.XMLGenerator): while True: try: elem = tmp_list.pop() - self.startElement(elem) + self._startElement(elem) except: break - def close(self): - #saxutils.XMLGenerator.endElement(self, u'gramps') - self.endDocument() + # Public + def generate(self, text, elements): + # reset output and start root element + self._output.seek(0) + self._writer.startElement('gramps', self._attrs) + + # split the elements to events + events = self._elements_to_events(elements) + + # feed the events into the xml generator + last_pos = 0 + indices = events.keys() + indices.sort() + for index in indices: + self._writer.characters(text[last_pos:index]) + for name, event_type, p in events[index]: + if event_type == MarkupParser.EVENT_START: + self._startElement(name) + elif event_type == MarkupParser.EVENT_END: + self._endElement(name) + last_pos = index + self._writer.characters(text[last_pos:]) + + # close root element and end doc + self._writer.endElement('gramps') + self._writer.endDocument() + + # copy result + self.content = self._output.getvalue() + 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', + """An extended TextBuffer with Gramps XML markup string interface. + + It implements MarkupParser and MarkupWriter on the input/output interface. + Also translates Gramps XML markup language to gtk.TextTag's and vice versa. + + """ + texttag_to_xml = { 'weight700': 'b', + 'style2': 'i', 'underline1': 'u', } + + xml_to_texttag = { + 'b': ('weight', 700), + 'i': ('style', 2), + 'u': ('underline', 1), + } def __init__(self): - self.tagdict = {} + self.parser = MarkupParser() + self.writer = MarkupWriter() self.tags = {} self.tag_markup = {} gtk.TextBuffer.__init__(self) - def set_text(self, pango_text): + def set_text(self, xmltext): + """Set the content of the buffer with markup tags""" try: - attrlist, text, accel = pango.parse_markup(pango_text, u'\x00') + parseString(xmltext, self.parser) + text = self.parser.content 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') - + # if parse fails remove all tags and use clear text instead + text = re.sub(r'(<.*?>)', '', xmltext) + text = saxutils.unescape(text) + 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) + for element in self.parser.elements: + self.add_element_to_buffer(element) - def add_iter_to_buffer(self, attriter): - """Insert attributes into the buffer. + def add_element_to_buffer(self, elem): + """Apply the xml element to the buffer""" + (start, end), name, attrs = elem + + tag = self.get_tag_from_element(name) + + if tag: + start_iter = self.get_iter_at_offset(start) + end_iter = self.get_iter_at_offset(end) + + self.apply_tag(tag, start_iter, end_iter) + + def get_tag_from_element(self, name): + """Convert xml element to gtk.TextTag""" + if not self.xml_to_texttag.has_key(name): + return None + + prop, val = self.xml_to_texttag[name] + + 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.tag_markup[self.tags[key]] = self.texttag_to_xml[key] + + return self.tags[key] + + def get_text(self, start=None, end=None, include_hidden_chars=True): + """Returns the buffer text with xml markup tags. - Convert the pango.Attribute at the received pango.AttrIterator - to gtk.TextTag and apply them on the buffer at the proper indices + If no markup was applied returns clean text. """ - 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) - + # get the clear text from the buffer 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)) + + # extract tags out of the buffer + tags = self.get_tags() - 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() + if len(tags): + # convert the tags to xml elements + elements = self.get_elements(tags) + # feed the elements into the xml writer + self.writer.generate(txt, elements) + txt = self.writer.content + + return txt def get_tags(self): """Extract TextTags from buffer. @@ -355,113 +359,37 @@ class MarkupBuffer(gtk.TextBuffer): else: tagdict[tag]=[(pos, pos)] return tagdict - - def get_event_list(self, tagdict): - """Create an event list for XML writer. + + def get_elements(self, tagdict): + """Convert TextTags to xml elements. - @param tagdict: tag dict to convert from + Create the format what MarkupWriter likes + @param tagdict: TextTag dictionary @param type: {TextTag: [(start, end),]} - @return: eventlist - @rtype: {index: [(TextTag, EVENT_TYPE, pair_index),]} + @return: elements; xml element list + @rtype: [((start, end), name, attrs)] """ - eventlist = {} + elements = [] 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)] + elements.append(((start_idx, end_idx+1), + self.tag_markup[text_tag], + None)) + return elements - # 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 pango_color_to_gdk(self, pc): + ##return gtk.gdk.Color(pc.red, pc.green, pc.blue) - def sort_events(self, event_a, event_b): - """Sort events that are at the same index. + ##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 - 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: @@ -496,7 +424,7 @@ class MarkupBuffer(gtk.TextBuffer): self.remove_tag(t, *selection) class EditorBuffer(MarkupBuffer): - """An interactive interface to allow marking up a gtk.TextBuffer. + """An interactive interface to allow markup 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: @@ -529,11 +457,9 @@ class EditorBuffer(MarkupBuffer): 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(): + for tag, 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) + self.apply_tag(tag, old_itr, insert_itr) def do_mark_set(self, iter, mark): # Every time the cursor moves, update our widgets that reflect @@ -543,11 +469,10 @@ class EditorBuffer(MarkupBuffer): self._in_mark_set = True if mark.get_name() == 'insert': - for tags,widg in self.tag_widgets.items(): + for tag,widg in self.tag_widgets.items(): active = True - for t in tags: - if not iter.has_tag(t): - active = False + if not iter.has_tag(tag): + active = False self.internal_toggle = True widg.set_active(active) self.internal_toggle = False @@ -559,99 +484,108 @@ class EditorBuffer(MarkupBuffer): # Private - def _toggle(self, widget, tags): - if self.internal_toggle: return + def _toggle(self, widget, tag): + if self.internal_toggle: + return + if widget.get_active(): - for t in tags: self.apply_tag_to_selection(t) + self.apply_tag_to_selection(tag) else: - for t in tags: self.remove_tag_from_selection(t) - - log.debug("Text: %s" % self.get_text()) + self.remove_tag_from_selection(tag) # 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) + def setup_widget_from_xml(self, widg, xmlstring): + """Setup widget from an xml markup string.""" + try: + parseString("%s" % xmlstring, self.parser) + except: + # raise Error + log.debug("set: " % self.parser.content) + + (start, end), name, attrs = self.parser.elements[0] + + return self.setup_widget(widg, name) + def setup_widget(self, widg, name): + tag = self.get_tag_from_element(name) + self.tag_widgets[tag] = widg + return widg.connect('toggled', self._toggle, tag) + 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 + + 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'), + ('Bold',gtk.STOCK_BOLD,'b'), + ('Underline',gtk.STOCK_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_xml(button,font) + hbox.pack_start(button, False) + + buf.set_text('' + 'Bold. Italic. Underline.' + '') + + win.show_all() + gtk.main() + stderrh = logging.StreamHandler(sys.stderr) stderrh.setLevel(logging.DEBUG) diff --git a/src/RelLib/_Note.py b/src/RelLib/_Note.py index 52178b592..ad15a8c93 100644 --- a/src/RelLib/_Note.py +++ b/src/RelLib/_Note.py @@ -100,7 +100,7 @@ class Note(SecondaryObject): """ text = self.text - if not markup: + if not markup and text[0:8] == '': text = self.delete_tags(text) return text