root/trunk/src/chat_control.py

Revision 10728, 93.9 kB (checked in by asterix, 22 hours ago)

fix missing argument

Line 
1# -*- coding:utf-8 -*-
2## src/chat_control.py
3##
4## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
5## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
6##                         Jean-Marie Traissard <jim AT lapin.org>
7##                         Nikos Kouremenos <kourem AT gmail.com>
8##                         Travis Shirk <travis AT pobox.com>
9## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
10##                    Julien Pivotto <roidelapluie AT gmail.com>
11## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
12##                         Stephan Erb <steve-e AT h3c.de>
13## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
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
30import os
31import time
32import gtk
33import pango
34import gobject
35import gtkgui_helpers
36import message_control
37import dialogs
38import history_window
39import notify
40import re
41
42from common import gajim
43from common import helpers
44from common import exceptions
45from message_control import MessageControl
46from conversation_textview import ConversationTextview
47from message_textview import MessageTextView
48from common.contacts import GC_Contact
49from common.logger import Constants
50constants = Constants()
51from common.pep import MOODS, ACTIVITIES
52from common.xmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC
53from common.xmpp.protocol import NS_RECEIPTS, NS_ESESSION
54
55try:
56        import gtkspell
57        HAS_GTK_SPELL = True
58except Exception:
59        HAS_GTK_SPELL = False
60
61HAVE_MARKUP_TOOLTIPS = gtk.pygtk_version >= (2, 12, 0)
62
63# the next script, executed in the "po" directory,
64# generates the following list.
65##!/bin/sh
66#LANG=$(for i in *.po; do j=${i/.po/}; echo -n "_('"$j"')":" '"$j"', " ; done)
67#echo "{_('en'):'en'",$LANG"}"
68langs = {_('English'): 'en', _('Belarusian'): 'be', _('Bulgarian'): 'bg', _('Breton'): 'br', _('Czech'): 'cs', _('German'): 'de', _('Greek'): 'el', _('British'): 'en_GB', _('Esperanto'): 'eo', _('Spanish'): 'es', _('Basque'): 'eu', _('French'): 'fr', _('Croatian'): 'hr', _('Italian'): 'it', _('Norwegian (b)'): 'nb', _('Dutch'): 'nl', _('Norwegian'): 'no', _('Polish'): 'pl', _('Portuguese'): 'pt', _('Brazilian Portuguese'): 'pt_BR', _('Russian'): 'ru', _('Serbian'): 'sr', _('Slovak'): 'sk', _('Swedish'): 'sv', _('Chinese (Ch)'): 'zh_CN'}
69
70################################################################################
71class ChatControlBase(MessageControl):
72        '''A base class containing a banner, ConversationTextview, MessageTextView
73        '''
74        def make_href(self, match):
75                url_color = gajim.config.get('urlmsgcolor')
76                return '<a href="%s"><span color="%s">%s</span></a>' % (match.group(),
77                        url_color, match.group())
78
79        def get_font_attrs(self):
80                ''' get pango font attributes for banner from theme settings '''
81                theme = gajim.config.get('roster_theme')
82                bannerfont = gajim.config.get_per('themes', theme, 'bannerfont')
83                bannerfontattrs = gajim.config.get_per('themes', theme, 'bannerfontattrs')
84
85                if bannerfont:
86                        font = pango.FontDescription(bannerfont)
87                else:
88                        font = pango.FontDescription('Normal')
89                if bannerfontattrs:
90                        # B attribute is set by default
91                        if 'B' in bannerfontattrs:
92                                font.set_weight(pango.WEIGHT_HEAVY)
93                        if 'I' in bannerfontattrs:
94                                font.set_style(pango.STYLE_ITALIC)
95
96                font_attrs = 'font_desc="%s"' % font.to_string()
97
98                # in case there is no font specified we use x-large font size
99                if font.get_size() == 0:
100                        font_attrs = '%s size="x-large"' % font_attrs
101                font.set_weight(pango.WEIGHT_NORMAL)
102                font_attrs_small = 'font_desc="%s" size="small"' % font.to_string()
103                return (font_attrs, font_attrs_small)
104
105        def get_nb_unread(self):
106                jid = self.contact.jid
107                if self.resource:
108                        jid += '/' + self.resource
109                type_ = self.type_id
110                return len(gajim.events.get_events(self.account, jid, ['printed_' + type_,
111                        type_]))
112
113        def draw_banner(self):
114                '''Draw the fat line at the top of the window that
115                houses the icon, jid, ...
116                '''
117                self.draw_banner_text()
118                self._update_banner_state_image()
119                # Derived types MAY implement this
120
121        def draw_banner_text(self):
122                pass # Derived types SHOULD implement this
123
124        def update_ui(self):
125                self.draw_banner()
126                # Derived types SHOULD implement this
127
128        def repaint_themed_widgets(self):
129                self._paint_banner()
130                self.draw_banner()
131                # Derived classes MAY implement this
132
133        def _update_banner_state_image(self):
134                pass # Derived types MAY implement this
135
136        def handle_message_textview_mykey_press(self, widget, event_keyval,
137        event_keymod):
138                pass # Derived should implement this rather than connecting to the event itself.
139
140        def status_url_clicked(self, widget, url):
141                helpers.launch_browser_mailer('url', url)
142
143        def __init__(self, type_id, parent_win, widget_name, contact, acct,
144        resource = None):
145                if resource is None:
146                        # We very likely got a contact with a random resource.
147                        # This is bad, we need the highest for caps etc.
148                        c = gajim.contacts.get_contact_with_highest_priority(
149                                acct, contact.jid)
150                        if c and not isinstance(c, GC_Contact):
151                                contact = c
152
153                MessageControl.__init__(self, type_id, parent_win, widget_name,
154                        contact, acct, resource = resource);
155
156                widget = self.xml.get_widget('history_button')
157                id = widget.connect('clicked', self._on_history_menuitem_activate)
158                self.handlers[id] = widget
159
160                # when/if we do XHTML we will put formatting buttons back
161                widget = self.xml.get_widget('emoticons_button')
162                id = widget.connect('clicked', self.on_emoticons_button_clicked)
163                self.handlers[id] = widget
164
165                # Create banner and connect signals
166                widget = self.xml.get_widget('banner_eventbox')
167                widget.set_property('height-request', gajim.config.get('chat_avatar_height'))
168                id = widget.connect('button-press-event',
169                        self._on_banner_eventbox_button_press_event)
170                self.handlers[id] = widget
171
172                self.urlfinder = re.compile(r"(www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'\"]+[^!,\.\s<>\)'\"\]]")
173
174                if gajim.HAVE_PYSEXY:
175                        import sexy
176                        self.banner_status_label = sexy.UrlLabel()
177                        self.banner_status_label.connect('url_activated', self.status_url_clicked)
178                else:
179                        self.banner_status_label = gtk.Label()
180                self.banner_status_label.set_selectable(True)
181                self.banner_status_label.set_alignment(0,0.5)
182
183                banner_vbox = self.xml.get_widget('banner_vbox')
184                banner_vbox.pack_start(self.banner_status_label)
185                self.banner_status_label.show()
186
187                # Init DND
188                self.TARGET_TYPE_URI_LIST = 80
189                self.dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ),
190                                ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_APP, 0)]
191                id = self.widget.connect('drag_data_received',
192                        self._on_drag_data_received)
193                self.handlers[id] = self.widget
194                self.widget.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
195                        gtk.DEST_DEFAULT_HIGHLIGHT |
196                        gtk.DEST_DEFAULT_DROP,
197                        self.dnd_list, gtk.gdk.ACTION_COPY)
198
199                # Create textviews and connect signals
200                self.conv_textview = ConversationTextview(self.account)
201                id = self.conv_textview.tv.connect('key_press_event',
202                        self._conv_textview_key_press_event)
203                self.handlers[id] = self.conv_textview.tv
204                # FIXME: DND on non editable TextView, find a better way
205                self.drag_entered = False
206                id = self.conv_textview.tv.connect('drag_data_received',
207                        self._on_drag_data_received)
208                self.handlers[id] = self.conv_textview.tv
209                id = self.conv_textview.tv.connect('drag_motion', self._on_drag_motion)
210                self.handlers[id] = self.conv_textview.tv
211                id = self.conv_textview.tv.connect('drag_leave', self._on_drag_leave)
212                self.handlers[id] = self.conv_textview.tv
213                self.conv_textview.tv.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
214                        gtk.DEST_DEFAULT_HIGHLIGHT |
215                        gtk.DEST_DEFAULT_DROP,
216                        self.dnd_list, gtk.gdk.ACTION_COPY)
217
218                self.conv_scrolledwindow = self.xml.get_widget(
219                        'conversation_scrolledwindow')
220                self.conv_scrolledwindow.add(self.conv_textview.tv)
221                widget = self.conv_scrolledwindow.get_vadjustment()
222                id = widget.connect('value-changed',
223                        self.on_conversation_vadjustment_value_changed)
224                self.handlers[id] = widget
225                id = widget.connect('changed',
226                        self.on_conversation_vadjustment_changed)
227                self.handlers[id] = widget
228                self.scroll_to_end_id = None
229                self.was_at_the_end = True
230
231                # add MessageTextView to UI and connect signals
232                self.msg_scrolledwindow = self.xml.get_widget('message_scrolledwindow')
233                self.msg_textview = MessageTextView()
234                id = self.msg_textview.connect('mykeypress',
235                        self._on_message_textview_mykeypress_event)
236                self.handlers[id] = self.msg_textview
237                self.msg_scrolledwindow.add(self.msg_textview)
238                id = self.msg_textview.connect('key_press_event',
239                        self._on_message_textview_key_press_event)
240                self.handlers[id] = self.msg_textview
241                id = self.msg_textview.connect('size-request', self.size_request)
242                self.handlers[id] = self.msg_textview
243                id = self.msg_textview.connect('populate_popup',
244                        self.on_msg_textview_populate_popup)
245                self.handlers[id] = self.msg_textview
246                # Setup DND
247                id = self.msg_textview.connect('drag_data_received',
248                        self._on_drag_data_received)
249                self.handlers[id] = self.msg_textview
250                self.msg_textview.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
251                        gtk.DEST_DEFAULT_HIGHLIGHT,
252                        self.dnd_list, gtk.gdk.ACTION_COPY)
253
254                self.update_font()
255
256                # Hook up send button
257                widget = self.xml.get_widget('send_button')
258                id = widget.connect('clicked', self._on_send_button_clicked)
259                self.handlers[id] = widget
260
261                widget = self.xml.get_widget('formattings_button')
262                id = widget.connect('clicked', self.on_formattings_button_clicked)
263                self.handlers[id] = widget
264
265                # the following vars are used to keep history of user's messages
266                self.sent_history = []
267                self.sent_history_pos = 0
268                self.orig_msg = None
269
270                # Emoticons menu
271                # set image no matter if user wants at this time emoticons or not
272                # (so toggle works ok)
273                img = self.xml.get_widget('emoticons_button_image')
274                img.set_from_file(os.path.join(gajim.DATA_DIR, 'emoticons', 'static',
275                        'smile.png'))
276                self.toggle_emoticons()
277
278                # Attach speller
279                if gajim.config.get('use_speller') and HAS_GTK_SPELL:
280                        try:
281                                spell = gtkspell.Spell(self.msg_textview)
282                                # loop removing non-existant dictionaries
283                                # iterating on a copy
284                                for lang in dict(langs):
285                                        try:
286                                                spell.set_language(langs[lang])
287                                        except Exception:
288                                                del langs[lang]
289                                # now set the one the user selected
290                                per_type = 'contacts'
291                                if self.type_id == message_control.TYPE_GC:
292                                        per_type = 'rooms'
293                                lang = gajim.config.get_per(per_type, self.contact.jid,
294                                        'speller_language')
295                                if not lang:
296                                        # use the default one
297                                        lang = gajim.config.get('speller_language')
298                                if lang:
299                                        self.msg_textview.lang = lang
300                                        spell.set_language(lang)
301                        except (gobject.GError, RuntimeError), msg:
302                                dialogs.AspellDictError(lang)
303                self.conv_textview.tv.show()
304                self._paint_banner()
305
306                # For XEP-0172
307                self.user_nick = None
308
309                self.smooth = True
310                self.msg_textview.grab_focus()
311
312        def on_msg_textview_populate_popup(self, textview, menu):
313                '''we override the default context menu and we prepend an option to switch languages'''
314                def _on_select_dictionary(widget, lang):
315                        per_type = 'contacts'
316                        if self.type_id == message_control.TYPE_GC:
317                                per_type = 'rooms'
318                        if not gajim.config.get_per(per_type, self.contact.jid):
319                                gajim.config.add_per(per_type, self.contact.jid)
320                        gajim.config.set_per(per_type, self.contact.jid, 'speller_language',
321                                lang)
322                        spell = gtkspell.get_from_text_view(self.msg_textview)
323                        self.msg_textview.lang = lang
324                        spell.set_language(lang)
325                        widget.set_active(True)
326
327                item = gtk.SeparatorMenuItem()
328                menu.prepend(item)
329
330                item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
331                menu.prepend(item)
332                id = item.connect('activate', self.msg_textview.clear)
333                self.handlers[id] = item
334
335                if gajim.config.get('use_speller') and HAS_GTK_SPELL:
336                        item = gtk.MenuItem(_('Spelling language'))
337                        menu.prepend(item)
338                        submenu = gtk.Menu()
339                        item.set_submenu(submenu)
340                        for lang in sorted(langs):
341                                item = gtk.CheckMenuItem(lang)
342                                if langs[lang] == self.msg_textview.lang:
343                                        item.set_active(True)
344                                submenu.append(item)
345                                id = item.connect('activate', _on_select_dictionary, langs[lang])
346                                self.handlers[id] = item
347
348                menu.show_all()
349
350        # moved from ChatControl
351        def _on_banner_eventbox_button_press_event(self, widget, event):
352                '''If right-clicked, show popup'''
353                if event.button == 3: # right click
354                        self.parent_win.popup_menu(event)
355
356        def _on_send_button_clicked(self, widget):
357                '''When send button is pressed: send the current message'''
358                if gajim.connections[self.account].connected < 2: # we are not connected
359                        dialogs.ErrorDialog(_('A connection is not available'),
360                                _('Your message can not be sent until you are connected.'))
361                        return
362                message_buffer = self.msg_textview.get_buffer()
363                start_iter = message_buffer.get_start_iter()
364                end_iter = message_buffer.get_end_iter()
365                message = message_buffer.get_text(start_iter, end_iter, 0).decode('utf-8')
366                xhtml = self.msg_textview.get_xhtml()
367
368                # send the message
369                self.send_message(message, xhtml=xhtml)
370
371        def _paint_banner(self):
372                '''Repaint banner with theme color'''
373                theme = gajim.config.get('roster_theme')
374                bgcolor = gajim.config.get_per('themes', theme, 'bannerbgcolor')
375                textcolor = gajim.config.get_per('themes', theme, 'bannertextcolor')
376                # the backgrounds are colored by using an eventbox by
377                # setting the bg color of the eventbox and the fg of the name_label
378                banner_eventbox = self.xml.get_widget('banner_eventbox')
379                banner_name_label = self.xml.get_widget('banner_name_label')
380                self.disconnect_style_event(banner_name_label)
381                self.disconnect_style_event(self.banner_status_label)
382                if bgcolor:
383                        banner_eventbox.modify_bg(gtk.STATE_NORMAL,
384                                gtk.gdk.color_parse(bgcolor))
385                        default_bg = False
386                else:
387                        default_bg = True
388                if textcolor:
389                        banner_name_label.modify_fg(gtk.STATE_NORMAL,
390                                gtk.gdk.color_parse(textcolor))
391                        self.banner_status_label.modify_fg(gtk.STATE_NORMAL,
392                                gtk.gdk.color_parse(textcolor))
393                        default_fg = False
394                else:
395                        default_fg = True
396                if default_bg or default_fg:
397                        self._on_style_set_event(banner_name_label, None, default_fg,
398                                default_bg)
399                        self._on_style_set_event(self.banner_status_label, None, default_fg,
400                                default_bg)
401
402        def disconnect_style_event(self, widget):
403                # Try to find the event_id
404                found = False
405                for id in self.handlers:
406                        if self.handlers[id] == widget:
407                                found = True
408                                break
409                if found:
410                        widget.disconnect(id)
411                        del self.handlers[id]
412
413        def connect_style_event(self, widget, set_fg = False, set_bg = False):
414                self.disconnect_style_event(widget)
415                id = widget.connect('style-set', self._on_style_set_event, set_fg, set_bg)
416                self.handlers[id] = widget
417
418        def _on_style_set_event(self, widget, style, *opts):
419                '''set style of widget from style class *.Frame.Eventbox
420                        opts[0] == True -> set fg color
421                        opts[1] == True -> set bg color'''
422                banner_eventbox = self.xml.get_widget('banner_eventbox')
423                self.disconnect_style_event(widget)
424                if opts[1]:
425                        bg_color = widget.style.bg[gtk.STATE_SELECTED]
426                        banner_eventbox.modify_bg(gtk.STATE_NORMAL, bg_color)
427                if opts[0]:
428                        fg_color = widget.style.fg[gtk.STATE_SELECTED]
429                        widget.modify_fg(gtk.STATE_NORMAL, fg_color)
430                self.connect_style_event(widget, opts[0], opts[1])
431
432        def _conv_textview_key_press_event(self, widget, event):
433                if gtk.gtk_version < (2, 12, 0):
434                        return
435                if event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.CONTROL_MASK):
436                        return False
437                self.parent_win.notebook.emit('key_press_event', event)
438
439        def show_emoticons_menu(self):
440                if not gajim.config.get('emoticons_theme'):
441                        return
442                msg_tv = self.msg_textview
443                def set_emoticons_menu_position(w, msg_tv = self.msg_textview):
444                        window = msg_tv.get_window(gtk.TEXT_WINDOW_WIDGET)
445                        # get the window position
446                        origin = window.get_origin()
447                        size = window.get_size()
448                        buf = msg_tv.get_buffer()
449                        # get the cursor position
450                        cursor = msg_tv.get_iter_location(buf.get_iter_at_mark(
451                                buf.get_insert()))
452                        cursor = msg_tv.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT,
453                                cursor.x, cursor.y)
454                        x = origin[0] + cursor[0]
455                        y = origin[1] + size[1]
456                        menu_width, menu_height = gajim.interface.emoticons_menu.size_request()
457                        #FIXME: get_line_count is not so good
458                        #get the iter of cursor, then tv.get_line_yrange
459                        # so we know in which y we are typing (not how many lines we have
460                        # then go show just above the current cursor line for up
461                        # or just below the current cursor line for down
462                        #TEST with having 3 lines and writing in the 2nd
463                        if y + menu_height > gtk.gdk.screen_height():
464                                # move menu just above cursor
465                                y -= menu_height + (msg_tv.allocation.height / buf.get_line_count())
466                        #else: # move menu just below cursor
467                        #       y -= (msg_tv.allocation.height / buf.get_line_count())
468                        return (x, y, True) # push_in True
469                gajim.interface.emoticon_menuitem_clicked = self.append_emoticon
470                gajim.interface.emoticons_menu.popup(None, None,
471                        set_emoticons_menu_position, 1, 0)
472
473        def _on_message_textview_key_press_event(self, widget, event):
474                if self.widget_name == 'muc_child_vbox':
475                        if event.keyval not in (gtk.keysyms.ISO_Left_Tab, gtk.keysyms.Tab):
476                                self.last_key_tabs = False
477                if event.state & gtk.gdk.SHIFT_MASK:
478                        # CTRL + SHIFT + TAB
479                        if event.state & gtk.gdk.CONTROL_MASK and \
480                                        event.keyval == gtk.keysyms.ISO_Left_Tab:
481                                self.parent_win.move_to_next_unread_tab(False)
482                                return True
483                        # SHIFT + PAGE_[UP|DOWN]: send to conv_textview
484                        elif event.keyval == gtk.keysyms.Page_Down or \
485                                        event.keyval == gtk.keysyms.Page_Up:
486                                self.conv_textview.tv.emit('key_press_event', event)
487                                return True
488                elif event.state & gtk.gdk.CONTROL_MASK:
489                        if event.keyval == gtk.keysyms.Tab: # CTRL + TAB
490                                self.parent_win.move_to_next_unread_tab(True)
491                                return True
492                        # CTRL + PAGE_[UP|DOWN]: send to parent notebook
493                        elif event.keyval == gtk.keysyms.Page_Down or \
494                                        event.keyval == gtk.keysyms.Page_Up:
495                                self.parent_win.notebook.emit('key_press_event', event)
496                                return True
497                        # we pressed a control key or ctrl+sth: we don't block
498                        # the event in order to let ctrl+c (copy text) and
499                        # others do their default work
500                        self.conv_textview.tv.emit('key_press_event', event)
501                return False
502
503        def _on_message_textview_mykeypress_event(self, widget, event_keyval,
504                event_keymod):
505                '''When a key is pressed:
506                if enter is pressed without the shift key, message (if not empty) is sent
507                and printed in the conversation'''
508
509                # NOTE: handles mykeypress which is custom signal connected to this
510                # CB in new_tab(). for this singal see message_textview.py
511                message_textview = widget
512                message_buffer = message_textview.get_buffer()
513                start_iter, end_iter = message_buffer.get_bounds()
514                message = message_buffer.get_text(start_iter, end_iter, False).decode(
515                        'utf-8')
516                xhtml = self.msg_textview.get_xhtml() 
517
518                # construct event instance from binding
519                event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) # it's always a key-press here
520                event.keyval = event_keyval
521                event.state = event_keymod
522                event.time = 0 # assign current time
523
524                if event.keyval == gtk.keysyms.Up:
525                        if event.state & gtk.gdk.CONTROL_MASK: # Ctrl+UP
526                                self.sent_messages_scroll('up', widget.get_buffer())
527                elif event.keyval == gtk.keysyms.Down:
528                        if event.state & gtk.gdk.CONTROL_MASK: # Ctrl+Down
529                                self.sent_messages_scroll('down', widget.get_buffer())
530                elif event.keyval == gtk.keysyms.Return or \
531                        event.keyval == gtk.keysyms.KP_Enter: # ENTER
532                        # NOTE: SHIFT + ENTER is not needed to be emulated as it is not
533                        # binding at all (textview's default action is newline)
534
535                        if gajim.config.get('send_on_ctrl_enter'):
536                                # here, we emulate GTK default action on ENTER (add new line)
537                                # normally I would add in keypress but it gets way to complex
538                                # to get instant result on changing this advanced setting
539                                if event.state == 0: # no ctrl, no shift just ENTER add newline
540                                        end_iter = message_buffer.get_end_iter()
541                                        message_buffer.insert_at_cursor('\n')
542                                        send_message = False
543                                elif event.state & gtk.gdk.CONTROL_MASK: # CTRL + ENTER
544                                        send_message = True
545                        else: # send on Enter, do newline on Ctrl Enter
546                                if event.state & gtk.gdk.CONTROL_MASK: # Ctrl + ENTER
547                                        end_iter = message_buffer.get_end_iter()
548                                        message_buffer.insert_at_cursor('\n')
549                                        send_message = False
550                                else: # ENTER
551                                        send_message = True
552
553                        if gajim.connections[self.account].connected < 2 and send_message:
554                                # we are not connected
555                                dialogs.ErrorDialog(_('A connection is not available'),
556                                        _('Your message can not be sent until you are connected.'))
557                                send_message = False
558
559                        if send_message:
560                                self.send_message(message, xhtml=xhtml) # send the message
561                else:
562                        # Give the control itself a chance to process
563                        self.handle_message_textview_mykey_press(widget, event_keyval,
564                                event_keymod)
565
566        def _on_drag_data_received(self, widget, context, x, y, selection,
567                target_type, timestamp):
568                pass # Derived classes SHOULD implement this method
569
570        def _on_drag_leave(self, widget, context, time):
571                # FIXME: DND on non editable TextView, find a better way
572                self.drag_entered = False
573                self.conv_textview.tv.set_editable(False)
574
575        def _on_drag_motion(self, widget, context, x, y, time):
576                # FIXME: DND on non editable TextView, find a better way
577                if not self.drag_entered:
578                        # We drag new data over the TextView, make it editable to catch dnd
579                        self.drag_entered_conv = True
580                        self.conv_textview.tv.set_editable(True