Module conversation_textview
[hide private]
[frames] | no frames]

Source Code for Module conversation_textview

   1  # -*- coding:utf-8 -*- 
   2  ## src/conversation_textview.py 
   3  ## 
   4  ## Copyright (C) 2005 Norman Rasmussen <norman AT rasmussen.co.za> 
   5  ## Copyright (C) 2005-2006 Alex Mauer <hawke AT hawkesnest.net> 
   6  ##                         Travis Shirk <travis AT pobox.com> 
   7  ## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com> 
   8  ## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org> 
   9  ## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com> 
  10  ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org> 
  11  ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org> 
  12  ##                    Julien Pivotto <roidelapluie AT gmail.com> 
  13  ##                    Stephan Erb <steve-e AT h3c.de> 
  14  ## 
  15  ## This file is part of Gajim. 
  16  ## 
  17  ## Gajim is free software; you can redistribute it and/or modify 
  18  ## it under the terms of the GNU General Public License as published 
  19  ## by the Free Software Foundation; version 3 only. 
  20  ## 
  21  ## Gajim is distributed in the hope that it will be useful, 
  22  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
  23  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  24  ## GNU General Public License for more details. 
  25  ## 
  26  ## You should have received a copy of the GNU General Public License 
  27  ## along with Gajim. If not, see <http://www.gnu.org/licenses/>. 
  28  ## 
  29   
  30  from threading import Timer # for smooth scrolling 
  31   
  32  import gtk 
  33  import pango 
  34  import gobject 
  35  import time 
  36  import os 
  37  import tooltips 
  38  import dialogs 
  39  import locale 
  40  import Queue 
  41  import urllib 
  42   
  43  import gtkgui_helpers 
  44  from common import gajim 
  45  from common import helpers 
  46  from common import latex 
  47  from common import i18n 
  48  from calendar import timegm 
  49  from common.fuzzyclock import FuzzyClock 
  50   
  51  from htmltextview import HtmlTextView 
  52  from common.exceptions import GajimGeneralException 
  53  from common.exceptions import LatexError 
  54   
  55  NOT_SHOWN = 0 
  56  ALREADY_RECEIVED = 1 
  57  SHOWN = 2 
  58   
