2007-02-11 05:10:48 +05:30
|
|
|
#
|
|
|
|
# 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$
|
|
|
|
|
2007-02-13 21:17:56 +05:30
|
|
|
"Handling formatted ('rich text') strings"
|
|
|
|
|
|
|
|
__revision__ = "$Revision$"
|
|
|
|
__author__ = "Zsolt Foldvari"
|
|
|
|
|
2007-02-11 05:10:48 +05:30
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
2007-02-13 21:17:56 +05:30
|
|
|
# Python modules
|
2007-02-11 05:10:48 +05:30
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2007-02-13 01:23:30 +05:30
|
|
|
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
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
2007-02-13 21:17:56 +05:30
|
|
|
# Set up logging
|
2007-02-11 05:10:48 +05:30
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
import logging
|
|
|
|
log = logging.getLogger(".MarkupText")
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
2007-02-13 21:17:56 +05:30
|
|
|
# GTK modules
|
2007-02-11 05:10:48 +05:30
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
import gtk
|
|
|
|
|
2007-02-13 21:17:56 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
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)
|
|
|
|
|
|
|
|
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:
|
2007-02-13 04:03:03 +05:30
|
|
|
raise SAXParseException('Root element missing')
|
2007-02-13 01:23:30 +05:30
|
|
|
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
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
class MarkupWriter:
|
2007-02-11 05:10:48 +05:30
|
|
|
"""Generate XML markup text for Notes.
|
|
|
|
|
|
|
|
Provides additional feature of accounting opened tags and closing them
|
2007-02-13 01:23:30 +05:30
|
|
|
properly in case of partially overlapping markups.
|
2007-02-11 05:10:48 +05:30
|
|
|
It is assumed that 'start name' and 'end name' are equal (e.g. <b>, </b>).
|
|
|
|
|
|
|
|
"""
|
2007-02-13 01:23:30 +05:30
|
|
|
def __init__(self, encoding='utf-8'):
|
|
|
|
self._output = StringIO()
|
|
|
|
self._encoding = encoding
|
|
|
|
self._writer = saxutils.XMLGenerator(self._output, self._encoding)
|
|
|
|
|
|
|
|
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."""
|
2007-02-11 05:10:48 +05:30
|
|
|
if not attrs:
|
2007-02-13 01:23:30 +05:30
|
|
|
attrs = self._attrs
|
|
|
|
self._writer.startElement(name, attrs)
|
|
|
|
self._open_elements.append(name)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
def _endElement(self, name):
|
|
|
|
"""Insert end tag."""
|
|
|
|
if not len(self._open_elements):
|
2007-02-11 05:10:48 +05:30
|
|
|
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:
|
2007-02-13 01:23:30 +05:30
|
|
|
elem = self._open_elements.pop()
|
|
|
|
self._writer.endElement(elem)
|
2007-02-11 05:10:48 +05:30
|
|
|
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()
|
2007-02-13 01:23:30 +05:30
|
|
|
self._startElement(elem)
|
2007-02-11 05:10:48 +05:30
|
|
|
except:
|
|
|
|
break
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
# Public
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
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()
|
|
|
|
|
2007-02-11 05:10:48 +05:30
|
|
|
class MarkupBuffer(gtk.TextBuffer):
|
2007-02-13 01:23:30 +05:30
|
|
|
"""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.
|
|
|
|
|
2007-02-13 21:17:56 +05:30
|
|
|
Based on 'gourmet-0.13.3', http://grecipe-manager.sourceforge.net/
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
"""
|
|
|
|
texttag_to_xml = {
|
2007-02-11 05:10:48 +05:30
|
|
|
'weight700': 'b',
|
2007-02-13 01:23:30 +05:30
|
|
|
'style2': 'i',
|
2007-02-11 05:10:48 +05:30
|
|
|
'underline1': 'u',
|
|
|
|
}
|
2007-02-13 01:23:30 +05:30
|
|
|
|
|
|
|
xml_to_texttag = {
|
|
|
|
'b': ('weight', 700),
|
|
|
|
'i': ('style', 2),
|
|
|
|
'u': ('underline', 1),
|
|
|
|
}
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
def __init__(self):
|
2007-02-13 01:23:30 +05:30
|
|
|
self.parser = MarkupParser()
|
|
|
|
self.writer = MarkupWriter()
|
2007-02-11 05:10:48 +05:30
|
|
|
self.tags = {}
|
|
|
|
self.tag_markup = {}
|
|
|
|
gtk.TextBuffer.__init__(self)
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
def set_text(self, xmltext):
|
|
|
|
"""Set the content of the buffer with markup tags"""
|
2007-02-11 05:10:48 +05:30
|
|
|
try:
|
2007-02-13 01:23:30 +05:30
|
|
|
parseString(xmltext, self.parser)
|
|
|
|
text = self.parser.content
|
2007-02-11 05:10:48 +05:30
|
|
|
except:
|
2007-02-13 01:23:30 +05:30
|
|
|
# if parse fails remove all tags and use clear text instead
|
|
|
|
text = re.sub(r'(<.*?>)', '', xmltext)
|
|
|
|
text = saxutils.unescape(text)
|
|
|
|
|
2007-02-11 05:10:48 +05:30
|
|
|
gtk.TextBuffer.set_text(self, text)
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
for element in self.parser.elements:
|
|
|
|
self.add_element_to_buffer(element)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
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)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
if tag:
|
|
|
|
start_iter = self.get_iter_at_offset(start)
|
|
|
|
end_iter = self.get_iter_at_offset(end)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
self.apply_tag(tag, start_iter, end_iter)
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
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]
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
def get_text(self, start=None, end=None, include_hidden_chars=True):
|
|
|
|
"""Returns the buffer text with xml markup tags.
|
|
|
|
|
|
|
|
If no markup was applied returns clean text.
|
|
|
|
|
|
|
|
"""
|
|
|
|
# get the clear text from the buffer
|
2007-02-11 05:10:48 +05:30
|
|
|
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))
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
# extract tags out of the buffer
|
|
|
|
tags = self.get_tags()
|
|
|
|
|
|
|
|
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
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
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
|
2007-02-13 01:23:30 +05:30
|
|
|
|
|
|
|
def get_elements(self, tagdict):
|
|
|
|
"""Convert TextTags to xml elements.
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
Create the format what MarkupWriter likes
|
|
|
|
@param tagdict: TextTag dictionary
|
2007-02-11 05:10:48 +05:30
|
|
|
@param type: {TextTag: [(start, end),]}
|
2007-02-13 01:23:30 +05:30
|
|
|
@return: elements; xml element list
|
|
|
|
@rtype: [((start, end), name, attrs)]
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
"""
|
2007-02-13 01:23:30 +05:30
|
|
|
elements = []
|
2007-02-11 05:10:48 +05:30
|
|
|
for text_tag, indices in tagdict.items():
|
|
|
|
for start_idx, end_idx in indices:
|
2007-02-13 01:23:30 +05:30
|
|
|
elements.append(((start_idx, end_idx+1),
|
|
|
|
self.tag_markup[text_tag],
|
|
|
|
None))
|
|
|
|
return elements
|
|
|
|
|
|
|
|
##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
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
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):
|
2007-02-13 01:23:30 +05:30
|
|
|
"""An interactive interface to allow markup a gtk.TextBuffer.
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
normal_button is a widget whose clicked signal will make us normal
|
2007-02-13 21:17:56 +05:30
|
|
|
toggle_widget_alist is a list that looks like this: [(widget, tag_name),]
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 21:17:56 +05:30
|
|
|
Based on 'gourmet-0.13.3', http://grecipe-manager.sourceforge.net/
|
|
|
|
|
2007-02-11 05:10:48 +05:30
|
|
|
"""
|
|
|
|
__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()
|
2007-02-13 21:17:56 +05:30
|
|
|
for widg, name in toggle_widget_alist:
|
|
|
|
self.setup_widget(widg, name)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
# 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...
|
2007-02-13 01:23:30 +05:30
|
|
|
for tag, w in self.tag_widgets.items():
|
2007-02-11 05:10:48 +05:30
|
|
|
if w.get_active():
|
2007-02-13 01:23:30 +05:30
|
|
|
self.apply_tag(tag, old_itr, insert_itr)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
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':
|
2007-02-13 01:23:30 +05:30
|
|
|
for tag,widg in self.tag_widgets.items():
|
2007-02-11 05:10:48 +05:30
|
|
|
active = True
|
2007-02-13 01:23:30 +05:30
|
|
|
if not iter.has_tag(tag):
|
|
|
|
active = False
|
2007-02-11 05:10:48 +05:30
|
|
|
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
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
def _toggle(self, widget, tag):
|
|
|
|
if self.internal_toggle:
|
|
|
|
return
|
|
|
|
|
2007-02-11 05:10:48 +05:30
|
|
|
if widget.get_active():
|
2007-02-13 01:23:30 +05:30
|
|
|
self.apply_tag_to_selection(tag)
|
2007-02-11 05:10:48 +05:30
|
|
|
else:
|
2007-02-13 01:23:30 +05:30
|
|
|
self.remove_tag_from_selection(tag)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
# Public API
|
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
def setup_widget_from_xml(self, widg, xmlstring):
|
|
|
|
"""Setup widget from an xml markup string."""
|
|
|
|
try:
|
|
|
|
parseString("<gramps>%s</gramps>" % 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)
|
2007-02-11 05:10:48 +05:30
|
|
|
|
2007-02-13 01:23:30 +05:30
|
|
|
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)
|
|
|
|
|
2007-02-11 05:10:48 +05:30
|
|
|
if gtk.pygtk_version < (2,8,0):
|
|
|
|
gobject.type_register(EditorBuffer)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
import sys
|
2007-02-13 01:23:30 +05:30
|
|
|
|
|
|
|
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>i</i>'),
|
|
|
|
('Bold',gtk.STOCK_BOLD,'<b>b</b>'),
|
|
|
|
('Underline',gtk.STOCK_UNDERLINE,'<u>u</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('<gramps>'
|
|
|
|
'<b>Bold</b>. <i>Italic</i>. <u>Underline</u>.'
|
|
|
|
'</gramps>')
|
|
|
|
|
|
|
|
win.show_all()
|
|
|
|
gtk.main()
|
|
|
|
|
2007-02-11 05:10:48 +05:30
|
|
|
|
|
|
|
stderrh = logging.StreamHandler(sys.stderr)
|
|
|
|
stderrh.setLevel(logging.DEBUG)
|
|
|
|
|
|
|
|
log = logging.getLogger()
|
|
|
|
log.setLevel(logging.DEBUG)
|
|
|
|
log.addHandler(stderrh)
|
|
|
|
|
|
|
|
sys.exit(main(sys.argv))
|