91
93
element_styles['i'] = element_styles['em']
92
94
element_styles['b'] = element_styles['strong']
99
This Integration Set includes a subset of the modules defined for
100
XHTML 1.0 but does not redefine any existing modules, nor
101
does it define any new modules. Specifically, it includes the
102
following modules only:
110
addr, blockquote, pre
114
h1, h2, h3, h4, h5, h6
119
abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var
128
Therefore XHTML-IM uses the following content models:
131
Block-like elements, e.g., paragraphs
133
Any block or inline elements
135
Character-level elements
141
XHTML-IM also uses the following Attribute Groups:
154
# ( pres = h1 | h2 | h3 | h4 | h5 | h6 )
155
#Block ( phrasal = address | blockquote | pre )
156
#NOT ( presentational = hr )
157
# ( structural = div | p )
159
#Inline ( phrasal = abbr | acronym | cite | code | dfn | em | kbd | q | samp | strong | var )
160
#NOT ( presentational = b | big | i | small | sub | sup | tt )
161
# ( structural = br | span )
162
#Param/Legacy param, font, basefont, center, s, strike, u, dir, menu, isindex
100
# This Integration Set includes a subset of the modules defined for
101
# XHTML 1.0 but does not redefine any existing modules, nor
102
# does it define any new modules. Specifically, it includes the
103
# following modules only:
111
# addr, blockquote, pre
115
# h1, h2, h3, h4, h5, h6
120
# abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var
125
# - List (ul, ol, dl)
129
# Therefore XHTML-IM uses the following content models:
132
# Block-like elements, e.g., paragraphs
134
# Any block or inline elements
136
# Character-level elements
137
# InlineNoAnchor.class
142
# XHTML-IM also uses the following Attribute Groups:
155
# ( pres = h1 | h2 | h3 | h4 | h5 | h6 )
156
# Block ( phrasal = address | blockquote | pre )
157
# NOT ( presentational = hr )
158
# ( structural = div | p )
160
# Inline ( phrasal = abbr | acronym | cite | code | dfn | em |
161
# kbd | q | samp | strong | var )
162
# NOT ( presentational = b | big | i | small | sub | sup | tt )
163
# ( structural = br | span )
164
# Param/Legacy param, font, basefont, center, s, strike, u, dir, menu,
166
167
BLOCK_HEAD = set(( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', ))
167
168
BLOCK_PHRASAL = set(( 'address', 'blockquote', 'pre', ))
185
186
('font-weight: bold', 'font-style: oblique')[weigth],
189
def build_patterns(view, config, interface):
190
# extra, rst does not mark _underline_ or /it/ up
191
# actually <b>, <i> or <u> are not in the JEP-0071, but are seen in the wild
192
basic_pattern = r'(?<!\w|\<|/|:)' r'/[^\s/]' r'([^/]*[^\s/])?' r'/(?!\w|/|:)|'\
193
r'(?<!\w)' r'_[^\s_]' r'([^_]*[^\s_])?' r'_(?!\w)'
194
view.basic_pattern_re = re.compile(basic_pattern)
196
emoticons_pattern = ''
197
if config.get('emoticons_theme'):
198
emoticons_pattern = gajim.interface.emot_only
200
view.emot_pattern_re = re.compile(emoticons_pattern, re.IGNORECASE)
201
# because emoticons match later (in the string) they need to be after
202
# basic matches that may occur earlier
203
emot_and_basic_pattern = basic_pattern + emoticons_pattern
204
view.emot_and_basic_re = re.compile(emot_and_basic_pattern, re.IGNORECASE)
207
189
def _parse_css_color(color):
208
190
'''_parse_css_color(css_color) -> gtk.gdk.Color'''
209
191
if color.startswith('rgb(') and color.endswith(')'):
222
205
It keeps a stack of "style spans" (start/end element pairs)
223
206
and a stack of list counters, for nested lists.
225
def __init__(self, textview, startiter):
208
def __init__(self, conv_textview, startiter):
226
209
xml.sax.handler.ContentHandler.__init__(self)
227
self.textbuf = textview.get_buffer()
228
self.textview = textview
210
self.textbuf = conv_textview.tv.get_buffer()
211
self.textview = conv_textview.tv
229
212
self.iter = startiter
213
self.conv_textview = conv_textview
231
215
self.starting=True
232
216
self.preserve = False
244
228
tag.set_property('paragraph-background-gdk', color)
247
#FIXME: when we migrate to 2.10 rm this
248
if gtk.gtk_version >= (2, 8, 5) or gobject.pygtk_version >= (2, 8, 1):
250
def _get_current_attributes(self):
251
attrs = self.textview.get_default_attributes()
252
self.iter.backward_char()
253
self.iter.get_attributes(attrs)
254
self.iter.forward_char()
259
# Workaround http://bugzilla.gnome.org/show_bug.cgi?id=317455
260
def _get_current_style_attr(self, propname, comb_oper=None):
261
tags = [tag for tag in self.styles if tag is not None]
263
is_set_name = propname + '-set'
266
if tag.get_property(is_set_name):
268
value = tag.get_property(propname)
269
if comb_oper is None:
272
value = comb_oper(value, tag.get_property(propname))
275
class _FakeAttrs(object):
276
__slots__ = ('font', 'font_scale')
278
def _get_current_attributes(self):
279
attrs = self._FakeAttrs()
280
attrs.font_scale = self._get_current_style_attr('scale',
282
if attrs.font_scale is None:
283
attrs.font_scale = 1.0
284
attrs.font = self._get_current_style_attr('font-desc')
285
if attrs.font is None:
286
attrs.font = self.textview.style.font_desc
231
def _get_current_attributes(self):
232
attrs = self.textview.get_default_attributes()
233
self.iter.backward_char()
234
self.iter.get_attributes(attrs)
235
self.iter.forward_char()
290
238
def __parse_length_frac_size_allocate(self, textview, allocation,
291
239
frac, callback, args):
535
483
def _process_img(self, attrs):
536
484
'''Process a img tag.
539
# Wait maximum 1s for connection
488
# Wait maximum 1s for connection
540
489
socket.setdefaulttimeout(1)
542
f = urllib2.urlopen(attrs['src'])
543
except Exception, ex:
491
f = urllib2.urlopen(attrs['src'])
492
except Exception, ex:
544
493
gajim.log.debug('Error loading image %s ' % attrs['src'] + str(ex))
546
alt = attrs.get('alt', 'Broken image')
548
# Wait 0.1s between each byte
550
f.fp._sock.fp._sock.settimeout(0.5)
553
# Max image size = 2 MB (to try to prevent DoS)
555
deadline = time.time() + 3
557
if time.time() > deadline:
558
gajim.log.debug(str('Timeout loading image %s ' % \
561
alt = attrs.get('alt', '')
564
alt += _('Timeout loading image')
495
alt = attrs.get('alt', 'Broken image')
497
# Wait 0.1s between each byte
568
except socket.timeout, ex:
569
gajim.log.debug('Timeout loading image %s ' % attrs['src'] + \
572
alt = attrs.get('alt', '')
575
alt += _('Timeout loading image')
581
if len(mem) > 2*1024*1024:
582
alt = attrs.get('alt', '')
585
alt += _('Image is too big')
499
f.fp._sock.fp._sock.settimeout(0.5)
502
# Max image size = 2 MB (to try to prevent DoS)
503
deadline = time.time() + 3
505
if time.time() > deadline:
506
gajim.log.debug(str('Timeout loading image %s ' % \
509
alt = attrs.get('alt', '')
512
alt += _('Timeout loading image')
516
except socket.timeout, ex:
517
gajim.log.debug('Timeout loading image %s ' % attrs['src'] + \
519
alt = attrs.get('alt', '')
522
alt += _('Timeout loading image')
528
if len(mem) > 2*1024*1024:
529
alt = attrs.get('alt', '')
532
alt += _('Image is too big')
589
536
# Caveat: GdkPixbuf is known not to be safe to load
710
657
def handle_specials(self, text):
712
se = self.textview.config.get('show_ascii_formatting_chars')
713
af = gajim.config.get('ascii_formatting')
714
if self.textview.config.get('emoticons_theme'):
716
iterator = self.textview.emot_and_basic_re.finditer(text)
718
iterator = self.textview.emot_pattern_re.finditer(text)
720
iterator = self.textview.basic_pattern_re.finditer(text)
723
for match in iterator:
724
start, end = match.span()
725
special_text = text[start:end]
727
self._insert_text(text[index:start])
728
index = end # update index
730
possible_emot_ascii_caps = special_text.upper() # emoticons keys are CAPS
731
if self.textview.config.get('emoticons_theme') and \
732
possible_emot_ascii_caps in self.textview.interface.emoticons.keys():
734
emot_ascii = possible_emot_ascii_caps
735
anchor = self.textbuf.create_child_anchor(self.iter)
737
img.set_from_file(self.textview.interface.emoticons[emot_ascii])
739
# TODO: add alt/tooltip with the special_text (a11y)
740
self.textview.add_child_at_anchor(img, anchor)
743
if special_text.startswith('/'): # it's explicit italics
744
self.startElement('i', {})
745
elif special_text.startswith('_'): # it's explicit underline
746
self.startElement('u', {})
747
if se: self._insert_text(special_text[0])
748
self.handle_specials(special_text[1:-1])
749
if se: self._insert_text(special_text[0])
750
if special_text.startswith('_'): # it's explicit underline
752
if special_text.startswith('/'): # it's explicit italics
754
if index < len(text):
755
self._insert_text(text[index:])
658
self.iter = self.conv_textview.detect_and_print_special_text(text, self._get_style_tags())
757
660
def characters(self, content):
758
661
if self.preserve:
759
662
self.text += content
801
704
tag = self._process_img(attrs)
802
705
if name in element_styles:
803
706
style += element_styles[name]
804
# so that explicit styles override implicit ones,
707
# so that explicit styles override implicit ones,
805
708
# we add the attribute last
806
709
style += ";"+attrs.get('style','')
809
712
self._begin_span(style, tag, id_)
941
843
x, y, _ = widget.window.get_pointer()
942
844
x, y = widget.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, x, y)
943
845
tags = widget.get_iter_at_location(x, y).get_tags()
944
is_over_anchor = False
946
if getattr(tag, 'is_anchor', False):
947
is_over_anchor = True
846
anchor_tags = [tag for tag in tags if getattr(tag, 'is_anchor', False)]
949
847
if self.tooltip.timeout != 0:
950
848
# Check if we should hide the line tooltip
951
if not is_over_anchor:
952
850
self.tooltip.hide_tooltip()
953
if not self._changed_cursor and is_over_anchor:
851
if not self._changed_cursor and anchor_tags:
954
852
window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
955
853
window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
956
854
self._changed_cursor = True
957
self.tooltip.timeout = gobject.timeout_add(500, self.show_tooltip, tag)
958
elif self._changed_cursor and not is_over_anchor:
855
self.tooltip.timeout = gobject.timeout_add(500, self.show_tooltip, anchor_tags[0])
856
elif self._changed_cursor and not anchor_tags:
959
857
window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
960
858
window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
961
859
self._changed_cursor = False
964
def display_html(self, html):
965
buffer = self.get_buffer()
966
eob = buffer.get_end_iter()
862
def display_html(self, html, conv_textview):
863
buffer_ = self.get_buffer()
864
eob = buffer_.get_end_iter()
967
865
## this works too if libxml2 is not available
968
866
# parser = xml.sax.make_parser(['drv_libxml2'])
969
867
# parser.setFeature(xml.sax.handler.feature_validation, True)
970
868
parser = xml.sax.make_parser()
971
parser.setContentHandler(HtmlHandler(self, eob))
869
parser.setContentHandler(HtmlHandler(conv_textview, eob))
972
870
parser.parse(StringIO(html))
974
872
# too much space after :)
975
873
#if not eob.starts_line():
976
# buffer.insert(eob, '\n')
874
# buffer_.insert(eob, '\n')
997
if gajim.config.get('emoticons_theme'):
1000
htmlview = HtmlTextView()
899
htmlview = ConversationTextview(None)
1002
901
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png')
1003
902
# use this for hr
1004
htmlview.focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
903
htmlview.tv.focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
1007
906
tooltip = tooltips.BaseTooltip()
1008
907
def on_textview_motion_notify_event(widget, event):
1009
908
'''change the cursor to a hand when we are over a mail or an url'''
1010
909
global change_cursor
1011
pointer_x, pointer_y, spam = htmlview.window.get_pointer()
1012
x, y = htmlview.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer_x,
910
pointer_x, pointer_y = htmlview.tv.window.get_pointer()[0:2]
911
x, y = htmlview.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer_x,
1014
tags = htmlview.get_iter_at_location(x, y).get_tags()
913
tags = htmlview.tv.get_iter_at_location(x, y).get_tags()
1015
914
if change_cursor:
1016
htmlview.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
915
htmlview.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
1017
916
gtk.gdk.Cursor(gtk.gdk.XTERM))
1018
917
change_cursor = None
1019
tag_table = htmlview.get_buffer().get_tag_table()
918
tag_table = htmlview.tv.get_buffer().get_tag_table()
1021
919
for tag in tags:
1023
921
if tag.is_anchor:
1024
htmlview.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
922
htmlview.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
1025
923
gtk.gdk.Cursor(gtk.gdk.HAND2))
1026
924
change_cursor = tag
1027
925
elif tag == tag_table.lookup('focus-out-line'):
1028
926
over_line = True
1031
930
#if line_tooltip.timeout != 0:
1032
931
# Check if we should hide the line tooltip
1035
934
#if over_line and not line_tooltip.win:
1036
935
# line_tooltip.timeout = gobject.timeout_add(500,
1037
936
# show_line_tooltip)
1038
# htmlview.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
937
# htmlview.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
1039
938
# gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
1040
939
# change_cursor = tag
1042
htmlview.connect('motion_notify_event', on_textview_motion_notify_event)
941
htmlview.tv.connect('motion_notify_event', on_textview_motion_notify_event)
1044
943
def handler(texttag, widget, event, iter_, kind, href):
1045
944
if event.type == gtk.gdk.BUTTON_PRESS:
1048
htmlview.html_hyperlink_handler = handler
947
htmlview.tv.html_hyperlink_handler = handler
1050
htmlview.display_html('<div><span style="color: red; text-decoration:underline">Hello</span><br/>\n'
949
htmlview.print_real_text(None, xhtml='<div><span style="color: red; text-decoration:underline">Hello</span><br/>\n'
1051
950
' <img src="http://images.slashdot.org/topics/topicsoftware.gif"/><br/>\n'
1052
951
' <span style="font-size: 500%; font-family: serif">World</span>\n'
1054
htmlview.display_html('<hr />')
1055
htmlview.display_html('''
953
htmlview.print_real_text(None, xhtml='<hr />')
954
htmlview.print_real_text(None, xhtml='''<body xmlns='http://www.w3.org/1999/xhtml'><p xmlns='http://www.w3.org/1999/xhtml'>a:b<a href='http://google.com/' xmlns='http://www.w3.org/1999/xhtml'>Google</a></p><br/></body>''')
955
htmlview.print_real_text(None, xhtml='''
956
<body xmlns='http://www.w3.org/1999/xhtml'>
1056
957
<p style='font-size:large'>
1057
<span style='font-style: italic'>O<span style='font-size:larger'>M</span>G</span>,
958
<span style='font-style: italic'>O<span style='font-size:larger'>M</span>G</span>,
1058
959
I'm <span style='color:green'>green</span>
1059
960
with <span style='font-weight: bold'>envy</span>!
1062
htmlview.display_html('<hr />')
1063
htmlview.display_html('''
964
htmlview.print_real_text(None, xhtml='<hr />')
965
htmlview.print_real_text(None, xhtml='''
966
<body xmlns='http://www.w3.org/1999/xhtml'>
967
http://test.com/ testing links autolinkifying
970
htmlview.print_real_text(None, xhtml='<hr />')
971
htmlview.print_real_text(None, xhtml='''
1064
972
<body xmlns='http://www.w3.org/1999/xhtml'>
1065
973
<p>As Emerson said in his essay <span style='font-style: italic; background-color:cyan'>Self-Reliance</span>:</p>
1066
974
<p style='margin-left: 5px; margin-right: 2%'>
1071
htmlview.display_html('<hr />')
1072
htmlview.display_html('''
979
htmlview.print_real_text(None, xhtml='<hr />')
980
htmlview.print_real_text(None, xhtml='''
1073
981
<body xmlns='http://www.w3.org/1999/xhtml'>
1074
982
<p style='text-align:center'>Hey, are you licensed to <a href='http://www.jabber.org/'>Jabber</a>?</p>
1075
983
<p style='text-align:right'><img src='http://www.jabber.org/images/psa-license.jpg'
1076
alt='A License to Jabber'
1077
width='50%' height='50%'
984
alt='A License to Jabber'
985
width='50%' height='50%'
1081
htmlview.display_html('<hr />')
1082
htmlview.display_html('''
989
htmlview.print_real_text(None, xhtml='<hr />')
990
htmlview.print_real_text(None, xhtml='''
1083
991
<body xmlns='http://www.w3.org/1999/xhtml'>
1084
992
<ul style='background-color:rgb(120,140,100)'>
1087
995
<li> Three </li>
1088
996
</ul><hr /><pre style="background-color:rgb(120,120,120)">def fac(n):
1090
998
if n==0: return acc
1091
999
return faciter(n-1, acc*n)
1092
1000
if n<0: raise ValueError('Must be non-negative')
1093
1001
return faciter(n,1)</pre>
1096
htmlview.display_html('<hr />')
1097
htmlview.display_html('''
1004
htmlview.print_real_text(None, xhtml='<hr />')
1005
htmlview.print_real_text(None, xhtml='''
1098
1006
<body xmlns='http://www.w3.org/1999/xhtml'>
1099
1007
<ol style='background-color:rgb(120,140,100)'>