50
49
if __name__ == '__main__':
51
50
from common import i18n
52
51
import common.configpaths
52
common.configpaths.gajimpaths.init_profile()
53
53
common.configpaths.gajimpaths.init(None)
54
55
from common import gajim
59
log = logging.getLogger('gajim.htmlview')
59
61
__all__ = ['HtmlTextView']
182
184
size = (num-1) // 2
183
185
weigth = (num - 1) % 2
184
186
element_styles[name] = '; font-size: %s; %s' % ( ('large', 'medium', 'small')[size],
185
('font-weight: bold', 'font-style: oblique')[weigth],
187
('font-weight: bold', 'font-style: oblique')[weigth],)
188
189
def _parse_css_color(color):
189
190
if color.startswith('rgb(') and color.endswith(')'):
215
216
self.preserve = False
216
217
self.styles = [] # a gtk.TextTag or None, for each span level
217
218
self.list_counters = [] # stack (top at head) of list
218
# counters, or None for unordered list
219
# counters, or None for unordered list
220
221
def _parse_style_color(self, tag, value):
221
222
color = _parse_css_color(value)
234
235
self.iter.forward_char()
237
def __parse_length_frac_size_allocate(self, textview, allocation,
238
frac, callback, args):
238
def __parse_length_frac_size_allocate(self, textview, allocation, frac,
239
240
callback(allocation.width*frac, *args)
241
def _parse_length(self, value, font_relative, block_relative, minl, maxl, callback, *args):
242
def _parse_length(self, value, font_relative, block_relative, minl, maxl,
243
245
Parse/calc length, converting to pixels, calls callback(length, *args)
244
246
when the length is first computed or changes
260
262
# textview width instead; a reasonable approximation..
261
263
alloc = self.textview.get_allocation()
262
264
self.__parse_length_frac_size_allocate(self.textview, alloc,
263
frac, callback, args)
265
frac, callback, args)
264
266
self.textview.connect('size-allocate',
265
self.__parse_length_frac_size_allocate,
266
frac, callback, args)
267
self.__parse_length_frac_size_allocate,
268
frac, callback, args)
268
270
callback(frac, *args)
300
302
val = sign*max(minl, min(abs(val), maxl))
301
303
callback(val, *args)
302
304
except Exception:
303
warnings.warn('Unable to parse length value "%s"' % value)
305
log.warning('Unable to parse length value "%s"' % value)
305
307
def __parse_font_size_cb(length, tag):
306
308
tag.set_property('size-points', length/display_resolution)
335
337
tag.set_property('scale', pango.SCALE_LARGE)
337
339
# font relative (5 ~ 4pt, 110 ~ 72pt)
338
self._parse_length(value, True, False, 5, 110, self.__parse_font_size_cb, tag)
340
self._parse_length(value, True, False, 5, 110,self.__parse_font_size_cb,
340
343
def _parse_style_font_style(self, tag, value):
345
348
'oblique': pango.STYLE_OBLIQUE,
348
warnings.warn('unknown font-style %s' % value)
351
log.warning('unknown font-style %s' % value)
350
353
tag.set_property('style', style)
359
362
def _parse_style_margin_left(self, tag, value):
361
self._parse_length(value, False, True, 1, 1000, self.__frac_length_tag_cb,
364
self._parse_length(value, False, True, 1, 1000,
365
self.__frac_length_tag_cb, tag, 'left-margin')
364
367
def _parse_style_margin_right(self, tag, value):
366
self._parse_length(value, False, True, 1, 1000, self.__frac_length_tag_cb,
369
self._parse_length(value, False, True, 1, 1000,
370
self.__frac_length_tag_cb, tag, 'right-margin')
369
372
def _parse_style_font_weight(self, tag, value):
370
373
# TODO: missing 'bolder' and 'lighter'
383
386
'bold': pango.WEIGHT_BOLD,
386
warnings.warn('unknown font-style %s' % value)
389
log.warning('unknown font-style %s' % value)
388
391
tag.set_property('weight', weight)
399
402
'justify': gtk.JUSTIFY_FILL,
402
warnings.warn('Invalid text-align:%s requested' % value)
405
log.warning('Invalid text-align:%s requested' % value)
404
407
tag.set_property('justification', align)
418
421
tag.set_property('strikethrough', False)
419
422
if 'blink' in values:
420
warnings.warn('text-decoration:blink not implemented')
423
log.warning('text-decoration:blink not implemented')
421
424
if 'overline' in values:
422
warnings.warn('text-decoration:overline not implemented')
425
log.warning('text-decoration:overline not implemented')
424
427
def _parse_style_white_space(self, tag, value):
425
428
if value == 'pre':
434
437
tag.set_property(propname, value)
435
438
except Exception:
436
gajim.log.warn( "Error with prop: " + propname + " for tag: " + str(tag))
439
log.warning( "Error with prop: " + propname + " for tag: " + str(tag))
439
442
def _parse_style_width(self, tag, value):
440
443
if value == 'auto':
442
445
self._parse_length(value, False, False, 1, 1000, self.__length_tag_cb,
444
447
def _parse_style_height(self, tag, value):
445
448
if value == 'auto':
447
450
self._parse_length(value, False, False, 1, 1000, self.__length_tag_cb,
451
454
# build a dictionary mapping styles to methods, for greater speed
452
455
__style_methods = dict()
453
456
for style in ('background-color', 'color', 'font-family', 'font-size',
454
'font-style', 'font-weight', 'margin-left', 'margin-right',
455
'text-align', 'text-decoration', 'white-space', 'display',
457
'font-style', 'font-weight', 'margin-left', 'margin-right',
458
'text-align', 'text-decoration', 'white-space', 'display',
458
461
method = locals()['_parse_style_%s' % style.replace('-', '_')]
460
warnings.warn('Style attribute "%s" not yet implemented' % style)
463
log.warning('Style attribute "%s" not yet implemented' % style)
462
465
__style_methods[style] = method
473
476
if href and href[0] != '#':
475
478
tag.type_ = type_ # to be used by the URL handler
476
tag.connect('event', self.textview.html_hyperlink_handler, 'url', href)
479
tag.connect('event', self.textview.hyperlink_handler, 'url')
477
480
tag.set_property('foreground', gajim.config.get('urlmsgcolor'))
478
481
tag.set_property('underline', pango.UNDERLINE_SINGLE)
479
482
tag.is_anchor = True
493
496
req.add_header('User-Agent', 'Gajim ' + gajim.version)
494
497
f = urllib2.urlopen(req)
495
498
except Exception, ex:
496
gajim.log.debug('Error loading image %s ' % attrs['src'] + str(ex))
499
log.debug('Error loading image %s ' % attrs['src'] + str(ex))
498
501
alt = attrs.get('alt', 'Broken image')
506
509
deadline = time.time() + 3
508
511
if time.time() > deadline:
509
gajim.log.debug(str('Timeout loading image %s ' % \
512
log.debug(str('Timeout loading image %s ' % \
512
515
alt = attrs.get('alt', '')
518
521
temp = f.read(100)
519
522
except socket.timeout, ex:
520
gajim.log.debug('Timeout loading image %s ' % attrs['src'] + \
523
log.debug('Timeout loading image %s ' % \
524
attrs['src'] + str(ex))
522
525
alt = attrs.get('alt', '')
594
597
self._insert_text('[IMG: %s]' % alt)
595
598
except Exception, ex:
596
gajim.log.error('Error loading image ' + str(ex))
599
log.error('Error loading image ' + str(ex))
598
601
alt = attrs.get('alt', 'Broken image')
618
621
method = self.__style_methods[attr]
620
warnings.warn('Style attribute "%s" requested '
621
'but not yet implemented' % attr)
623
log.warning('Style attribute "%s" requested '
624
'but not yet implemented' % attr)
623
626
method(self, tag, val)
624
627
self.styles.append(tag)
660
663
def handle_specials(self, text):
661
self.iter = self.conv_textview.detect_and_print_special_text(text, self._get_style_tags())
664
self.iter = self.conv_textview.detect_and_print_special_text(text,
665
self._get_style_tags())
663
667
def characters(self, content):
664
668
if self.preserve:
665
669
self.text += content
667
671
if allwhitespace_rx.match(content) is not None and self._starts_line():
670
673
self.text += content
671
674
self.starting = False
750
753
elif name in ('a', 'img', 'body', 'html'):
752
755
elif name in INLINE:
755
warnings.warn('Unhandled element "%s"' % name)
758
log.warning('Unhandled element "%s"' % name)
757
760
def endElement(self, name):
758
761
endPreserving = False
763
766
#FIXME: plenty of unused attributes (width, height,...) :)
764
767
self._jump_line()
766
self.textbuf.insert_pixbuf(self.iter, self.textview.focus_out_line_pixbuf)
769
self.textbuf.insert_pixbuf(self.iter,
770
self.textview.focus_out_line_pixbuf)
767
771
#self._insert_text(u'\u2550'*40)
768
772
self._jump_line()
769
773
except Exception, e:
770
gajim.log.debug(str('Error in hr'+e))
774
log.debug(str('Error in hr'+e))
771
775
elif name in LIST_ELEMS:
772
776
self.list_counters.pop()
773
777
elif name == 'li':
859
863
window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
860
864
window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
861
865
self._changed_cursor = True
862
self.tooltip.timeout = gobject.timeout_add(500, self.show_tooltip, anchor_tags[0])
866
self.tooltip.timeout = gobject.timeout_add(500, self.show_tooltip,
863
868
elif self._changed_cursor and not anchor_tags:
864
869
window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
865
870
window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
886
891
self.emit_stop_by_name('copy-clipboard')
888
893
def on_html_text_view_realized(self, unused_data):
889
self.get_buffer().remove_selection_clipboard(self.get_clipboard(gtk.gdk.SELECTION_PRIMARY))
894
self.get_buffer().remove_selection_clipboard(self.get_clipboard(
895
gtk.gdk.SELECTION_PRIMARY))
891
897
def on_html_text_view_unrealized(self, unused_data):
892
self.get_buffer().add_selection_clipboard(self.get_clipboard(gtk.gdk.SELECTION_PRIMARY))
898
self.get_buffer().add_selection_clipboard(self.get_clipboard(
899
gtk.gdk.SELECTION_PRIMARY))
894
901
def on_text_buffer_mark_set(self, location, mark, unused_data):
895
902
bounds = self.get_buffer().get_selection_bounds()
956
953
global change_cursor
957
954
pointer_x, pointer_y = htmlview.tv.window.get_pointer()[0:2]
958
x, y = htmlview.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer_x,
955
x, y = htmlview.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
956
pointer_x, pointer_y)
960
957
tags = htmlview.tv.get_iter_at_location(x, y).get_tags()
961
958
if change_cursor:
962
959
htmlview.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
963
gtk.gdk.Cursor(gtk.gdk.XTERM))
960
gtk.gdk.Cursor(gtk.gdk.XTERM))
964
961
change_cursor = None
965
962
tag_table = htmlview.tv.get_buffer().get_tag_table()
968
965
if tag.is_anchor:
969
966
htmlview.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
970
gtk.gdk.Cursor(gtk.gdk.HAND2))
967
gtk.gdk.Cursor(gtk.gdk.HAND2))
971
968
change_cursor = tag
972
969
elif tag == tag_table.lookup('focus-out-line'):
988
985
htmlview.tv.connect('motion_notify_event', on_textview_motion_notify_event)
990
def handler(texttag, widget, event, iter_, kind, href):
987
def handler(texttag, widget, event, iter_, kind):
991
988
if event.type == gtk.gdk.BUTTON_PRESS:
994
htmlview.tv.html_hyperlink_handler = handler
996
htmlview.print_real_text(None, xhtml='<div><span style="color: red; text-decoration:underline">Hello</span><br/>\n'
997
' <img src="http://images.slashdot.org/topics/topicsoftware.gif"/><br/>\n'
998
' <span style="font-size: 500%; font-family: serif">World</span>\n'
991
htmlview.tv.hyperlink_handler = htmlview.hyperlink_handler
993
htmlview.print_real_text(None, xhtml='<div>'
994
'<span style="color: red; text-decoration:underline">Hello</span><br/>\n'
995
' <img src="http://images.slashdot.org/topics/topicsoftware.gif"/><br/>\n'
996
'<span style="font-size: 500%; font-family: serif">World</span>\n'
1000
998
htmlview.print_real_text(None, xhtml='<hr />')
1001
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>''')
999
htmlview.print_real_text(None, xhtml='''
1000
<body xmlns='http://www.w3.org/1999/xhtml'>
1001
<p xmlns='http://www.w3.org/1999/xhtml'>a:b
1002
<a href='http://google.com/' xmlns='http://www.w3.org/1999/xhtml'>Google
1002
1006
htmlview.print_real_text(None, xhtml='''
1003
1007
<body xmlns='http://www.w3.org/1999/xhtml'>
1004
1008
<p style='font-size:large'>
1005
<span style='font-style: italic'>O<span style='font-size:larger'>M</span>G</span>,
1009
<span style='font-style: italic'>O
1010
<span style='font-size:larger'>M</span>G</span>,
1006
1011
I'm <span style='color:green'>green</span>
1007
1012
with <span style='font-weight: bold'>envy</span>!
1017
1022
htmlview.print_real_text(None, xhtml='<hr />')
1018
1023
htmlview.print_real_text(None, xhtml='''
1019
1024
<body xmlns='http://www.w3.org/1999/xhtml'>
1020
<p>As Emerson said in his essay <span style='font-style: italic; background-color:cyan'>Self-Reliance</span>:</p>
1025
<p>As Emerson said in his essay <span style='
1026
font-style: italic; background-color:cyan'>Self-Reliance</span>:</p>
1021
1027
<p style='margin-left: 5px; margin-right: 2%'>
1022
1028
"A foolish consistency is the hobgoblin of little minds."
1026
1032
htmlview.print_real_text(None, xhtml='<hr />')
1027
1033
htmlview.print_real_text(None, xhtml='''
1028
1034
<body xmlns='http://www.w3.org/1999/xhtml'>
1029
<p style='text-align:center'>Hey, are you licensed to <a href='http://www.jabber.org/'>Jabber</a>?</p>
1030
<p style='text-align:right'><img src='http://www.jabber.org/images/psa-license.jpg'
1031
alt='A License to Jabber'
1032
width='50%' height='50%'
1035
<p style='text-align:center'>
1036
Hey, are you licensed to <a href='http://www.jabber.org/'>Jabber</a>?
1038
<p style='text-align:right'>
1039
<img src='http://www.xmpp.org/images/psa-license.jpg'
1040
alt='A License to Jabber' width='50%' height='50%'/>
1036
1044
htmlview.print_real_text(None, xhtml='<hr />')
1062
1070
<li> Three </li></ol>
1073
htmlview.print_real_text(None, xhtml='<hr />')
1074
htmlview.print_real_text(None, xhtml='''
1075
<body xmlns='http://www.w3.org/1999/xhtml'>
1078
<a href='xmpp:example@example.org'>xmpp link</a>
1080
<div xmlns='http://www.w3.org/1999/xhtml'>
1081
<cite style='margin: 7px;' title='xmpp:examples@example.org'>
1083
<strong>examples@example.org wrote:</strong>
1085
<p>this cite - bla bla bla, smile- :-) ...</p>
1065
1095
htmlview.tv.show()
1066
1096
sw = gtk.ScrolledWindow()
1067
1097
sw.set_property('hscrollbar-policy', gtk.POLICY_AUTOMATIC)