root/branches/gajim_0.7/src/chat.py

Revision 1565, 24.3 kB (checked in by nk, 4 years ago)

some more translatable strings

Line 
1##      plugins/tabbed_chat_window.py
2##
3## Gajim Team:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Vincent Hanquez <tab@snarc.org>
6##      - Nikos Kouremenos <kourem@gmail.com>
7##
8##      Copyright (C) 2003-2005 Gajim Team
9##
10## This program is free software; you can redistribute it and/or modify
11## it under the terms of the GNU General Public License as published
12## by the Free Software Foundation; version 2 only.
13##
14## This program is distributed in the hope that it will be useful,
15## but WITHOUT ANY WARRANTY; without even the implied warranty of
16## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17## GNU General Public License for more details.
18##
19
20import gtk
21import gtk.glade
22import pango
23import gobject
24import time
25
26from common import gajim
27from common import i18n
28
29_ = i18n._
30APP = i18n.APP
31gtk.glade.bindtextdomain(APP, i18n.DIR)
32gtk.glade.textdomain(APP)
33
34GTKGUI_GLADE = 'gtkgui.glade'
35
36class Chat:
37        """Class for chat/groupchat windows"""
38        def __init__(self, plugin, account, widget_name):
39                self.xml = gtk.glade.XML(GTKGUI_GLADE, widget_name, APP)
40                self.window = self.xml.get_widget(widget_name)
41                self.widget_name = widget_name
42                self.notebook = self.xml.get_widget('chat_notebook')
43                self.notebook.remove_page(0)
44                self.plugin = plugin
45                self.account = account
46                self.change_cursor = None
47                self.xmls = {}
48                self.tagIn = {}
49                self.tagOut = {}
50                self.tagStatus = {}
51                self.nb_unread = {}
52                self.last_message_time = {}
53                self.print_time_timeout_id = {}
54                self.names = {} # what is printed in the tab (eg. user.name)
55                self.childs = {}
56
57        def update_tags(self):
58                for jid in self.tagIn:
59                        self.tagIn[jid].set_property('foreground',
60                                        gajim.config.get('inmsgcolor'))
61                        self.tagOut[jid].set_property('foreground',
62                                        gajim.config.get('outmsgcolor'))
63                        self.tagStatus[jid].set_property('foreground',
64                                        gajim.config.get('statusmsgcolor'))
65
66        def update_print_time(self):
67                if gajim.config.get('print_time') != 'sometimes':
68                        list_jid = self.print_time_timeout_id.keys()
69                        for jid in list_jid:
70                                gobject.source_remove(self.print_time_timeout_id[jid])
71                                del self.print_time_timeout_id[jid]
72                else:
73                        for jid in self.xmls:
74                                if self.print_time_timeout_id.has_key(jid):
75                                        continue
76                                self.print_time_timeout(jid)
77                                self.print_time_timeout_id[jid] = \
78                                                gobject.timeout_add(300000,
79                                                        self.print_time_timeout,
80                                                        jid)
81
82        def show_title(self):
83                """redraw the window's title"""
84                unread = 0
85                for jid in self.nb_unread:
86                        unread += self.nb_unread[jid]
87                start = ""
88                if unread > 1:
89                        start = '[' + str(unread) + '] '
90                elif unread == 1:
91                        start = '* '
92                chat = self.names[jid]
93                if len(self.xmls) > 1: # if more than one tab in the same window
94                        if self.widget_name == 'tabbed_chat_window':
95                                chat = 'Chat'
96                        elif self.widget_name == 'groupchat_window':
97                                chat = 'Groupchat'
98                title = start + chat
99                if len(gajim.connections) >= 2: # if we have 2 or more accounts
100                        title = title + _(' (account: ') + self.account + ')'
101
102                self.window.set_title(title)
103
104        def redraw_tab(self, jid):
105                """redraw the label of the tab"""
106                start = ''
107                if self.nb_unread[jid] > 1:
108                        start = '[' + str(self.nb_unread[jid]) + '] '
109                elif self.nb_unread[jid] == 1:
110                        start = '* '
111
112                child = self.childs[jid]
113                if self.widget_name == 'tabbed_chat_window':
114                        nickname = self.notebook.get_tab_label(child).get_children()[1]
115                elif self.widget_name == 'groupchat_window':
116                        nickname = self.notebook.get_tab_label(child).get_children()[0]
117
118                nickname.set_text(start + self.names[jid])
119
120        def on_window_destroy(self, widget, kind): #kind is 'chats' or 'gc'
121                #clean self.plugin.windows[self.account][kind]
122                for jid in self.xmls:
123                        if self.plugin.systray_enabled and self.nb_unread[jid] > 0:
124                                self.plugin.systray.remove_jid(jid, self.account)
125                        del self.plugin.windows[self.account][kind][jid]
126                        if self.print_time_timeout_id.has_key(jid):
127                                gobject.source_remove(self.print_time_timeout_id[jid])
128                if self.plugin.windows[self.account][kind].has_key('tabbed'):
129                        del self.plugin.windows[self.account][kind]['tabbed']
130
131        def get_active_jid(self):
132                notebook = self.notebook
133                active_child = notebook.get_nth_page(notebook.get_current_page())
134                active_jid = ''
135                for jid in self.xmls:
136                        if self.childs[jid] == active_child:
137                                active_jid = jid
138                                break
139                return active_jid
140
141        def on_close_button_clicked(self, button, jid):
142                """When close button is pressed: close a tab"""
143                self.remove_tab(jid)
144
145        def on_chat_window_focus_in_event(self, widget, event):
146                """When window get focus"""
147                jid = self.get_active_jid()
148                textview = self.xmls[jid].get_widget('conversation_textview')
149                buffer = textview.get_buffer()
150                end_iter = buffer.get_end_iter()
151                end_rect = textview.get_iter_location(end_iter)
152                visible_rect = textview.get_visible_rect()
153                if end_rect.y <= (visible_rect.y + visible_rect.height):
154                        #we are at the end
155                        if self.nb_unread[jid] > 0:
156                                self.nb_unread[jid] = 0
157                                self.redraw_tab(jid)
158                                self.show_title()
159                                if self.plugin.systray_enabled:
160                                        self.plugin.systray.remove_jid(jid, self.account)
161
162        def on_chat_notebook_switch_page(self, notebook, page, page_num):
163                new_child = notebook.get_nth_page(page_num)
164                new_jid = ''
165                for jid in self.xmls:
166                        if self.childs[jid] == new_child:
167                                new_jid = jid
168                                break
169
170                conversation_textview = self.xmls[new_jid].get_widget(
171                        'conversation_textview')
172                conversation_buffer = conversation_textview.get_buffer()
173                end_iter = conversation_buffer.get_end_iter()
174                end_rect = conversation_textview.get_iter_location(end_iter)
175                visible_rect = conversation_textview.get_visible_rect()
176                if end_rect.y <= (visible_rect.y + visible_rect.height):
177                        #we are at the end
178                        if self.nb_unread[new_jid] > 0:
179                                self.nb_unread[new_jid] = 0
180                                self.redraw_tab(new_jid)
181                                self.show_title()
182                                if self.plugin.systray_enabled:
183                                        self.plugin.systray.remove_jid(new_jid, self.account)
184               
185                if self.widget_name == 'tabbed_chat_window':
186                        nontabbed_status_image = self.xmls[jid].get_widget(
187                                'nontabbed_status_image')
188                        if len(self.xmls) > 1:
189                                nontabbed_status_image.hide()
190                        else:
191                                nontabbed_status_image.show()
192               
193                conversation_textview.grab_focus()
194
195        def set_active_tab(self, jid):
196                self.notebook.set_current_page(self.notebook.page_num(self.childs[jid]))
197
198        def remove_tab(self, jid, kind): #kind is 'chats' or 'gc'
199                if len(self.xmls) == 1:
200                        self.window.destroy()
201                        return
202                if self.nb_unread[jid] > 0:
203                        self.nb_unread[jid] = 0
204                        self.show_title()
205                        if self.plugin.systray_enabled:
206                                self.plugin.systray.remove_jid(jid, self.account)
207                if self.print_time_timeout_id.has_key(jid):
208                        gobject.source_remove(self.print_time_timeout_id[jid])
209                        del self.print_time_timeout_id[jid]
210                self.notebook.remove_page(self.notebook.page_num(self.childs[jid]))
211                if len(self.xmls) == 2:
212                        # one that remains and one that we'll remove, 1 tab remains
213                        self.notebook.set_show_tabs(False)
214
215                del self.plugin.windows[self.account][kind][jid]
216                del self.nb_unread[jid]
217                del self.last_message_time[jid]
218                del self.xmls[jid]
219                del self.tagIn[jid]
220                del self.tagOut[jid]
221                del self.tagStatus[jid]
222                self.show_title()
223
224        def new_tab(self, jid):
225                self.nb_unread[jid] = 0
226                self.last_message_time[jid] = 0
227               
228                conversation_textview = self.xmls[jid].get_widget(
229                        'conversation_textview')
230                conversation_buffer = conversation_textview.get_buffer()
231                end_iter = conversation_buffer.get_end_iter()
232               
233                conversation_buffer.create_mark('end', end_iter, False)
234               
235                self.tagIn[jid] = conversation_buffer.create_tag('incoming')
236                color = gajim.config.get('inmsgcolor')
237                self.tagIn[jid].set_property('foreground', color)
238                self.tagOut[jid] = conversation_buffer.create_tag('outgoing')
239                color = gajim.config.get('outmsgcolor')
240                self.tagOut[jid].set_property('foreground', color)
241                self.tagStatus[jid] = conversation_buffer.create_tag('status')
242                color = gajim.config.get('statusmsgcolor')
243                self.tagStatus[jid].set_property('foreground', color)
244               
245                tag = conversation_buffer.create_tag('time_sometimes')
246                tag.set_property('foreground', '#9e9e9e')
247                tag.set_property('scale', pango.SCALE_SMALL)
248                tag.set_property('justification', gtk.JUSTIFY_CENTER)
249               
250                tag = conversation_buffer.create_tag('url')
251                tag.set_property('foreground', '#0000ff')
252                tag.set_property('underline', pango.UNDERLINE_SINGLE)
253                tag.connect('event', self.hyperlink_handler, 'url')
254
255                tag = conversation_buffer.create_tag('mail')
256                tag.set_property('foreground', '#0000ff')
257                tag.set_property('underline', pango.UNDERLINE_SINGLE)
258                tag.connect('event', self.hyperlink_handler, 'mail')
259               
260                tag = conversation_buffer.create_tag('bold')
261                tag.set_property('weight', pango.WEIGHT_BOLD)
262               
263                tag = conversation_buffer.create_tag('italic')
264                tag.set_property('style', pango.STYLE_ITALIC)
265               
266                tag = conversation_buffer.create_tag('underline')
267                tag.set_property('underline', pango.UNDERLINE_SINGLE)
268               
269                self.xmls[jid].signal_autoconnect(self)
270                conversation_scrolledwindow = self.xmls[jid].get_widget(
271                        'conversation_scrolledwindow')
272                conversation_scrolledwindow.get_vadjustment().connect('value-changed',
273                        self.on_conversation_vadjustment_value_changed)
274               
275                child = self.childs[jid]
276                self.notebook.append_page(child)
277               
278
279                if len(self.xmls) > 1:
280                        self.notebook.set_show_tabs(True)
281
282                if self.widget_name == 'tabbed_chat_window':
283                        xm = gtk.glade.XML(GTKGUI_GLADE, 'chat_tab_hbox', APP)
284                        tab_hbox = xm.get_widget('chat_tab_hbox')
285                elif self.widget_name == 'groupchat_window':
286                        xm = gtk.glade.XML(GTKGUI_GLADE, 'groupchat_tab_hbox', APP)
287                        tab_hbox = xm.get_widget('groupchat_tab_hbox')
288
289                xm.signal_connect('on_close_button_clicked', 
290                        self.on_close_button_clicked, jid)
291                self.notebook.set_tab_label(child, tab_hbox)
292
293                self.show_title()
294
295        def on_conversation_textview_key_press_event(self, widget, event):
296                """Do not block these events and send them to the notebook"""
297                if event.state & gtk.gdk.CONTROL_MASK:
298                        if event.keyval == gtk.keysyms.Tab: # CTRL + TAB
299                                self.notebook.emit('key_press_event', event)
300                        elif event.keyval == gtk.keysyms.ISO_Left_Tab: # CTRL + SHIFT + TAB
301                                self.notebook.emit('key_press_event', event)
302                        elif event.keyval == gtk.keysyms.Page_Down: # CTRL + PAGE DOWN
303                                self.notebook.emit('key_press_event', event)
304                        elif event.keyval == gtk.keysyms.Page_Up: # CTRL + PAGE UP
305                                self.notebook.emit('key_press_event', event)
306                        elif event.keyval == gtk.keysyms.v: # CTRL + V
307                                jid = self.get_active_jid()
308                                message_textview = self.xmls[jid].get_widget('message_textview')
309                                if not message_textview.is_focus():
310                                        message_textview.grab_focus()
311                                message_textview.emit('key_press_event', event)
312                               
313        def on_chat_notebook_key_press_event(self, widget, event):
314                st = '1234567890' # zero is here cause humans count from 1, pc from 0 :P
315                jid = self.get_active_jid()
316                if event.keyval == gtk.keysyms.Escape: # ESCAPE
317                        if self.widget_name == 'tabbed_chat_window':
318                                self.remove_tab(jid)
319                elif event.keyval == gtk.keysyms.F4 and \
320                     (event.state & gtk.gdk.CONTROL_MASK): # CTRL + F4
321                                self.remove_tab(jid)
322                elif event.string and event.string in st and \
323                     (event.state & gtk.gdk.MOD1_MASK): # alt + 1,2,3..
324                        self.notebook.set_current_page(st.index(event.string))
325                elif event.keyval == gtk.keysyms.Page_Down:
326                        if event.state & gtk.gdk.CONTROL_MASK: # CTRL + PAGE DOWN
327                                current = self.notebook.get_current_page()
328                                if current > 0:
329                                        self.notebook.set_current_page(current-1)
330                        elif event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE DOWN
331                                conversation_textview = self.xmls[jid].\
332                                        get_widget('conversation_textview')
333                                rect = conversation_textview.get_visible_rect()
334                                iter = conversation_textview.get_iter_at_location(rect.x,\
335                                        rect.y + rect.height)
336                                conversation_textview.scroll_to_iter(iter, 0.1, True, 0, 0)
337                elif event.keyval == gtk.keysyms.Page_Up: 
338                        if event.state & gtk.gdk.CONTROL_MASK: # CTRL + PAGE UP
339                                current = self.notebook.get_current_page()
340                                if current < (self.notebook.get_n_pages()-1):
341                                        self.notebook.set_current_page(current + 1)
342                        elif event.state & gtk.gdk.SHIFT_MASK: # SHIFT + PAGE UP
343                                conversation_textview = self.xmls[jid].\
344                                        get_widget('conversation_textview')
345                                rect = conversation_textview.get_visible_rect()
346                                iter = conversation_textview.get_iter_at_location(rect.x, rect.y)
347                                conversation_textview.scroll_to_iter(iter, 0.1, True, 0, 1)
348                                # or event.keyval == gtk.keysyms.KP_Up
349                elif event.keyval == gtk.keysyms.Up: 
350                        if event.state & gtk.gdk.SHIFT_MASK: # SHIFT + UP
351                                conversation_scrolledwindow = self.xml.get_widget('conversation_scrolledwindow')
352                                conversation_scrolledwindow.emit('scroll-child',
353                                        gtk.SCROLL_PAGE_BACKWARD, False)
354                elif event.keyval == gtk.keysyms.ISO_Left_Tab: # SHIFT + TAB
355                        if (event.state & gtk.gdk.CONTROL_MASK): # CTRL + SHIFT + TAB
356                                current = self.notebook.get_current_page()
357                                if current > 0:
358                                        self.notebook.set_current_page(current - 1)
359                                else:
360                                        self.notebook.set_current_page(self.notebook.get_n_pages()-1)
361                elif event.keyval == gtk.keysyms.Tab: # TAB
362                        if event.state & gtk.gdk.CONTROL_MASK: # CTRL + TAB
363                                current = self.notebook.get_current_page()
364                                if current < (self.notebook.get_n_pages()-1):
365                                        self.notebook.set_current_page(current + 1)
366                                else:
367                                        self.notebook.set_current_page(0)
368                elif event.keyval == gtk.keysyms.v:
369                        if event.state & gtk.gdk.CONTROL_MASK: # CTRL + V
370                                jid = self.get_active_jid()
371                                message_textview = self.xmls[jid].get_widget('message_textview')
372                                if not message_textview.is_focus():
373                                        message_textview.grab_focus()
374                                message_textview.emit('key_press_event', event)
375                elif (event.state & gtk.gdk.CONTROL_MASK) or \
376                     (event.keyval == gtk.keysyms.Control_L) or \
377                     (event.keyval == gtk.keysyms.Control_R):
378                        # we pressed a control key or ctrl+sth: we don't block
379                        # the event in order to let ctrl+c (copy text) and
380                        # others do their default work
381                        pass
382                else: # it's a normal key press make sure message_textview has focus
383                        message_textview = self.xmls[jid].get_widget('message_textview')
384                        if not message_textview.is_focus():
385                                message_textview.grab_focus()
386                        message_textview.emit('key_press_event', event)
387
388        def on_conversation_vadjustment_value_changed(self, widget):
389                jid = self.get_active_jid()
390                if not self.nb_unread[jid]:
391                        return
392                textview = self.xmls[jid].get_widget('conversation_textview')
393                buffer = textview.get_buffer()
394                end_iter = buffer.get_end_iter()
395                end_rect = textview.get_iter_location(end_iter)
396                visible_rect = textview.get_visible_rect()
397                if end_rect.y <= (visible_rect.y + visible_rect.height) and \
398                   self.window.is_active():
399                        #we are at the end
400                        self.nb_unread[jid] = 0
401                        self.redraw_tab(jid)
402                        self.show_title()
403                        if self.plugin.systray_enabled:
404                                self.plugin.systray.remove_jid(jid, self.account)
405       
406        def on_conversation_textview_motion_notify_event(self, widget, event):
407                """change the cursor to a hand when we are on a mail or an url"""
408                x, y, spam = widget.window.get_pointer()
409                x, y = widget.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, x, y)
410                tags = widget.get_iter_at_location(x, y).get_tags()
411                if self.change_cursor:
412                        widget.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
413                                gtk.gdk.Cursor(gtk.gdk.XTERM))
414                        self.change_cursor = None
415                tag_table = widget.get_buffer().get_tag_table()
416                for tag in tags:
417                        if tag == tag_table.lookup('url') or tag == tag_table.lookup('mail'):
418                                widget.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
419                                        gtk.gdk.Cursor(gtk.gdk.HAND2))
420                                self.change_cursor = tag
421                return False
422
423        def on_clear(self, widget, textview):
424                '''clear text in the given textview'''
425                buffer = textview.get_buffer()
426                start, end = buffer.get_bounds()
427                buffer.delete(start, end)
428
429        def on_conversation_textview_populate_popup(self, textview, menu):
430                item = gtk.MenuItem() # seperator
431                menu.prepend(item)
432                item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
433                menu.prepend(item)
434                item.connect('activate', self.on_clear, textview)
435                menu.show_all()
436                       
437        def on_conversation_textview_button_press_event(self, widget, event):
438                # Do not open the standard popup menu, so we block right button
439                # click on a taged text
440
441                if event.button != 3:
442                        return False
443
444                win = widget.get_window(gtk.TEXT_WINDOW_TEXT)
445                x, y = widget.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,\
446                        int(event.x), int(event.y))
447                iter = widget.get_iter_at_location(x, y)
448                tags = iter.get_tags()
449
450                if not tags:
451                        return False
452
453                for tag in tags:
454                        tag_name = tag.get_property('name')
455                        if 'url' in tag_name or 'mail' in tag_name:
456                                return True
457       
458        def print_time_timeout(self, jid):
459                if not jid in self.xmls.keys():
460                        return 0
461                if gajim.config.get('print_time') == 'sometimes':
462                        textview = self.xmls[jid].get_widget('conversation_textview')
463                        buffer = textview.get_buffer()
464                        end_iter = buffer.get_end_iter()
465                        tim = time.localtime()
466                        tim_format = time.strftime('%H:%M', tim)
467                        buffer.insert_with_tags_by_name(end_iter,
468                                                '\n' + tim_format,
469                                                'time_sometimes')
470                        #scroll to the end of the textview
471                        end_rect = textview.get_iter_location(end_iter)
472                        visible_rect = textview.get_visible_rect()
473                        if end_rect.y <= (visible_rect.y + visible_rect.height):
474                                #we are at the end
475                                self.scroll_to_end(textview)
476                        return 1
477                if self.print_time_timeout_id.has_key(jid):
478                        del self.print_time_timeout_id[jid]
479                return 0
480
481        def on_open_link_activated(self, widget, kind, text):
482                self.plugin.launch_browser_mailer(kind, text)
483
484        def on_copy_link_activated(self, widget, text):
485                clip = gtk.clipboard_get()
486                clip.set_text(text)
487
488        def make_link_menu(self, event, kind, text):
489                menu = gtk.Menu()
490                if kind == 'mail':
491                        item = gtk.MenuItem(_('_Open Email Composer'))
492                else:
493                        item = gtk.MenuItem(_('_Open Link'))
494                item.connect('activate', self.on_open_link_activated, kind, text)
495                menu.append(item)
496                if kind == 'mail':
497                        item = gtk.MenuItem(_('_Copy Email Address'))
498                else: # It's an url
499                        item = gtk.MenuItem(_('_Copy Link Address'))
500                item.connect('activate', self.on_copy_link_activated, text)
501                menu.append(item)
502
503                menu.popup(None, None, None, event.button, event.time)
504                menu.show_all()
505                menu.reposition()
506
507        def hyperlink_handler(self, texttag, widget, event, iter, kind):
508                if event.type == gtk.gdk.BUTTON_PRESS:
509                        begin_iter = iter.copy()
510                        #we get the begining of the tag
511                        while not begin_iter.begins_tag(texttag):
512                                begin_iter.backward_char()
513                        end_iter = iter.copy()
514                        #we get the end of the tag
515                        while not end_iter.ends_tag(texttag):
516                                end_iter.forward_char()
517                        word = begin_iter.get_text(end_iter)
518                        if event.button == 3: # right click
519                                self.make_link_menu(event, kind, word)
520                        else:
521                                #we launch the correct application
522                                self.plugin.launch_browser_mailer(kind, word)
523
524        def print_with_tag_list(self, buffer, text, iter, tag_list):
525                begin_mark = buffer.create_mark('begin_tag', iter, True)
526                buffer.insert(iter, text)
527                begin_tagged = buffer.get_iter_at_mark(begin_mark)
528                for tag in tag_list:
529                        buffer.apply_tag_by_name(tag, begin_tagged, iter)
530                buffer.delete_mark(begin_mark)
531
532        def detect_and_print_special_text(self, otext, jid, other_tags,
533                                                print_all_special):
534                textview = self.xmls[jid].get_widget('conversation_textview')
535                buffer = textview.get_buffer()
536               
537                start = 0
538                end = 0
539                index = 0
540               
541                # basic: links + mail + formatting is always checked (we like that)
542                if gajim.config.get('useemoticons'): # search for emoticons & urls
543                        iterator = self.plugin.emot_and_basic_re.finditer(otext)
544                else: # search for just urls + mail + formatting
545                        iterator = self.plugin.basic_pattern_re.finditer(otext)
546                for match in iterator:
547                        start, end = match.