Philipp Hörist pushed to branch master at gajim / gajim

Commits:

2 changed files:

Changes:

  • gajim/conversation_textview.py
    ... ... @@ -172,7 +172,7 @@ class ConversationTextview(GObject.GObject):
    172 172
     
    
    173 173
             # no need to inherit TextView, use it as atrribute is safer
    
    174 174
             self.tv = HtmlTextView(account)
    
    175
    -        self.tv.connect_tooltip(self.query_tooltip)
    
    175
    +        self.tv.connect('query-tooltip', self._query_tooltip)
    
    176 176
     
    
    177 177
             # set properties
    
    178 178
             self.tv.set_border_width(1)
    
    ... ... @@ -199,7 +199,7 @@ class ConversationTextview(GObject.GObject):
    199 199
             self.handlers[id_] = self.tv
    
    200 200
     
    
    201 201
             self.account = account
    
    202
    -        self.cursor_changed = False
    
    202
    +        self._cursor_changed = False
    
    203 203
             self.last_time_printout = 0
    
    204 204
             self.encryption_enabled = False
    
    205 205
     
    
    ... ... @@ -304,14 +304,12 @@ class ConversationTextview(GObject.GObject):
    304 304
     
    
    305 305
             self.just_cleared = False
    
    306 306
     
    
    307
    -    def query_tooltip(self, widget, x_pos, y_pos, keyboard_mode, tooltip):
    
    307
    +    def _query_tooltip(self, widget, x_pos, y_pos, keyboard_mode, tooltip):
    
    308 308
             window = widget.get_window(Gtk.TextWindowType.TEXT)
    
    309 309
             x_pos, y_pos = self.tv.window_to_buffer_coords(
    
    310 310
                 Gtk.TextWindowType.TEXT, x_pos, y_pos)
    
    311
    -        if Gtk.MINOR_VERSION > 18:
    
    312
    -            iter_ = self.tv.get_iter_at_position(x_pos, y_pos)[1]
    
    313
    -        else:
    
    314
    -            iter_ = self.tv.get_iter_at_position(x_pos, y_pos)[0]
    
    311
    +
    
    312
    +        iter_ = self.tv.get_iter_at_position(x_pos, y_pos)[1]
    
    315 313
             for tag in iter_.get_tags():
    
    316 314
                 tag_name = tag.get_property('name')
    
    317 315
                 if tag_name == 'focus-out-line':
    
    ... ... @@ -327,11 +325,11 @@ class ConversationTextview(GObject.GObject):
    327 325
                             text = text[:47] + '…'
    
    328 326
                         tooltip.set_text(text)
    
    329 327
                         window.set_cursor(get_cursor('HAND2'))
    
    330
    -                    self.cursor_changed = True
    
    328
    +                    self._cursor_changed = True
    
    331 329
                         return True
    
    332 330
                 if tag_name in ('url', 'mail', 'xmpp', 'sth_at_sth'):
    
    333 331
                     window.set_cursor(get_cursor('HAND2'))
    
    334
    -                self.cursor_changed = True
    
    332
    +                self._cursor_changed = True
    
    335 333
                     return False
    
    336 334
                 try:
    
    337 335
                     text = self.corrected_text_list[tag_name]
    
    ... ... @@ -339,9 +337,9 @@ class ConversationTextview(GObject.GObject):
    339 337
                     return True
    
    340 338
                 except KeyError:
    
    341 339
                     pass
    
    342
    -        if self.cursor_changed:
    
    340
    +        if self._cursor_changed:
    
    343 341
                 window.set_cursor(get_cursor('XTERM'))
    
    344
    -            self.cursor_changed = False
    
    342
    +            self._cursor_changed = False
    
    345 343
             return False
    
    346 344
     
    
    347 345
         def del_handlers(self):
    
    ... ... @@ -358,11 +356,7 @@ class ConversationTextview(GObject.GObject):
    358 356
                 app.css_config.get_value('.gajim-status-message', StyleAttr.COLOR))
    
    359 357
             self.tagMarked.set_property('foreground',
    
    360 358
                 app.css_config.get_value('.gajim-highlight-message', StyleAttr.COLOR))
    
    361
    -        color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
    
    362
    -        self.tv.tagURL.set_property('foreground', color)
    
    363
    -        self.tv.tagMail.set_property('foreground', color)
    
    364
    -        self.tv.tagXMPP.set_property('foreground', color)
    
    365
    -        self.tv.tagSthAtSth.set_property('foreground', color)
    
    359
    +        self.tv.update_tags()
    
    366 360
     
    
    367 361
         def scroll_to_end(self, force=False):
    
    368 362
             if self.autoscroll or force:
    

  • gajim/htmltextview.py
    ... ... @@ -56,34 +56,32 @@ from gajim.gui_menu_builder import get_conv_context_menu
    56 56
     
    
    57 57
     log = logging.getLogger('gajim.htmlview')
    
    58 58
     
    
    59
    -__all__ = ['HtmlTextView']
    
    60
    -
    
    61 59
     whitespace_rx = re.compile('\\s+')
    
    62 60
     allwhitespace_rx = re.compile('^\\s*$')
    
    63 61
     
    
    64 62
     # embryo of CSS classes
    
    65 63
     classes = {
    
    66
    -        #'system-message':';display: none',
    
    67
    -        'problematic': ';color: red',
    
    64
    +    #'system-message':';display: none',
    
    65
    +    'problematic': ';color: red',
    
    68 66
     }
    
    69 67
     
    
    70 68
     # styles for elements
    
    71 69
     _element_styles = {
    
    72
    -                'u'             : ';text-decoration: underline',
    
    73
    -                'em'            : ';font-style: oblique',
    
    74
    -                'cite'          : '; background-color:rgb(170,190,250);'
    
    75
    -                                  'font-style: oblique',
    
    76
    -                'li'            : '; margin-left: 1em; margin-right: 10%',
    
    77
    -                'strong'        : ';font-weight: bold',
    
    78
    -                'pre'           : '; background-color:rgb(190,190,190);'
    
    79
    -                                  'font-family: monospace; white-space: pre;'
    
    80
    -                                  'margin-left: 1em; margin-right: 10%',
    
    81
    -                'kbd'           : ';background-color:rgb(210,210,210);'
    
    82
    -                                  'font-family: monospace',
    
    83
    -                'blockquote'    : '; background-color:rgb(170,190,250);'
    
    84
    -                                  'margin-left: 2em; margin-right: 10%',
    
    85
    -                'dt'            : ';font-weight: bold; font-style: oblique',
    
    86
    -                'dd'            : ';margin-left: 2em; font-style: oblique'
    
    70
    +    'u'             : ';text-decoration: underline',
    
    71
    +    'em'            : ';font-style: oblique',
    
    72
    +    'cite'          : '; background-color:rgb(170,190,250);'
    
    73
    +                      'font-style: oblique',
    
    74
    +    'li'            : '; margin-left: 1em; margin-right: 10%',
    
    75
    +    'strong'        : ';font-weight: bold',
    
    76
    +    'pre'           : '; background-color:rgb(190,190,190);'
    
    77
    +                      'font-family: monospace; white-space: pre;'
    
    78
    +                      'margin-left: 1em; margin-right: 10%',
    
    79
    +    'kbd'           : ';background-color:rgb(210,210,210);'
    
    80
    +                      'font-family: monospace',
    
    81
    +    'blockquote'    : '; background-color:rgb(170,190,250);'
    
    82
    +                      'margin-left: 2em; margin-right: 10%',
    
    83
    +    'dt'            : ';font-weight: bold; font-style: oblique',
    
    84
    +    'dd'            : ';margin-left: 2em; font-style: oblique'
    
    87 85
     }
    
    88 86
     # no difference for the moment
    
    89 87
     _element_styles['dfn'] = _element_styles['em']
    
    ... ... @@ -93,6 +91,12 @@ _element_styles['tt'] = _element_styles['kbd']
    93 91
     _element_styles['i'] = _element_styles['em']
    
    94 92
     _element_styles['b'] = _element_styles['strong']
    
    95 93
     
    
    94
    +_supported_style_attrs = [
    
    95
    +    'background-color', 'color', 'font-family', 'font-size', 'font-style',
    
    96
    +    'font-weight', 'margin-left', 'margin-right', 'text-align',
    
    97
    +    'text-decoration', 'white-space', 'display', 'width', 'height'
    
    98
    +]
    
    99
    +
    
    96 100
     # ==========
    
    97 101
     #   XEP-0071
    
    98 102
     # ==========
    
    ... ... @@ -182,8 +186,9 @@ for _name in BLOCK_HEAD:
    182 186
         _num = int(_name[1])
    
    183 187
         _header_size = (_num - 1) // 2
    
    184 188
         _weight = (_num - 1) % 2
    
    185
    -    _element_styles[_name] = '; font-size: %s; %s' % (('large', 'medium', 'small')[_header_size],
    
    186
    -        ('font-weight: bold', 'font-style: oblique')[_weight],)
    
    189
    +    _element_styles[_name] = '; font-size: %s; %s' % (
    
    190
    +        ('large', 'medium', 'small')[_header_size],
    
    191
    +        ('font-weight: bold', 'font-style: oblique')[_weight])
    
    187 192
     
    
    188 193
     def _parse_css_color(color):
    
    189 194
         if color.startswith('rgb(') and color.endswith(')'):
    
    ... ... @@ -216,26 +221,35 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    216 221
             self.list_counters = [] # stack (top at head) of list
    
    217 222
                                     # counters, or None for unordered list
    
    218 223
     
    
    224
    +        # build a dictionary mapping styles to methods
    
    225
    +        self.__style_methods = {}
    
    226
    +        for style in _supported_style_attrs:
    
    227
    +            method_names = '_parse_style_%s' % style.replace('-', '_')
    
    228
    +            self.__style_methods[style] = method_names
    
    229
    +
    
    219 230
         def _get_points_from_pixels(self, pixels):
    
    220 231
             resolution = self.textview.get_screen().get_resolution()
    
    221 232
             # points = pixels * 72 / resolution
    
    222 233
             return pixels * 72 / resolution
    
    223 234
     
    
    224
    -    def _parse_style_color(self, tag, value):
    
    235
    +    @staticmethod
    
    236
    +    def _parse_style_color(tag, value):
    
    225 237
             color = _parse_css_color(value)
    
    226 238
             tag.set_property('foreground-gdk', color)
    
    227 239
     
    
    228
    -    def _parse_style_background_color(self, tag, value):
    
    240
    +    @staticmethod
    
    241
    +    def _parse_style_background_color(tag, value):
    
    229 242
             color = _parse_css_color(value)
    
    230 243
             tag.set_property('background-gdk', color)
    
    231 244
             tag.set_property('paragraph-background-gdk', color)
    
    232 245
     
    
    233
    -    def __parse_length_frac_size_allocate(self, textview, allocation, frac,
    
    234
    -        callback, args):
    
    246
    +    @staticmethod
    
    247
    +    def __parse_length_frac_size_allocate(_textview, allocation, frac,
    
    248
    +                                          callback, args):
    
    235 249
             callback(allocation.width*frac, *args)
    
    236 250
     
    
    237 251
         def _parse_length(self, value, font_relative, block_relative, minl, maxl,
    
    238
    -        callback, *args):
    
    252
    +                      callback, *args):
    
    239 253
             """
    
    240 254
             Parse/calc length, converting to pixels, calls callback(length, *args)
    
    241 255
             when the length is first computed or changes
    
    ... ... @@ -259,11 +273,11 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    259 273
                     # This is difficult/impossible to implement, so we use
    
    260 274
                     # textview width instead; a reasonable approximation..
    
    261 275
                     alloc = self.textview.get_allocation()
    
    262
    -                self.__parse_length_frac_size_allocate(self.textview, alloc,
    
    263
    -                    frac, callback, args)
    
    276
    +                self.__parse_length_frac_size_allocate(
    
    277
    +                    self.textview, alloc, frac, callback, args)
    
    264 278
                     self.textview.connect('size-allocate',
    
    265
    -                    self.__parse_length_frac_size_allocate,
    
    266
    -                    frac, callback, args)
    
    279
    +                                      self.__parse_length_frac_size_allocate,
    
    280
    +                                      frac, callback, args)
    
    267 281
                 else:
    
    268 282
                     callback(frac, *args)
    
    269 283
                 return
    
    ... ... @@ -324,7 +338,8 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    324 338
             elif type_ == 'px':
    
    325 339
                 tag.set_property('size-points', self._get_points_from_pixels(size))
    
    326 340
     
    
    327
    -    def _parse_style_display(self, tag, value):
    
    341
    +    @staticmethod
    
    342
    +    def _parse_style_display(tag, value):
    
    328 343
             if value == 'none':
    
    329 344
                 tag.set_property('invisible', 'true')
    
    330 345
             # FIXME: display: block, inline
    
    ... ... @@ -355,13 +370,14 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    355 370
             self._parse_length(
    
    356 371
                 value, True, False, 5, 110, self.__parse_font_size_cb, tag)
    
    357 372
     
    
    358
    -    def _parse_style_font_style(self, tag, value):
    
    373
    +    @staticmethod
    
    374
    +    def _parse_style_font_style(tag, value):
    
    359 375
             try:
    
    360 376
                 style = {
    
    361
    -                    'normal': Pango.Style.NORMAL,
    
    362
    -                    'italic': Pango.Style.ITALIC,
    
    363
    -                    'oblique': Pango.Style.OBLIQUE,
    
    364
    -                    }[value]
    
    377
    +                'normal': Pango.Style.NORMAL,
    
    378
    +                'italic': Pango.Style.ITALIC,
    
    379
    +                'oblique': Pango.Style.OBLIQUE,
    
    380
    +            }[value]
    
    365 381
             except KeyError:
    
    366 382
                 log.warning('unknown font-style %s', value)
    
    367 383
             else:
    
    ... ... @@ -376,51 +392,55 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    376 392
         def _parse_style_margin_left(self, tag, value):
    
    377 393
             # block relative
    
    378 394
             self._parse_length(value, False, True, 1, 1000,
    
    379
    -            self.__frac_length_tag_cb, tag, 'left-margin')
    
    395
    +                           self.__frac_length_tag_cb, tag, 'left-margin')
    
    380 396
     
    
    381 397
         def _parse_style_margin_right(self, tag, value):
    
    382 398
             # block relative
    
    383 399
             self._parse_length(value, False, True, 1, 1000,
    
    384
    -            self.__frac_length_tag_cb, tag, 'right-margin')
    
    400
    +                           self.__frac_length_tag_cb, tag, 'right-margin')
    
    385 401
     
    
    386
    -    def _parse_style_font_weight(self, tag, value):
    
    402
    +    @staticmethod
    
    403
    +    def _parse_style_font_weight(tag, value):
    
    387 404
             # TODO: missing 'bolder' and 'lighter'
    
    388 405
             try:
    
    389 406
                 weight = {
    
    390
    -                    '100': Pango.Weight.ULTRALIGHT,
    
    391
    -                    '200': Pango.Weight.ULTRALIGHT,
    
    392
    -                    '300': Pango.Weight.LIGHT,
    
    393
    -                    '400': Pango.Weight.NORMAL,
    
    394
    -                    '500': Pango.Weight.NORMAL,
    
    395
    -                    '600': Pango.Weight.BOLD,
    
    396
    -                    '700': Pango.Weight.BOLD,
    
    397
    -                    '800': Pango.Weight.ULTRABOLD,
    
    398
    -                    '900': Pango.Weight.HEAVY,
    
    399
    -                    'normal': Pango.Weight.NORMAL,
    
    400
    -                    'bold': Pango.Weight.BOLD,
    
    401
    -                    }[value]
    
    407
    +                '100': Pango.Weight.ULTRALIGHT,
    
    408
    +                '200': Pango.Weight.ULTRALIGHT,
    
    409
    +                '300': Pango.Weight.LIGHT,
    
    410
    +                '400': Pango.Weight.NORMAL,
    
    411
    +                '500': Pango.Weight.NORMAL,
    
    412
    +                '600': Pango.Weight.BOLD,
    
    413
    +                '700': Pango.Weight.BOLD,
    
    414
    +                '800': Pango.Weight.ULTRABOLD,
    
    415
    +                '900': Pango.Weight.HEAVY,
    
    416
    +                'normal': Pango.Weight.NORMAL,
    
    417
    +                'bold': Pango.Weight.BOLD,
    
    418
    +            }[value]
    
    402 419
             except KeyError:
    
    403 420
                 log.warning('unknown font-style %s', value)
    
    404 421
             else:
    
    405 422
                 tag.set_property('weight', weight)
    
    406 423
     
    
    407
    -    def _parse_style_font_family(self, tag, value):
    
    424
    +    @staticmethod
    
    425
    +    def _parse_style_font_family(tag, value):
    
    408 426
             tag.set_property('family', value)
    
    409 427
     
    
    410
    -    def _parse_style_text_align(self, tag, value):
    
    428
    +    @staticmethod
    
    429
    +    def _parse_style_text_align(tag, value):
    
    411 430
             try:
    
    412 431
                 align = {
    
    413
    -                    'left': Gtk.Justification.LEFT,
    
    414
    -                    'right': Gtk.Justification.RIGHT,
    
    415
    -                    'center': Gtk.Justification.CENTER,
    
    416
    -                    'justify': Gtk.Justification.FILL,
    
    417
    -                    }[value]
    
    432
    +                'left': Gtk.Justification.LEFT,
    
    433
    +                'right': Gtk.Justification.RIGHT,
    
    434
    +                'center': Gtk.Justification.CENTER,
    
    435
    +                'justify': Gtk.Justification.FILL,
    
    436
    +            }[value]
    
    418 437
             except KeyError:
    
    419 438
                 log.warning('Invalid text-align: %s requested', value)
    
    420 439
             else:
    
    421 440
                 tag.set_property('justification', align)
    
    422 441
     
    
    423
    -    def _parse_style_text_decoration(self, tag, value):
    
    442
    +    @staticmethod
    
    443
    +    def _parse_style_text_decoration(tag, value):
    
    424 444
             values = value.split(' ')
    
    425 445
             if 'none' in values:
    
    426 446
                 tag.set_property('underline', Pango.Underline.NONE)
    
    ... ... @@ -438,7 +458,8 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    438 458
             if 'overline' in values:
    
    439 459
                 log.warning('text-decoration:overline not implemented')
    
    440 460
     
    
    441
    -    def _parse_style_white_space(self, tag, value):
    
    461
    +    @staticmethod
    
    462
    +    def _parse_style_white_space(tag, value):
    
    442 463
             if value == 'pre':
    
    443 464
                 tag.set_property('wrap_mode', Gtk.WrapMode.NONE)
    
    444 465
             elif value == 'normal':
    
    ... ... @@ -446,7 +467,8 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    446 467
             elif value == 'nowrap':
    
    447 468
                 tag.set_property('wrap_mode', Gtk.WrapMode.NONE)
    
    448 469
     
    
    449
    -    def __length_tag_cb(self, value, tag, propname):
    
    470
    +    @staticmethod
    
    471
    +    def __length_tag_cb(value, tag, propname):
    
    450 472
             try:
    
    451 473
                 tag.set_property(propname, value)
    
    452 474
             except Exception:
    
    ... ... @@ -455,29 +477,13 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    455 477
         def _parse_style_width(self, tag, value):
    
    456 478
             if value == 'auto':
    
    457 479
                 return
    
    458
    -        self._parse_length(value, False, False, 1, 1000, self.__length_tag_cb,
    
    459
    -            tag, "width")
    
    480
    +        self._parse_length(value, False, False, 1, 1000,
    
    481
    +                           self.__length_tag_cb, tag, "width")
    
    460 482
         def _parse_style_height(self, tag, value):
    
    461 483
             if value == 'auto':
    
    462 484
                 return
    
    463
    -        self._parse_length(value, False, False, 1, 1000, self.__length_tag_cb,
    
    464
    -            tag, "height")
    
    465
    -
    
    466
    -
    
    467
    -    # build a dictionary mapping styles to methods, for greater speed
    
    468
    -    __style_methods = dict()
    
    469
    -    for style in ('background-color', 'color', 'font-family', 'font-size',
    
    470
    -                  'font-style', 'font-weight', 'margin-left', 'margin-right',
    
    471
    -                  'text-align', 'text-decoration', 'white-space', 'display',
    
    472
    -                  'width', 'height'):
    
    473
    -        try:
    
    474
    -            method = locals()['_parse_style_%s' % style.replace('-', '_')]
    
    475
    -        except KeyError:
    
    476
    -            log.warning('Style attribute "%s" not yet implemented', style)
    
    477
    -        else:
    
    478
    -            __style_methods[style] = method
    
    479
    -    del style  # pylint: disable=undefined-loop-variable
    
    480
    -    # --
    
    485
    +        self._parse_length(value, False, False, 1, 1000,
    
    486
    +                           self.__length_tag_cb, tag, "height")
    
    481 487
     
    
    482 488
         def _get_style_tags(self):
    
    483 489
             return [tag for tag in self.styles if tag is not None]
    
    ... ... @@ -523,8 +529,10 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    523 529
                 else:
    
    524 530
                     if self.conv_textview:
    
    525 531
                         img_mark = self.textbuf.create_mark(None, self.iter, True)
    
    526
    -                    app.thread_interface(helpers.download_image, [
    
    527
    -                        self.conv_textview.account, attrs], self._update_img,
    
    532
    +                    app.thread_interface(
    
    533
    +                        helpers.download_image,
    
    534
    +                        [self.conv_textview.account, attrs],
    
    535
    +                        self._update_img,
    
    528 536
                             [attrs, img_mark, self._get_style_tags()])
    
    529 537
                         alt = attrs.get('alt', '')
    
    530 538
                         if alt:
    
    ... ... @@ -542,34 +550,36 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    542 550
                     def width_cb(length):
    
    543 551
                         dims[0] = length
    
    544 552
                     # process width and height attributes
    
    545
    -                w = attrs.get('width')
    
    546
    -                h = attrs.get('height')
    
    553
    +                width = attrs.get('width')
    
    554
    +                height = attrs.get('height')
    
    547 555
                     # override with width and height styles
    
    548 556
                     for attr, val in style_iter(attrs.get('style', '')):
    
    549 557
                         if attr == 'width':
    
    550
    -                        w = val
    
    558
    +                        width = val
    
    551 559
                         elif attr == 'height':
    
    552
    -                        h = val
    
    553
    -                if w:
    
    554
    -                    self._parse_length(w, False, False, 1, 1000, width_cb)
    
    555
    -                if h:
    
    556
    -                    self._parse_length(h, False, False, 1, 1000, height_cb)
    
    557
    -                def set_size(pixbuf, w, h, dims):
    
    560
    +                        height = val
    
    561
    +                if width:
    
    562
    +                    self._parse_length(width, False, False, 1, 1000, width_cb)
    
    563
    +                if height:
    
    564
    +                    self._parse_length(height, False, False, 1, 1000, height_cb)
    
    565
    +
    
    566
    +                def set_size(_pixbuf, width_, height_, dims):
    
    558 567
                         """
    
    559 568
                         FIXME: Floats should be relative to the whole textview, and
    
    560 569
                         resize with it. This needs new pifbufs for every resize,
    
    561 570
                         GdkPixbuf.Pixbuf.scale_simple or similar.
    
    562 571
                         """
    
    563 572
                         if isinstance(dims[0], float):
    
    564
    -                        dims[0] = int(dims[0]*w)
    
    573
    +                        dims[0] = int(dims[0] * width_)
    
    565 574
                         elif not dims[0]:
    
    566
    -                        dims[0] = w
    
    575
    +                        dims[0] = width_
    
    567 576
                         if isinstance(dims[1], float):
    
    568
    -                        dims[1] = int(dims[1]*h)
    
    577
    +                        dims[1] = int(dims[1] * height_)
    
    569 578
                         if not dims[1]:
    
    570
    -                        dims[1] = h
    
    579
    +                        dims[1] = height_
    
    571 580
                         loader.set_size(*dims)
    
    572
    -                if w or h:
    
    581
    +
    
    582
    +                if width or height:
    
    573 583
                         loader.connect('size-prepared', set_size, dims)
    
    574 584
                     loader.write(mem)
    
    575 585
                     loader.close()
    
    ... ... @@ -621,12 +631,10 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    621 631
                 attr = attr.lower()
    
    622 632
                 val = val
    
    623 633
                 try:
    
    624
    -                method = self.__style_methods[attr]
    
    634
    +                getattr(self, self.__style_methods[attr])(tag, val)
    
    625 635
                 except KeyError:
    
    626 636
                     log.warning('Style attribute "%s" requested '
    
    627 637
                                 'but not yet implemented', attr)
    
    628
    -            else:
    
    629
    -                method(self, tag, val)
    
    630 638
             self.styles.append(tag)
    
    631 639
     
    
    632 640
         def _end_span(self):
    
    ... ... @@ -660,7 +668,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    660 668
             else:
    
    661 669
                 self._insert_text(text.strip('\n'))
    
    662 670
     
    
    663
    -    def _anchor_event(self, tag, textview, event, iter_, href, type_):
    
    671
    +    def _anchor_event(self, _tag, _textview, event, _iter, href, type_):
    
    664 672
             if event.type == Gdk.EventType.BUTTON_PRESS:
    
    665 673
                 self.textview.emit('url-clicked', href, type_)
    
    666 674
                 return True
    
    ... ... @@ -668,8 +676,8 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    668 676
     
    
    669 677
         def handle_specials(self, text):
    
    670 678
             if self.conv_textview:
    
    671
    -            self.iter = self.conv_textview.detect_and_print_special_text(text,
    
    672
    -                self._get_style_tags(), iter_=self.iter)
    
    679
    +            self.iter = self.conv_textview.detect_and_print_special_text(
    
    680
    +                text, self._get_style_tags(), iter_=self.iter)
    
    673 681
             else:
    
    674 682
                 self._insert_text(text)
    
    675 683
     
    
    ... ... @@ -682,7 +690,6 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    682 690
             self.text += content
    
    683 691
             self.starting = False
    
    684 692
     
    
    685
    -
    
    686 693
         def startElement(self, name, attrs):
    
    687 694
             self._flush_text()
    
    688 695
             klass = [i for i in attrs.get('class', ' ').split(' ') if i]
    
    ... ... @@ -767,10 +774,10 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    767 774
                 log.warning('Unhandled element "%s"', name)
    
    768 775
     
    
    769 776
         def endElement(self, name):
    
    770
    -        endPreserving = False
    
    771
    -        newLine = False
    
    777
    +        end_preserving = False
    
    778
    +        newline = False
    
    772 779
             if name == 'br':
    
    773
    -            newLine = True
    
    780
    +            newline = True
    
    774 781
             elif name == 'hr':
    
    775 782
                 #FIXME: plenty of unused attributes (width, height,...) :)
    
    776 783
                 self._jump_line()
    
    ... ... @@ -779,7 +786,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    779 786
             elif name in LIST_ELEMS:
    
    780 787
                 self.list_counters.pop()
    
    781 788
             elif name == 'li':
    
    782
    -            newLine = True
    
    789
    +            newline = True
    
    783 790
             elif name == 'img':
    
    784 791
                 pass
    
    785 792
             elif name in ('body', 'html'):
    
    ... ... @@ -792,73 +799,62 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
    792 799
                 pass
    
    793 800
             elif name in BLOCK:
    
    794 801
                 if name == 'pre':
    
    795
    -                endPreserving = True
    
    802
    +                end_preserving = True
    
    796 803
                 elif name in BLOCK_STRUCT:
    
    797
    -                newLine = True
    
    804
    +                newline = True
    
    798 805
             else:
    
    799 806
                 log.warning("Unhandled element '%s'", name)
    
    800 807
             self._flush_text()
    
    801
    -        if endPreserving:
    
    808
    +        if end_preserving:
    
    802 809
                 self.preserve = False
    
    803
    -        if newLine:
    
    810
    +        if newline:
    
    804 811
                 self._jump_line()
    
    805 812
             self._end_span()
    
    806 813
     
    
    807 814
     
    
    808 815
     class HtmlTextView(Gtk.TextView):
    
    809 816
     
    
    810
    -    def __init__(self, account=None):
    
    817
    +    _tags = ['url', 'mail', 'xmpp', 'sth_at_sth']
    
    818
    +
    
    819
    +    def __init__(self, account, standalone=False):
    
    811 820
             Gtk.TextView.__init__(self)
    
    812 821
             self.set_wrap_mode(Gtk.WrapMode.CHAR)
    
    813 822
             self.set_editable(False)
    
    814
    -        self._changed_cursor = False
    
    815 823
             self.set_has_tooltip(True)
    
    816
    -        self.connect('realize', self.on_html_text_view_realized)
    
    817
    -        self.connect('unrealize', self.on_html_text_view_unrealized)
    
    818
    -        self.connect('copy-clipboard', self.on_html_text_view_copy_clipboard)
    
    819
    -        self.id_ = self.connect('button-release-event',
    
    820
    -                                self.on_left_mouse_button_release)
    
    824
    +        self.connect('copy-clipboard', self._on_copy_clipboard)
    
    821 825
             self.get_buffer().eol_tag = self.get_buffer().create_tag('eol')
    
    822
    -        self.config = app.config
    
    823
    -        self.interface = app.interface
    
    826
    +
    
    824 827
             self.account = account
    
    825 828
             self.plugin_modified = False
    
    829
    +        self._cursor_changed = False
    
    826 830
     
    
    827
    -    def connect_tooltip(self, func=None):
    
    828
    -        self.connect('query-tooltip', func or self.__query_tooltip)
    
    831
    +        if standalone:
    
    832
    +            self.connect('query-tooltip', self._query_tooltip)
    
    833
    +            self.create_tags()
    
    829 834
     
    
    830 835
         def create_tags(self):
    
    831
    -        buffer_ = self.get_buffer()
    
    836
    +        color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
    
    837
    +
    
    838
    +        attrs = {'foreground': color,
    
    839
    +                 'underline': Pango.Underline.SINGLE}
    
    840
    +
    
    841
    +        for tag in self._tags:
    
    842
    +            tag_ = self.get_buffer().create_tag(tag, **attrs)
    
    843
    +            tag_.connect('event', self.hyperlink_handler, tag)
    
    832 844
     
    
    833
    -        self.tagURL = buffer_.create_tag('url')
    
    845
    +    def update_tags(self):
    
    846
    +        tag_table = self.get_buffer().get_tag_table()
    
    834 847
             color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
    
    835
    -        self.tagURL.set_property('foreground', color)
    
    836
    -        self.tagURL.set_property('underline', Pango.Underline.SINGLE)
    
    837
    -        self.tagURL.connect('event', self.hyperlink_handler, 'url')
    
    838
    -
    
    839
    -        self.tagMail = buffer_.create_tag('mail')
    
    840
    -        self.tagMail.set_property('foreground', color)
    
    841
    -        self.tagMail.set_property('underline', Pango.Underline.SINGLE)
    
    842
    -        self.tagMail.connect('event', self.hyperlink_handler, 'mail')
    
    843
    -
    
    844
    -        self.tagXMPP = buffer_.create_tag('xmpp')
    
    845
    -        self.tagXMPP.set_property('foreground', color)
    
    846
    -        self.tagXMPP.set_property('underline', Pango.Underline.SINGLE)
    
    847
    -        self.tagXMPP.connect('event', self.hyperlink_handler, 'xmpp')
    
    848
    -
    
    849
    -        self.tagSthAtSth = buffer_.create_tag('sth_at_sth')
    
    850
    -        self.tagSthAtSth.set_property('foreground', color)
    
    851
    -        self.tagSthAtSth.set_property('underline', Pango.Underline.SINGLE)
    
    852
    -        self.tagSthAtSth.connect('event', self.hyperlink_handler, 'sth_at_sth')
    
    853
    -
    
    854
    -    def __query_tooltip(self, widget, x_pos, y_pos, keyboard_mode, tooltip):
    
    848
    +
    
    849
    +        for tag in self._tags:
    
    850
    +            tag_table.lookup(tag).set_property('foreground', color)
    
    851
    +
    
    852
    +    def _query_tooltip(self, widget, x_pos, y_pos, _keyboard_mode, tooltip):
    
    855 853
             window = widget.get_window(Gtk.TextWindowType.TEXT)
    
    856 854
             x_pos, y_pos = self.window_to_buffer_coords(
    
    857 855
                 Gtk.TextWindowType.TEXT, x_pos, y_pos)
    
    858
    -        if Gtk.MINOR_VERSION > 18:
    
    859
    -            iter_ = self.get_iter_at_position(x_pos, y_pos)[1]
    
    860
    -        else:
    
    861
    -            iter_ = self.get_iter_at_position(x_pos, y_pos)[0]
    
    856
    +        iter_ = self.get_iter_at_position(x_pos, y_pos)[1]
    
    857
    +
    
    862 858
             for tag in iter_.get_tags():
    
    863 859
                 if getattr(tag, 'is_anchor', False):
    
    864 860
                     text = getattr(tag, 'title', False)
    
    ... ... @@ -866,13 +862,19 @@ class HtmlTextView(Gtk.TextView):
    866 862
                         if len(text) > 50:
    
    867 863
                             text = text[:47] + '…'
    
    868 864
                         tooltip.set_text(text)
    
    869
    -                if not self._changed_cursor:
    
    870 865
                         window.set_cursor(get_cursor('HAND2'))
    
    871
    -                    self._changed_cursor = True
    
    872
    -                return True
    
    873
    -        if self._changed_cursor:
    
    866
    +                    self._cursor_changed = True
    
    867
    +                    return True
    
    868
    +
    
    869
    +            tag_name = tag.get_property('name')
    
    870
    +            if tag_name in ('url', 'mail', 'xmpp', 'sth_at_sth'):
    
    871
    +                window.set_cursor(get_cursor('HAND2'))
    
    872
    +                self._cursor_changed = True
    
    873
    +                return False
    
    874
    +
    
    875
    +        if self._cursor_changed:
    
    874 876
                 window.set_cursor(get_cursor('XTERM'))
    
    875
    -            self._changed_cursor = False
    
    877
    +            self._cursor_changed = False
    
    876 878
             return False
    
    877 879
     
    
    878 880
         def show_context_menu(self, _event, kind, text):
    
    ... ... @@ -880,7 +882,7 @@ class HtmlTextView(Gtk.TextView):
    880 882
             if menu is None:
    
    881 883
                 return
    
    882 884
     
    
    883
    -        def destroy(menu, pspec):
    
    885
    +        def destroy(menu, _pspec):
    
    884 886
                 visible = menu.get_property('visible')
    
    885 887
                 if not visible:
    
    886 888
                     GLib.idle_add(menu.destroy)
    
    ... ... @@ -893,7 +895,7 @@ class HtmlTextView(Gtk.TextView):
    893 895
             if event.type == Gdk.EventType.BUTTON_PRESS:
    
    894 896
                 begin_iter = iter_.copy()
    
    895 897
                 # we get the beginning of the tag
    
    896
    -            while not begin_iter.begins_tag(texttag):
    
    898
    +            while not begin_iter.starts_tag(texttag):
    
    897 899
                     begin_iter.backward_char()
    
    898 900
                 end_iter = iter_.copy()
    
    899 901
                 # we get the end of the tag
    
    ... ... @@ -946,8 +948,8 @@ class HtmlTextView(Gtk.TextView):
    946 948
                           'mlat=%(lat)s&mlon=%(lon)s&zoom=16' % \
    
    947 949
                           {'lat': lat, 'lon': lon}
    
    948 950
                     helpers.launch_browser_mailer(kind, uri)
    
    949
    -            # other URIs
    
    950 951
                 else:
    
    952
    +                # other URIs
    
    951 953
                     helpers.launch_browser_mailer(kind, word)
    
    952 954
     
    
    953 955
         def display_html(self, html, textview, conv_textview, iter_=None):
    
    ... ... @@ -956,42 +958,16 @@ class HtmlTextView(Gtk.TextView):
    956 958
                 eob = iter_
    
    957 959
             else:
    
    958 960
                 eob = buffer_.get_end_iter()
    
    959
    -        ## this works too if libxml2 is not available
    
    960
    -        # parser = xml.sax.make_parser(['drv_libxml2'])
    
    961
    -        # parser.setFeature(xml.sax.handler.feature_validation, True)
    
    962 961
             parser = xml.sax.make_parser()
    
    963 962
             parser.setContentHandler(HtmlHandler(textview, conv_textview, eob))
    
    964 963
             parser.parse(StringIO(html))
    
    965 964
     
    
    966
    -        # too much space after :)
    
    967
    -        #if not eob.starts_line():
    
    968
    -        #    buffer_.insert(eob, '\n')
    
    969
    -
    
    970
    -    def on_html_text_view_copy_clipboard(self, unused_data):
    
    971
    -        clipboard = self.get_clipboard(Gdk.SELECTION_CLIPBOARD)
    
    972
    -        selected = self.get_selected_text()
    
    965
    +    @staticmethod
    
    966
    +    def _on_copy_clipboard(textview):
    
    967
    +        clipboard = textview.get_clipboard(Gdk.SELECTION_CLIPBOARD)
    
    968
    +        selected = textview.get_selected_text()
    
    973 969
             clipboard.set_text(selected, -1)
    
    974
    -        GObject.signal_stop_emission_by_name(self, 'copy-clipboard')
    
    975
    -
    
    976
    -    def on_html_text_view_realized(self, unused_data):
    
    977
    -        self.get_buffer().remove_selection_clipboard(self.get_clipboard(
    
    978
    -            Gdk.SELECTION_PRIMARY))
    
    979
    -
    
    980
    -    def on_html_text_view_unrealized(self, unused_data):
    
    981
    -        self.get_buffer().add_selection_clipboard(self.get_clipboard(
    
    982
    -            Gdk.SELECTION_PRIMARY))
    
    983
    -
    
    984
    -    def on_left_mouse_button_release(self, widget, event):
    
    985
    -        if event.button != 1:
    
    986
    -            return
    
    987
    -
    
    988
    -        bounds = self.get_buffer().get_selection_bounds()
    
    989
    -        if bounds:
    
    990
    -            # textview can be hidden while we add a new line in it.
    
    991
    -            if self.has_screen():
    
    992
    -                clipboard = self.get_clipboard(Gdk.SELECTION_PRIMARY)
    
    993
    -                selected = self.get_selected_text()
    
    994
    -                clipboard.set_text(selected, -1)
    
    970
    +        GObject.signal_stop_emission_by_name(textview, 'copy-clipboard')
    
    995 971
     
    
    996 972
         def get_selected_text(self):
    
    997 973
             bounds = self.get_buffer().get_selection_bounds()