59 -def is_selection_modified(mark):
60 name = mark.get_name() 61 if name and name in ('selection_bound', 'insert'): 62 return True 63 else: 64 return False
65
66 -def has_focus(widget):
67 return widget.flags() & gtk.HAS_FOCUS == gtk.HAS_FOCUS
68
69 -class TextViewImage(gtk.Image):
70
71 - def __init__(self, anchor, text):
72 super(TextViewImage, self).__init__() 73 self.anchor = anchor 74 self._selected = False 75 self._disconnect_funcs = [] 76 self.connect('parent-set', self.on_parent_set) 77 self.connect('expose-event', self.on_expose) 78 self.set_tooltip_text(text) 79 self.anchor.set_data('plaintext', text)
80
81 - def _get_selected(self):
82 parent = self.get_parent() 83 if not parent or not self.anchor: return False 84 buffer_ = parent.get_buffer() 85 position = buffer_.get_iter_at_child_anchor(self.anchor) 86 bounds = buffer_.get_selection_bounds() 87 if bounds and position.in_range(*bounds): 88 return True 89 else: 90 return False
91
92 - def get_state(self):
93 parent = self.get_parent() 94 if not parent: 95 return gtk.STATE_NORMAL 96 if self._selected: 97 if has_focus(parent): 98 return gtk.STATE_SELECTED 99 else: 100 return gtk.STATE_ACTIVE 101 else: 102 return gtk.STATE_NORMAL
103
104 - def _update_selected(self):
105 selected = self._get_selected() 106 if self._selected != selected: 107 self._selected = selected 108 self.queue_draw()
109
110 - def _do_connect(self, widget, signal, callback):
111 id_ = widget.connect(signal, callback) 112 def disconnect(): 113 widget.disconnect(id_)
114 self._disconnect_funcs.append(disconnect)
115
116 - def _disconnect_signals(self):
117 for func in self._disconnect_funcs: 118 func() 119 self._disconnect_funcs = []
120
121 - def on_parent_set(self, widget, old_parent):
122 parent = self.get_parent() 123 if not parent: 124 self._disconnect_signals() 125 return 126 127 self._do_connect(parent, 'style-set', self.do_queue_draw) 128 self._do_connect(parent, 'focus-in-event', self.do_queue_draw) 129 self._do_connect(parent, 'focus-out-event', self.do_queue_draw) 130 131 textbuf = parent.get_buffer() 132 self._do_connect(textbuf, 'mark-set', self.on_mark_set) 133 self._do_connect(textbuf, 'mark-deleted', self.on_mark_deleted)
134
135 - def do_queue_draw(self, *args):
136 self.queue_draw() 137 return False
138
139 - def on_mark_set(self, buf, iterat, mark):
140 self.on_mark_modified(mark) 141 return False
142
143 - def on_mark_deleted(self, buf, mark):
144 self.on_mark_modified(mark) 145 return False
146
147 - def on_mark_modified(self, mark):
148 if is_selection_modified(mark): 149 self._update_selected()
150
151 - def on_expose(self, widget, event):
152 state = self.get_state() 153 if state != gtk.STATE_NORMAL: 154 gc = widget.get_style().base_gc[state] 155 area = widget.allocation 156 widget.window.draw_rectangle(gc, True, area.x, area.y, 157 area.width, area.height) 158 return False
159 160
161 -class ConversationTextview(gobject.GObject):
162 """ 163 Class for the conversation textview (where user reads already said messages) 164 for chat/groupchat windows 165 """ 166 __gsignals__ = dict( 167 quote = (gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_ACTION, 168 None, # return value 169 (str, ) # arguments 170 ) 171 ) 172 173 FOCUS_OUT_LINE_PIXBUF = gtkgui_helpers.get_icon_pixmap('gajim-muc_separator') 174 XEP0184_WARNING_PIXBUF = gtkgui_helpers.get_icon_pixmap( 175 'gajim-receipt_missing') 176 177 # smooth scroll constants 178 MAX_SCROLL_TIME = 0.4 # seconds 179 SCROLL_DELAY = 33 # milliseconds 180
181 - def __init__(self, account, used_in_history_window = False):
182 """ 183 If used_in_history_window is True, then we do not show Clear menuitem in 184 context menu 185 """ 186 gobject.GObject.__init__(self) 187 self.used_in_history_window = used_in_history_window 188 189 self.fc = FuzzyClock() 190 191 192 # no need to inherit TextView, use it as atrribute is safer 193 self.tv = HtmlTextView() 194 self.tv.html_hyperlink_handler = self.html_hyperlink_handler 195 196 # set properties 197 self.tv.set_border_width(1) 198 self.tv.set_accepts_tab(True) 199 self.tv.set_editable(False) 200 self.tv.set_cursor_visible(False) 201 self.tv.set_wrap_mode(gtk.WRAP_WORD_CHAR) 202 self.tv.set_left_margin(2) 203 self.tv.set_right_margin(2) 204 self.handlers = {} 205 self.images = [] 206 self.image_cache = {} 207 self.xep0184_marks = {} 208 self.xep0184_shown = {} 209 210 # It's True when we scroll in the code, so we can detect scroll from user 211 self.auto_scrolling = False 212 213 # connect signals 214 id_ = self.tv.connect('motion_notify_event', 215 self.on_textview_motion_notify_event) 216 self.handlers[id_] = self.tv 217 id_ = self.tv.connect('populate_popup', self.on_textview_populate_popup) 218 self.handlers[id_] = self.tv 219 id_ = self.tv.connect('button_press_event', 220 self.on_textview_button_press_event) 221 self.handlers[id_] = self.tv 222 223 id_ = self.tv.connect('expose-event', 224 self.on_textview_expose_event) 225 self.handlers[id_] = self.tv 226 227 228 self.account = account 229 self.change_cursor = False 230 self.last_time_printout = 0 231 232 font = pango.FontDescription(gajim.config.get('conversation_font')) 233 self.tv.modify_font(font) 234 buffer_ = self.tv.get_buffer() 235 end_iter = buffer_.get_end_iter() 236 buffer_.create_mark('end', end_iter, False) 237 238 self.tagIn = buffer_.create_tag('incoming') 239 color = gajim.config.get('inmsgcolor') 240 font = pango.FontDescription(gajim.config.get('inmsgfont')) 241 self.tagIn.set_property('foreground', color) 242 self.tagIn.set_property('font-desc', font) 243 244 self.tagOut = buffer_.create_tag('outgoing') 245 color = gajim.config.get('outmsgcolor') 246 font = pango.FontDescription(gajim.config.get('outmsgfont')) 247 self.tagOut.set_property('foreground', color) 248 self.tagOut.set_property('font-desc', font) 249 250 self.tagStatus = buffer_.create_tag('status') 251 color = gajim.config.get('statusmsgcolor') 252 font = pango.FontDescription(gajim.config.get('satusmsgfont')) 253 self.tagStatus.set_property('foreground', color) 254 self.tagStatus.set_property('font-desc', font) 255 256 self.tagInText = buffer_.create_tag('incomingtxt') 257 color = gajim.config.get('inmsgtxtcolor') 258 font = pango.FontDescription(gajim.config.get('inmsgtxtfont')) 259 if color: 260 self.tagInText.set_property('foreground', color) 261 self.tagInText.set_property('font-desc', font) 262 263 self.tagOutText = buffer_.create_tag('outgoingtxt') 264 color = gajim.config.get('outmsgtxtcolor') 265 if color: 266 font = pango.FontDescription(gajim.config.get('outmsgtxtfont')) 267 self.tagOutText.set_property('foreground', color) 268 self.tagOutText.set_property('font-desc', font) 269 270 colors = gajim.config.get('gc_nicknames_colors') 271 colors = colors.split(':') 272 for i, color in enumerate(colors): 273 tagname = 'gc_nickname_color_' + str(i) 274 tag = buffer_.create_tag(tagname) 275 tag.set_property('foreground', color) 276 277 tag = buffer_.create_tag('marked') 278 color = gajim.config.get('markedmsgcolor') 279 tag.set_property('foreground', color) 280 tag.set_property('weight', pango.WEIGHT_BOLD) 281 282 tag = buffer_.create_tag('time_sometimes') 283 tag.set_property('foreground', 'darkgrey') 284 tag.set_property('scale', pango.SCALE_SMALL) 285 tag.set_property('justification', gtk.JUSTIFY_CENTER) 286 287 tag = buffer_.create_tag('small') 288 tag.set_property('scale', pango.SCALE_SMALL) 289 290 tag = buffer_.create_tag('restored_message') 291 color = gajim.config.get('restored_messages_color') 292 tag.set_property('foreground', color) 293 294 self.tagURL = buffer_.create_tag('url') 295 color = gajim.config.get('urlmsgcolor') 296 self.tagURL.set_property('foreground', color) 297 self.tagURL.set_property('underline', pango.UNDERLINE_SINGLE) 298 id_ = self.tagURL.connect('event', self.hyperlink_handler, 'url') 299 self.handlers[id_] = self.tagURL 300 301 self.tagMail = buffer_.create_tag('mail') 302 self.tagMail.set_property('foreground', color) 303 self.tagMail.set_property('underline', pango.UNDERLINE_SINGLE) 304 id_ = self.tagMail.connect('event', self.hyperlink_handler, 'mail') 305 self.handlers[id_] = self.tagMail 306 307 self.tagXMPP = buffer_.create_tag('xmpp') 308 self.tagXMPP.set_property('foreground', color) 309 self.tagXMPP.set_property('underline', pango.UNDERLINE_SINGLE) 310 id_ = self.tagXMPP.connect('event', self.hyperlink_handler, 'xmpp') 311 self.handlers[id_] = self.tagXMPP 312 313 self.tagSthAtSth = buffer_.create_tag('sth_at_sth') 314 self.tagSthAtSth.set_property('foreground', color) 315 self.tagSthAtSth.set_property('underline', pango.UNDERLINE_SINGLE) 316 id_ = self.tagSthAtSth.connect('event', self.hyperlink_handler, 317 'sth_at_sth') 318 self.handlers[id_] = self.tagSthAtSth 319 320 tag = buffer_.create_tag('bold') 321 tag.set_property('weight', pango.WEIGHT_BOLD) 322 323 tag = buffer_.create_tag('italic') 324 tag.set_property('style', pango.STYLE_ITALIC) 325 326 tag = buffer_.create_tag('underline') 327 tag.set_property('underline', pango.UNDERLINE_SINGLE) 328 329 buffer_.create_tag('focus-out-line', justification = gtk.JUSTIFY_CENTER) 330 self.displaymarking_tags = {} 331 332 tag = buffer_.create_tag('xep0184-warning') 333 334 # One mark at the begining then 2 marks between each lines 335 size = gajim.config.get('max_conversation_lines') 336 size = 2 * size - 1 337 self.marks_queue = Queue.Queue(size) 338 339 self.allow_focus_out_line = True 340 # holds a mark at the end of --- line 341 self.focus_out_end_mark = None 342 343 self.xep0184_warning_tooltip = tooltips.BaseTooltip() 344 345 self.line_tooltip = tooltips.BaseTooltip() 346 # use it for hr too 347 self.tv.focus_out_line_pixbuf = ConversationTextview.FOCUS_OUT_LINE_PIXBUF 348 self.smooth_id = None
349
350 - def del_handlers(self):
351 for i in self.handlers.keys(): 352 if self.handlers[i].handler_is_connected(i): 353 self.handlers[i].disconnect(i) 354 del self.handlers 355 self.tv.destroy()
356 #FIXME: 357 # self.line_tooltip.destroy() 358
359 - def update_tags(self):
360 self.tagIn.set_property('foreground', gajim.config.get('inmsgcolor')) 361 self.tagOut.set_property('foreground', gajim.config.get('outmsgcolor')) 362 self.tagStatus.set_property('foreground', 363 gajim.config.get('statusmsgcolor')) 364 self.tagURL.set_property('foreground', gajim.config.get('urlmsgcolor')) 365 self.tagMail.set_property('foreground', gajim.config.get('urlmsgcolor'))
366
367 - def at_the_end(self):
368 buffer_ = self.tv.get_buffer() 369 end_iter = buffer_.get_end_iter() 370 end_rect = self.tv.get_iter_location(end_iter) 371 visible_rect = self.tv.get_visible_rect() 372 if end_rect.y <= (visible_rect.y + visible_rect.height): 373 return True 374 return False
375 376 # Smooth scrolling inspired by Pidgin code
377 - def smooth_scroll(self):
378 parent = self.tv.get_parent() 379 if not parent: 380 return False 381 vadj = parent.get_vadjustment() 382 max_val = vadj.upper - vadj.page_size + 1 383 cur_val = vadj.get_value() 384 # scroll by 1/3rd of remaining distance 385 onethird = cur_val + ((max_val - cur_val) / 3.0) 386 self.auto_scrolling = True 387 vadj.set_value(onethird) 388 self.auto_scrolling = False 389 if max_val - onethird < 0.01: 390 self.smooth_id = None 391 self.smooth_scroll_timer.cancel() 392 return False 393 return True
394
395 - def smooth_scroll_timeout(self):
396 gobject.idle_add(self.do_smooth_scroll_timeout) 397 return
398
399 - def do_smooth_scroll_timeout(self):
400 if not self.smooth_id: 401 # we finished scrolling 402 return 403 gobject.source_remove(self.smooth_id) 404 self.smooth_id = None 405 parent = self.tv.get_parent() 406 if parent: 407 vadj = parent.get_vadjustment() 408 self.auto_scrolling = True 409 vadj.set_value(vadj.upper - vadj.page_size + 1) 410 self.auto_scrolling = False
411
412 - def smooth_scroll_to_end(self):
413 if None != self.smooth_id: # already scrolling 414 return False 415 self.smooth_id = gobject.timeout_add(self.SCROLL_DELAY, 416 self.smooth_scroll) 417 self.smooth_scroll_timer = Timer(self.MAX_SCROLL_TIME, 418 self.smooth_scroll_timeout) 419 self.smooth_scroll_timer.start() 420 return False
421
422 - def scroll_to_end(self):
423 parent = self.tv.get_parent() 424 buffer_ = self.tv.get_buffer() 425 end_mark = buffer_.get_mark('end') 426 if not end_mark: 427 return False 428 self.auto_scrolling = True 429 self.tv.scroll_to_mark(end_mark, 0, True, 0, 1) 430 adjustment = parent.get_hadjustment() 431 adjustment.set_value(0) 432 self.auto_scrolling = False 433 return False # when called in an idle_add, just do it once
434
435 - def bring_scroll_to_end(self, diff_y = 0, 436 use_smooth=gajim.config.get('use_smooth_scrolling')):
437 ''' scrolls to the end of textview if end is not visible ''' 438 buffer_ = self.tv.get_buffer() 439 end_iter = buffer_.get_end_iter() 440 end_rect = self.tv.get_iter_location(end_iter) 441 visible_rect = self.tv.get_visible_rect() 442 # scroll only if expected end is not visible 443 if end_rect.y >= (visible_rect.y + visible_rect.height + diff_y): 444 if use_smooth: 445 gobject.idle_add(self.smooth_scroll_to_end) 446 else: 447 gobject.idle_add(self.scroll_to_end_iter)
448
449 - def scroll_to_end_iter(self):
450 buffer_ = self.tv.get_buffer() 451 end_iter = buffer_.get_end_iter() 452 if not end_iter: 453 return False 454 self.tv.scroll_to_iter(end_iter, 0, False, 1, 1) 455 return False # when called in an idle_add, just do it once
456
457 - def stop_scrolling(self):
458 if self.smooth_id: 459 gobject.source_remove(self.smooth_id) 460 self.smooth_id = None 461 self.smooth_scroll_timer.cancel()
462
463 - def show_xep0184_warning(self, id_):
464 if id_ in self.xep0184_marks: 465 return 466 467 buffer_ = self.tv.get_buffer() 468 buffer_.begin_user_action() 469 470 self.xep0184_marks[id_] = buffer_.create_mark(None, 471 buffer_.get_end_iter(), left_gravity=True) 472 self.xep0184_shown[id_] = NOT_SHOWN 473 474 def show_it(): 475 if (not id_ in self.xep0184_shown) or \ 476 self.xep0184_shown[id_] == ALREADY_RECEIVED: 477 return False 478 479 end_iter = buffer_.get_iter_at_mark(self.xep0184_marks[id_]) 480 buffer_.insert(end_iter, ' ') 481 anchor = buffer_.create_child_anchor(end_iter) 482 img = TextViewImage(anchor, '') 483 img.set_from_pixbuf(ConversationTextview.XEP0184_WARNING_PIXBUF) 484 img.show() 485 self.tv.add_child_at_anchor(img, anchor) 486 before_img_iter = buffer_.get_iter_at_mark(self.xep0184_marks[id_]) 487 before_img_iter.forward_char() 488 post_img_iter = before_img_iter.copy() 489 post_img_iter.forward_char() 490 buffer_.apply_tag_by_name('xep0184-warning', before_img_iter, 491 post_img_iter) 492 493 self.xep0184_shown[id_] = SHOWN 494 return False
495 gobject.timeout_add_seconds(3, show_it) 496 497 buffer_.end_user_action()
498
499 - def hide_xep0184_warning(self, id_):
500 if id_ not in self.xep0184_marks: 501 return 502 503 if self.xep0184_shown[id_] == NOT_SHOWN: 504 self.xep0184_shown[id_] = ALREADY_RECEIVED 505 return 506 507 buffer_ = self.tv.get_buffer() 508 buffer_.begin_user_action() 509 510 begin_iter = buffer_.get_iter_at_mark(self.xep0184_marks[id_]) 511 512 end_iter = begin_iter.copy() 513 # XXX: Is there a nicer way? 514 end_iter.forward_char() 515 end_iter.forward_char() 516 517 buffer_.delete(begin_iter, end_iter) 518 buffer_.delete_mark(self.xep0184_marks[id_]) 519 520 buffer_.end_user_action() 521 522 del self.xep0184_marks[id_] 523 del self.xep0184_shown[id_]
524
525 - def show_focus_out_line(self):
526 if not self.allow_focus_out_line: 527 # if room did not receive focus-in from the last time we added 528 # --- line then do not readd 529 return 530 531 print_focus_out_line = False 532 buffer_ = self.tv.get_buffer() 533 534 if self.focus_out_end_mark is None: 535 # this happens only first time we focus out on this room 536 print_focus_out_line = True 537 538 else: 539 focus_out_end_iter = buffer_.get_iter_at_mark(self.focus_out_end_mark) 540 focus_out_end_iter_offset = focus_out_end_iter.get_offset() 541 if focus_out_end_iter_offset != buffer_.get_end_iter().get_offset(): 542 # this means after last-focus something was printed 543 # (else end_iter's offset is the same as before) 544 # only then print ---- line (eg. we avoid printing many following 545 # ---- lines) 546 print_focus_out_line = True 547 548 if print_focus_out_line and buffer_.get_char_count() > 0: 549 buffer_.begin_user_action() 550 551 # remove previous focus out line if such focus out line exists 552 if self.focus_out_end_mark is not None: 553 end_iter_for_previous_line = buffer_.get_iter_at_mark( 554 self.focus_out_end_mark) 555 begin_iter_for_previous_line = end_iter_for_previous_line.copy() 556 # img_char+1 (the '\n') 557 begin_iter_for_previous_line.backward_chars(2) 558 559 # remove focus out line 560 buffer_.delete(begin_iter_for_previous_line, 561 end_iter_for_previous_line) 562 buffer_.delete_mark(self.focus_out_end_mark) 563 564 # add the new focus out line 565 end_iter = buffer_.get_end_iter() 566 buffer_.insert(end_iter, '\n') 567 buffer_.insert_pixbuf(end_iter, 568 ConversationTextview.FOCUS_OUT_LINE_PIXBUF) 569 570 end_iter = buffer_.get_end_iter() 571 before_img_iter = end_iter.copy() 572 # one char back (an image also takes one char) 573 before_img_iter.backward_char() 574 buffer_.apply_tag_by_name('focus-out-line', before_img_iter, end_iter) 575 576 self.allow_focus_out_line = False 577 578 # update the iter we hold to make comparison the next time 579 self.focus_out_end_mark = buffer_.create_mark(None, 580 buffer_.get_end_iter(), left_gravity=True) 581 582 buffer_.end_user_action() 583 584 # scroll to the end (via idle in case the scrollbar has appeared) 585 gobject.idle_add(self.scroll_to_end)
586
587 - def show_xep0184_warning_tooltip(self):
588 pointer = self.tv.get_pointer() 589 x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, 590 pointer[0], pointer[1]) 591 tags = self.tv.get_iter_at_location(x, y).get_tags() 592 tag_table = self.tv.get_buffer().get_tag_table() 593 xep0184_warning = False 594 for tag in tags: 595 if tag == tag_table.lookup('xep0184-warning'): 596 xep0184_warning = True 597 break 598 if xep0184_warning and not self.xep0184_warning_tooltip.win: 599 # check if the current pointer is still over the line 600 position = self.tv.window.get_origin() 601 self.xep0184_warning_tooltip.show_tooltip(_('This icon indicates that ' 602 'this message has not yet\nbeen received by the remote end. ' 603 "If this icon stays\nfor a long time, it's likely the message got " 604 'lost.'), 8, position[1] + pointer[1])
605
606 - def show_line_tooltip(self):
607 pointer = self.tv.get_pointer() 608 x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, 609 pointer[0], pointer[1]) 610 tags = self.tv.get_iter_at_location(x, y).get_tags() 611 tag_table = self.tv.get_buffer().get_tag_table() 612 over_line = False 613 for tag in tags: 614 if tag == tag_table.lookup('focus-out-line'): 615 over_line = True 616 break 617 if over_line and not self.line_tooltip.win: 618 # check if the current pointer is still over the line 619 position = self.tv.window.get_origin() 620 self.line_tooltip.show_tooltip(_('Text below this line is what has ' 621 'been said since the\nlast time you paid attention to this group ' 622 'chat'), 8, position[1] + pointer[1])
623
624 - def on_textview_expose_event(self, widget, event):
625 expalloc = event.area 626 exp_x0 = expalloc.x 627 exp_y0 = expalloc.y 628 exp_x1 = exp_x0 + expalloc.width 629 exp_y1 = exp_y0 + expalloc.height 630 631 try: 632 tryfirst = [self.image_cache[(exp_x0, exp_y0)]] 633 except KeyError: 634 tryfirst = [] 635 636 for image in tryfirst + self.images: 637 imgalloc = image.allocation 638 img_x0 = imgalloc.x 639 img_y0 = imgalloc.y 640 img_x1 = img_x0 + imgalloc.width 641 img_y1 = img_y0 + imgalloc.height 642 643 if img_x0 <= exp_x0 and img_y0 <= exp_y0 and \ 644 exp_x1 <= img_x1 and exp_y1 <= img_y1: 645 self.image_cache[(img_x0, img_y0)] = image 646 widget.propagate_expose(image, event) 647 return True 648 return False
649
650 - def on_textview_motion_notify_event(self, widget, event):
651 """ 652 Change the cursor to a hand when we are over a mail or an url 653 """ 654 pointer_x, pointer_y = self.tv.window.get_pointer()[0:2] 655 x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, 656 pointer_x, pointer_y) 657 tags = self.tv.get_iter_at_location(x, y).get_tags() 658 if self.change_cursor: 659 self.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor( 660 gtk.gdk.Cursor(gtk.gdk.XTERM)) 661 self.change_cursor = False 662 tag_table = self.tv.get_buffer().get_tag_table() 663 over_line = False 664 xep0184_warning = False 665 for tag in tags: 666 if tag in (tag_table.lookup('url'), tag_table.lookup('mail'), \ 667 tag_table.lookup('xmpp'), tag_table.lookup('sth_at_sth')): 668 self.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor( 669 gtk.gdk.Cursor(gtk.gdk.HAND2)) 670 self.change_cursor = True 671 elif tag == tag_table.lookup('focus-out-line'): 672 over_line = True 673 elif tag == tag_table.lookup('xep0184-warning'): 674 xep0184_warning = True 675 676 if self.line_tooltip.timeout != 0: 677 # Check if we should hide the line tooltip 678 if not over_line: 679 self.line_tooltip.hide_tooltip() 680 if self.xep0184_warning_tooltip.timeout != 0: 681 # Check if we should hide the XEP-184 warning tooltip 682 if not xep0184_warning: 683 self.xep0184_warning_tooltip.hide_tooltip() 684 if over_line and not self.line_tooltip.win: 685 self.line_tooltip.timeout = gobject.timeout_add(500, 686 self.show_line_tooltip) 687 self.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor( 688 gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) 689 self.change_cursor = True 690 if xep0184_warning and not self.xep0184_warning_tooltip.win: 691 self.xep0184_warning_tooltip.timeout = gobject.timeout_add(500, 692 self.show_xep0184_warning_tooltip) 693 self.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor( 694 gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) 695 self.change_cursor = True
696
697 - def clear(self, tv = None):
698 """ 699 Clear text in the textview 700 """ 701 buffer_ = self.tv.get_buffer() 702 start, end = buffer_.get_bounds() 703 buffer_.delete(start, end) 704 size = gajim.config.get('max_conversation_lines') 705 size = 2 * size - 1 706 self.marks_queue = Queue.Queue(size) 707 self.focus_out_end_mark = None 708 self.just_cleared = True
709
710 - def visit_url_from_menuitem(self, widget, link):
711 """ 712 Basically it filters out the widget instance 713 """ 714 helpers.launch_browser_mailer('url', link)
715
716 - def on_textview_populate_popup(self, textview, menu):
717 """ 718 Override the default context menu and we prepend Clear (only if 719 used_in_history_window is False) and if we have sth selected we show a 720 submenu with actions on the phrase (see 721 on_conversation_textview_button_press_event) 722 """ 723 separator_menuitem_was_added = False 724 if not self.used_in_history_window: 725 item = gtk.SeparatorMenuItem() 726 menu.prepend(item) 727 separator_menuitem_was_added = True 728 729 item = gtk.ImageMenuItem(gtk.STOCK_CLEAR) 730 menu.prepend(item) 731 id_ = item.connect('activate', self.clear) 732 self.handlers[id_] = item 733 734 if self.selected_phrase: 735 if not separator_menuitem_was_added: 736 item = gtk.SeparatorMenuItem() 737 menu.prepend(item) 738 739 if not self.used_in_history_window: 740 item = gtk.MenuItem(_('_Quote')) 741 id_ = item.connect('activate', self.on_quote) 742 self.handlers[id_] = item 743 menu.prepend(item) 744 745 _selected_phrase = helpers.reduce_chars_newlines( 746 self.selected_phrase, 25, 2) 747 item = gtk.MenuItem(_('_Actions for "%s"') % _selected_phrase) 748 menu.prepend(item) 749 submenu = gtk.Menu() 750 item.set_submenu(submenu) 751 phrase_for_url = urllib.quote(self.selected_phrase.encode('utf-8')) 752 753 always_use_en = gajim.config.get('always_english_wikipedia') 754 if always_use_en: 755 link = 'http://en.wikipedia.org/wiki/Special:Search?search=%s'\ 756 % phrase_for_url 757 else: 758 link = 'http://%s.wikipedia.org/wiki/Special:Search?search=%s'\ 759 % (gajim.LANG, phrase_for_url) 760 item = gtk.MenuItem(_('Read _Wikipedia Article')) 761 id_ = item.connect('activate', self.visit_url_from_menuitem, link) 762 self.handlers[id_] = item 763 submenu.append(item) 764 765 item = gtk.MenuItem(_('Look it up in _Dictionary')) 766 dict_link = gajim.config.get('dictionary_url') 767 if dict_link == 'WIKTIONARY': 768 # special link (yeah undocumented but default) 769 always_use_en = gajim.config.get('always_english_wiktionary') 770 if always_use_en: 771 link = 'http://en.wiktionary.org/wiki/Special:Search?search=%s'\ 772 % phrase_for_url 773 else: 774 link = 'http://%s.wiktionary.org/wiki/Special:Search?search=%s'\ 775 % (gajim.LANG, phrase_for_url) 776 id_ = item.connect('activate', self.visit_url_from_menuitem, link) 777 self.handlers[id_] = item 778 else: 779 if dict_link.find('%s') == -1: 780 # we must have %s in the url if not WIKTIONARY 781 item = gtk.MenuItem(_( 782 'Dictionary URL is missing an "%s" and it is not WIKTIONARY')) 783 item.set_property('sensitive', False) 784 else: 785 link = dict_link % phrase_for_url 786 id_ = item.connect('activate', self.visit_url_from_menuitem, 787 link) 788 self.handlers[id_] = item 789 submenu.append(item) 790 791 792 search_link = gajim.config.get('search_engine') 793 if search_link.find('%s') == -1: 794 # we must have %s in the url 795 item = gtk.MenuItem(_('Web Search URL is missing an "%s"')) 796 item.set_property('sensitive', False) 797 else: 798 item = gtk.MenuItem(_('Web _Search for it')) 799 link = search_link % phrase_for_url 800 id_ = item.connect('activate', self.visit_url_from_menuitem, link) 801 self.handlers[id_] = item 802 submenu.append(item) 803 804 item = gtk.MenuItem(_('Open as _Link')) 805 id_ = item.connect('activate', self.visit_url_from_menuitem, link) 806 self.handlers[id_] = item 807 submenu.append(item) 808 809 menu.show_all()
810
811 - def on_quote(self, widget):
812 self.emit('quote', self.selected_phrase)
813
814 - def on_textview_button_press_event(self, widget, event):
815 # If we clicked on a taged text do NOT open the standard popup menu 816 # if normal text check if we have sth selected 817 self.selected_phrase = '' # do not move belove event button check! 818 819 if event.button != 3: # if not right click 820 return False 821 822 x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, 823 int(event.x), int(event.y)) 824 iter_ = self.tv.get_iter_at_location(x, y) 825 tags = iter_.get_tags() 826 827 828 if tags: # we clicked on sth special (it can be status message too) 829 for tag in tags: 830 tag_name = tag.get_property('name') 831 if tag_name in ('url', 'mail', 'xmpp', 'sth_at_sth'): 832 return True # we block normal context menu 833 834 # we check if sth was selected and if it was we assign 835 # selected_phrase variable 836 # so on_conversation_textview_populate_popup can use it 837 buffer_ = self.tv.get_buffer() 838 return_val = buffer_.get_selection_bounds() 839 if return_val: # if sth was selected when we right-clicked 840 # get the selected text 841 start_sel, finish_sel = return_val[0], return_val[1] 842 self.selected_phrase = buffer_.get_text(start_sel, finish_sel).decode( 843 'utf-8') 844 elif ord(iter_.get_char()) > 31: 845 # we clicked on a word, do as if it's selected for context menu 846 start_sel = iter_.copy() 847 if not start_sel.starts_word(): 848 start_sel.backward_word_start() 849 finish_sel = iter_.copy() 850 if not finish_sel.ends_word(): 851 finish_sel.forward_word_end() 852 self.selected_phrase = buffer_.get_text(start_sel, finish_sel).decode( 853 'utf-8')
854 857 861
862 - def on_start_chat_activate(self, widget, jid):
863 gajim.interface.new_chat_from_jid(self.account, jid)
864
865 - def on_join_group_chat_menuitem_activate(self, widget, room_jid):
866 if 'join_gc' in gajim.interface.instances[self.account]: 867 instance = gajim.interface.instances[self.account]['join_gc'] 868 instance.xml.get_object('room_jid_entry').set_text(room_jid) 869 gajim.interface.instances[self.account]['join_gc'].window.present() 870 else: 871 try: 872 dialogs.JoinGroupchatWindow(account=self.account, room_jid=room_jid) 873 except GajimGeneralException: 874 pass
875
876 - def on_add_to_roster_activate(self, widget, jid):
877 dialogs.AddNewContactWindow(self.account, jid)
878 938 967 976 977
978 - def detect_and_print_special_text(self, otext, other_tags, graphics=True):
979 """ 980 Detect special text (emots & links & formatting), print normal text 981 before any special text it founds, then print special text (that happens 982 many times until last special text is printed) and then return the index 983 after *last* special text, so we can print it in 984 print_conversation_line() 985 """ 986 buffer_ = self.tv.get_buffer() 987 988 insert_tags_func = buffer_.insert_with_tags_by_name 989 # detect_and_print_special_text() is also used by 990 # HtmlHandler.handle_specials() and there tags is gtk.TextTag objects, 991 # not strings 992 if other_tags and isinstance(other_tags[0], gtk.TextTag): 993 insert_tags_func = buffer_.insert_with_tags 994 995 index = 0 996 997 # Too many special elements (emoticons, LaTeX formulas, etc) 998 # may cause Gajim to freeze (see #5129). 999 # We impose an arbitrary limit of 100 specials per message. 1000 specials_limit = 100 1001 1002 # basic: links + mail + formatting is always checked (we like that) 1003 if gajim.config.get('emoticons_theme') and graphics: 1004 # search for emoticons & urls 1005 iterator = gajim.interface.emot_and_basic_re.finditer(otext) 1006 else: # search for just urls + mail + formatting 1007 iterator = gajim.interface.basic_pattern_re.finditer(otext) 1008 for match in iterator: 1009 start, end = match.span() 1010 special_text = otext[start:end] 1011 if start > index: 1012 text_before_special_text = otext[index:start] 1013 end_iter = buffer_.get_end_iter() 1014 # we insert normal text 1015 insert_tags_func(end_iter, text_before_special_text, *other_tags) 1016 index = end # update index 1017 1018 # now print it 1019 self.print_special_text(special_text, other_tags, graphics=graphics) 1020 specials_limit -= 1 1021 if specials_limit <= 0: 1022 break 1023 1024 # add the rest of text located in the index and after 1025 end_iter = buffer_.get_end_iter() 1026 insert_tags_func(end_iter, otext[index:], *other_tags) 1027 1028 return buffer_.get_end_iter()
1029
1030 - def print_special_text(self, special_text, other_tags, graphics=True):
1031 """ 1032 Is called by detect_and_print_special_text and prints special text 1033 (emots, links, formatting) 1034 """ 1035 tags = [] 1036 use_other_tags = True 1037 text_is_valid_uri = False 1038 show_ascii_formatting_chars = \ 1039 gajim.config.get('show_ascii_formatting_chars') 1040 buffer_ = self.tv.get_buffer() 1041 1042 # Check if we accept this as an uri 1043 schemes = gajim.config.get('uri_schemes').split() 1044 for scheme in schemes: 1045 if special_text.startswith(scheme + ':'): 1046 text_is_valid_uri = True 1047 1048 possible_emot_ascii_caps = special_text.upper() # emoticons keys are CAPS 1049 if gajim.config.get('emoticons_theme') and \ 1050 possible_emot_ascii_caps in gajim.interface.emoticons.keys() and graphics: 1051 # it's an emoticon 1052 emot_ascii = possible_emot_ascii_caps 1053 end_iter = buffer_.get_end_iter() 1054 anchor = buffer_.create_child_anchor(end_iter) 1055 img = TextViewImage(anchor, special_text) 1056 animations = gajim.interface.emoticons_animations 1057 if not emot_ascii in animations: 1058 animations[emot_ascii] = gtk.gdk.PixbufAnimation( 1059 gajim.interface.emoticons[emot_ascii]) 1060 img.set_from_animation(animations[emot_ascii]) 1061 img.show() 1062 self.images.append(img) 1063 # add with possible animation 1064 self.tv.add_child_at_anchor(img, anchor) 1065 elif special_text.startswith('www.') or \ 1066 special_text.startswith('ftp.') or \ 1067 text_is_valid_uri: 1068 tags.append('url') 1069 use_other_tags = False 1070 elif special_text.startswith('mailto:'): 1071 tags.append('mail') 1072 use_other_tags = False 1073 elif special_text.startswith('xmpp:'): 1074 tags.append('xmpp') 1075 use_other_tags = False 1076 elif gajim.interface.sth_at_sth_dot_sth_re.match(special_text): 1077 # it's a JID or mail 1078 tags.append('sth_at_sth') 1079 use_other_tags = False 1080 elif special_text.startswith('*'): # it's a bold text 1081 tags.append('bold') 1082 if special_text[1] == '/' and special_text[-2] == '/' and\ 1083 len(special_text) > 4: # it's also italic 1084 tags.append('italic') 1085 if not show_ascii_formatting_chars: 1086 special_text = special_text[2:-2] # remove */ /* 1087 elif special_text[1] == '_' and special_text[-2] == '_' and \ 1088 len(special_text) > 4: # it's also underlined 1089 tags.append('underline') 1090 if not show_ascii_formatting_chars: 1091 special_text = special_text[2:-2] # remove *_ _* 1092 else: 1093 if not show_ascii_formatting_chars: 1094 special_text = special_text[1:-1] # remove * * 1095 elif special_text.startswith('/'): # it's an italic text 1096 tags.append('italic') 1097 if special_text[1] == '*' and special_text[-2] == '*' and \ 1098 len(special_text) > 4: # it's also bold 1099 tags.append('bold') 1100 if not show_ascii_formatting_chars: 1101 special_text = special_text[2:-2] # remove /* */ 1102 elif special_text[1] == '_' and special_text[-2] == '_' and \ 1103 len(special_text) > 4: # it's also underlined 1104 tags.append('underline') 1105 if not show_ascii_formatting_chars: 1106 special_text = special_text[2:-2] # remove /_ _/ 1107 else: 1108 if not show_ascii_formatting_chars: 1109 special_text = special_text[1:-1] # remove / / 1110 elif special_text.startswith('_'): # it's an underlined text 1111 tags.append('underline') 1112 if special_text[1] == '*' and special_text[-2] == '*' and \ 1113 len(special_text) > 4: # it's also bold 1114 tags.append('bold') 1115 if not show_ascii_formatting_chars: 1116 special_text = special_text[2:-2] # remove _* *_ 1117 elif special_text[1] == '/' and special_text[-2] == '/' and \ 1118 len(special_text) > 4: # it's also italic 1119 tags.append('italic') 1120 if not show_ascii_formatting_chars: 1121 special_text = special_text[2:-2] # remove _/ /_ 1122 else: 1123 if not show_ascii_formatting_chars: 1124 special_text = special_text[1:-1] # remove _ _ 1125 elif gajim.HAVE_LATEX and special_text.startswith('$$') and \ 1126 special_text.endswith('$$') and graphics: 1127 try: 1128 imagepath = latex.latex_to_image(special_text[2:-2]) 1129 except LatexError, e: 1130 # print the error after the line has been written 1131 gobject.idle_add(self.print_conversation_line, str(e), '', 'info', 1132 '', None) 1133 imagepath = None 1134 end_iter = buffer_.get_end_iter() 1135 if imagepath is not None: 1136 anchor = buffer_.create_child_anchor(end_iter) 1137 img = TextViewImage(anchor, special_text) 1138 img.set_from_file(imagepath) 1139 img.show() 1140 # add 1141 self.tv.add_child_at_anchor(img, anchor) 1142 # delete old file 1143 try: 1144 os.remove(imagepath) 1145 except Exception: 1146 pass 1147 else: 1148 buffer_.insert(end_iter, special_text) 1149 use_other_tags = False 1150 else: 1151 # It's nothing special 1152 if use_other_tags: 1153 end_iter = buffer_.get_end_iter() 1154 insert_tags_func = buffer_.insert_with_tags_by_name 1155 if other_tags and isinstance(other_tags[0], gtk.TextTag): 1156 insert_tags_func = buffer_.insert_with_tags 1157 1158 insert_tags_func(end_iter, special_text, *other_tags) 1159 1160 if tags: 1161 end_iter = buffer_.get_end_iter() 1162 all_tags = tags[:] 1163 if use_other_tags: 1164 all_tags += other_tags 1165 # convert all names to TextTag 1166 ttt = buffer_.get_tag_table() 1167 all_tags = [(ttt.lookup(t) if isinstance(t, str) else t) for t in all_tags] 1168 buffer_.insert_with_tags(end_iter, special_text, *all_tags)
1169
1170 - def print_empty_line(self):
1171 buffer_ = self.tv.get_buffer() 1172 end_iter = buffer_.get_end_iter() 1173 buffer_.insert_with_tags_by_name(end_iter, '\n', 'eol') 1174 self.just_cleared = False
1175
1176 - def print_conversation_line(self, text, jid, kind, name, tim, 1177 other_tags_for_name=[], other_tags_for_time=[], 1178 other_tags_for_text=[], subject=None, old_kind=None, xhtml=None, 1179 simple=False, graphics=True, displaymarking=None):
1180 """ 1181 Print 'chat' type messages 1182 """ 1183 buffer_ = self.tv.get_buffer() 1184 buffer_.begin_user_action() 1185 if self.marks_queue.full(): 1186 # remove oldest line 1187 m1 = self.marks_queue.get() 1188 m2 = self.marks_queue.get() 1189 i1 = buffer_.get_iter_at_mark(m1) 1190 i2 = buffer_.get_iter_at_mark(m2) 1191 buffer_.delete(i1, i2) 1192 buffer_.delete_mark(m1) 1193 end_iter = buffer_.get_end_iter() 1194 end_offset = end_iter.get_offset() 1195 at_the_end = self.at_the_end() 1196 move_selection = False 1197 if buffer_.get_has_selection() and buffer_.get_selection_bounds()[1].\ 1198 get_offset() == end_offset: 1199 move_selection = True 1200 1201 # Create one mark and add it to queue once if it's the first line 1202 # else twice (one for end bound, one for start bound) 1203 mark = None 1204 if buffer_.get_char_count() > 0: 1205 if not simple: 1206 buffer_.insert_with_tags_by_name(end_iter, '\n', 'eol') 1207 if move_selection: 1208 sel_start, sel_end = buffer_.get_selection_bounds() 1209 sel_end.backward_char() 1210 buffer_.select_range(sel_start, sel_end) 1211 mark = buffer_.create_mark(None, end_iter, left_gravity=True) 1212 self.marks_queue.put(mark) 1213 if not mark: 1214 mark = buffer_.create_mark(None, end_iter, left_gravity=True) 1215 self.marks_queue.put(mark) 1216 if kind == 'incoming_queue': 1217 kind = 'incoming' 1218 if old_kind == 'incoming_queue': 1219 old_kind = 'incoming' 1220 # print the time stamp 1221 if not tim: 1222 # We don't have tim for outgoing messages... 1223 tim = time.localtime() 1224 current_print_time = gajim.config.get('print_time') 1225 if current_print_time == 'always' and kind != 'info' and not simple: 1226 timestamp_str = self.get_time_to_show(tim) 1227 timestamp = time.strftime(timestamp_str, tim) 1228 buffer_.insert_with_tags_by_name(end_iter, timestamp, 1229 *other_tags_for_time) 1230 elif current_print_time == 'sometimes' and kind != 'info' and not simple: 1231 every_foo_seconds = 60 * gajim.config.get( 1232 'print_ichat_every_foo_minutes') 1233 seconds_passed = time.mktime(tim) - self.last_time_printout 1234 if seconds_passed > every_foo_seconds: 1235 self.last_time_printout = time.mktime(tim) 1236 end_iter = buffer_.get_end_iter() 1237 if gajim.config.get('print_time_fuzzy') > 0: 1238 ft = self.fc.fuzzy_time(gajim.config.get('print_time_fuzzy'), tim) 1239 tim_format = ft.decode(locale.getpreferredencoding()) 1240 else: 1241 tim_format = self.get_time_to_show(tim) 1242 buffer_.insert_with_tags_by_name(end_iter, tim_format + '\n', 1243 'time_sometimes') 1244 # If there's a displaymarking, print it here. 1245 if displaymarking: 1246 self.print_displaymarking(displaymarking) 1247 # kind = info, we print things as if it was a status: same color, ... 1248 if kind in ('error', 'info'): 1249 kind = 'status' 1250 other_text_tag = self.detect_other_text_tag(text, kind) 1251 text_tags = other_tags_for_text[:] # create a new list 1252 if other_text_tag: 1253 # note that color of /me may be overwritten in gc_control 1254 text_tags.append(other_text_tag) 1255 else: # not status nor /me 1256 if gajim.config.get('chat_merge_consecutive_nickname'): 1257 if kind != old_kind or self.just_cleared: 1258 self.print_name(name, kind, other_tags_for_name) 1259 else: 1260 self.print_real_text(gajim.config.get( 1261 'chat_merge_consecutive_nickname_indent')) 1262 else: 1263 self.print_name(name, kind, other_tags_for_name) 1264 if kind == 'incoming': 1265 text_tags.append('incomingtxt') 1266 elif kind == 'outgoing': 1267 text_tags.append('outgoingtxt') 1268 self.print_subject(subject) 1269 self.print_real_text(text, text_tags, name, xhtml, graphics=graphics) 1270 1271 # scroll to the end of the textview 1272 if at_the_end or kind == 'outgoing': 1273 # we are at the end or we are sending something 1274 # scroll to the end (via idle in case the scrollbar has appeared) 1275 if gajim.config.get('use_smooth_scrolling'): 1276 gobject.idle_add(self.smooth_scroll_to_end) 1277 else: 1278 gobject.idle_add(self.scroll_to_end) 1279 1280 self.just_cleared = False 1281 buffer_.end_user_action()
1282
1283 - def get_time_to_show(self, tim):
1284 """ 1285 Get the time, with the day before if needed and return it. It DOESN'T 1286 format a fuzzy time 1287 """ 1288 format = '' 1289 # get difference in days since epoch (86400 = 24*3600) 1290 # number of days since epoch for current time (in GMT) - 1291 # number of days since epoch for message (in GMT) 1292 diff_day = int(timegm(time.localtime())) / 86400 -\ 1293 int(timegm(tim)) / 86400 1294 if diff_day == 0: 1295 day_str = '' 1296 else: 1297 #%i is day in year (1-365) 1298 day_str = i18n.ngettext('Yesterday', '%i days ago', diff_day, 1299 replace_plural=diff_day) 1300 if day_str: 1301 format += day_str + ' ' 1302 timestamp_str = gajim.config.get('time_stamp') 1303 timestamp_str = helpers.from_one_line(timestamp_str) 1304 format += timestamp_str 1305 tim_format = time.strftime(format, tim) 1306 if locale.getpreferredencoding() != 'KOI8-R': 1307 # if tim_format comes as unicode because of day_str. 1308 # we convert it to the encoding that we want (and that is utf-8) 1309 tim_format = helpers.ensure_utf8_string(tim_format) 1310 return tim_format
1311
1312 - def detect_other_text_tag(self, text, kind):
1313 if kind == 'status': 1314 return kind 1315 elif text.startswith('/me ') or text.startswith('/me\n'): 1316 return kind
1317
1318 - def print_displaymarking(self, displaymarking):
1319 bgcolor = displaymarking.getAttr('bgcolor') or '#FFF' 1320 fgcolor = displaymarking.getAttr('fgcolor') or '#000' 1321 text = displaymarking.getData() 1322 if text: 1323 buffer_ = self.tv.get_buffer() 1324 end_iter = buffer_.get_end_iter() 1325 tag = self.displaymarking_tags.setdefault(bgcolor + '/' + fgcolor, 1326 buffer_.create_tag(None, background=bgcolor, foreground=fgcolor)) 1327 buffer_.insert_with_tags(end_iter, '[' + text + ']', tag) 1328 end_iter = buffer_.get_end_iter() 1329 buffer_.insert_with_tags(end_iter, ' ')
1330
1331 - def print_name(self, name, kind, other_tags_for_name):
1332 if name: 1333 buffer_ = self.tv.get_buffer() 1334 end_iter = buffer_.get_end_iter() 1335 name_tags = other_tags_for_name[:] # create a new list 1336 name_tags.append(kind) 1337 before_str = gajim.config.get('before_nickname') 1338 before_str = helpers.from_one_line(before_str) 1339 after_str = gajim.config.get('after_nickname') 1340 after_str = helpers.from_one_line(after_str) 1341 format = before_str + name + after_str + ' ' 1342 buffer_.insert_with_tags_by_name(end_iter, format, *name_tags)
1343
1344 - def print_subject(self, subject):
1345 if subject: # if we have subject, show it too! 1346 subject = _('Subject: %s\n') % subject 1347 buffer_ = self.tv.get_buffer() 1348 end_iter = buffer_.get_end_iter() 1349 buffer_.insert(end_iter, subject) 1350 self.print_empty_line()
1351
1352 - def print_real_text(self, text, text_tags=[], name=None, xhtml=None, 1353 graphics=True):
1354 """ 1355 Add normal and special text. call this to add text 1356 """ 1357 if xhtml: 1358 try: 1359 if name and (text.startswith('/me ') or text.startswith('/me\n')): 1360 xhtml = xhtml.replace('/me', '<i>* %s</i>' % (name,), 1) 1361 self.tv.display_html(xhtml.encode('utf-8'), self) 1362 return 1363 except Exception, e: 1364 gajim.log.debug('Error processing xhtml' + str(e)) 1365 gajim.log.debug('with |' + xhtml + '|') 1366 1367 # /me is replaced by name if name is given 1368 if name and (text.startswith('/me ') or text.startswith('/me\n')): 1369 text = '* ' + name + text[3:] 1370 text_tags.append('italic') 1371 # detect urls formatting and if the user has it on emoticons 1372 self.detect_and_print_special_text(text, text_tags, graphics=graphics)
1373