clickable_nicknames.py 5.93 KB
Newer Older
Dicson's avatar
Dicson committed
1 2
from gi.repository import Gtk
from gi.repository import Gdk
Dicson's avatar
Dicson committed
3

4 5 6
from gajim.common import app
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log_calls
Philipp Hörist's avatar
Philipp Hörist committed
7
from gajim.plugins.plugins_i18n import _
8

Dicson's avatar
Dicson committed
9 10

class ClickableNicknames(GajimPlugin):
11

Dicson's avatar
Dicson committed
12 13
    @log_calls('ClickableNicknamesPlugin')
    def init(self):
Dicson's avatar
Dicson committed
14 15
        self.description = _('Clickable nicknames '
            'in the conversation textview.')
16
        self.config_dialog = None 
Dicson's avatar
Dicson committed
17
        self.gui_extension_points = {
Dicson's avatar
Dicson committed
18
                'chat_control_base': (self.connect_with_chat_control,
Dicson's avatar
Dicson committed
19 20 21 22 23 24
                                       self.disconnect_from_chat_control)}

        self.is_active = None
        self.gc_controls = {}

        self.tag_names = []
25
        colors = app.config.get('gc_nicknames_colors')
Dicson's avatar
Dicson committed
26 27 28 29 30 31 32
        colors = colors.split(':')
        for i, color in enumerate(colors):
            tagname = 'gc_nickname_color_' + str(i)
            self.tag_names.append(tagname)

    @log_calls('ClickableNicknamesPlugin')
    def activate(self):
33
        for gc_control in app.interface.msg_win_mgr.get_controls('gc'):
Dicson's avatar
Dicson committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
            # TODO support minimized groupchat
            if gc_control not in self.gc_controls.keys():
                control = Base(self, gc_control)
                self.gc_controls[gc_control] = control
            else:
                self.gc_controls[gc_control].connect_signals()
        self.is_active = True

    @log_calls('ClickableNicknamesPlugin')
    def deactivate(self):
        for control in self.gc_controls.keys():
            self.gc_controls[control].disconnect_from_chat_control()
        self.gc_controls.clear()
        self.is_active = None

    @log_calls('ClickableNicknamesPlugin')
    def connect_with_chat_control(self, chat_control):
        if chat_control.widget_name != 'groupchat_control':
            return
        if self.is_active:
            control = Base(self, chat_control)
            self.gc_controls[chat_control] = control

    @log_calls('ClickableNicknamesPlugin')
    def disconnect_from_chat_control(self, chat_control):
        pass

Dicson's avatar
Dicson committed
61

Dicson's avatar
Dicson committed
62
class Base(object):
Dicson's avatar
Dicson committed
63

Dicson's avatar
Dicson committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    def __init__(self, plugin, chat_control):
        self.plugin = plugin
        self.chat_control = chat_control
        self.textview = self.chat_control.conv_textview
        self.tags_id = []
        self.change_cursor = False
        self.connect_signals()

    def connect_signals(self):
        # connect signals with textbuffer tags
        self.tag_names = self.plugin.tag_names
        tag_table = self.textview.tv.get_buffer().get_tag_table()
        for name in self.tag_names:
            tag = tag_table.lookup(name)
            if tag:
                id_ = tag.connect('event', self.insert_nick, name)
                self.chat_control.handlers[id_] = tag
                self.tags_id.append((id_, tag))

        self.id_ = self.textview.tv.connect('motion_notify_event',
                self.on_textview_motion_notify_event)
        self.chat_control.handlers[self.id_] = self.textview.tv

    def on_textview_motion_notify_event(self, widget, event):
        # change cursor on the nicks
Dicson's avatar
Dicson committed
89 90 91 92
        pointer_x, pointer_y = self.textview.tv.get_window(
            Gtk.TextWindowType.TEXT).get_pointer()[1:3]
        x, y = self.textview.tv.window_to_buffer_coords(Gtk.TextWindowType.TEXT,
            pointer_x, pointer_y)
Yann Leboulanger's avatar
Yann Leboulanger committed
93
        tags = self.textview.tv.get_iter_at_location(x, y)[1].get_tags()
Dicson's avatar
Dicson committed
94 95
        tag_table = self.textview.tv.get_buffer().get_tag_table()
        if self.change_cursor:
Dicson's avatar
Dicson committed
96 97
            self.textview.tv.get_window(Gtk.TextWindowType.TEXT).set_cursor(
                    Gdk.Cursor.new(Gdk.CursorType.XTERM))
Dicson's avatar
Dicson committed
98 99 100
            self.change_cursor = False
        for tag in tags:
            if tag in [x[1] for x in self.tags_id]:
Dicson's avatar
Dicson committed
101 102
                self.textview.tv.get_window(Gtk.TextWindowType.TEXT).set_cursor(
                    Gdk.Cursor.new(Gdk.CursorType.HAND2))
Dicson's avatar
Dicson committed
103 104 105 106
            self.change_cursor = True

    def insert_nick(self, texttag, widget, event, iter_, kind):
        # insert nickname to message buffer
Dicson's avatar
Dicson committed
107
        if event.type == Gdk.EventType.BUTTON_PRESS and event.button.button == 1:
Dicson's avatar
Dicson committed
108 109
            # left mouse button clicked
            begin_iter = iter_.copy()
Alexander Krotov's avatar
Alexander Krotov committed
110
            # we get the beginning of the tag
Dicson's avatar
Dicson committed
111 112 113 114 115 116 117
            while not begin_iter.begins_tag(texttag):
                begin_iter.backward_char()
            end_iter = iter_.copy()
            # we get the end of the tag
            while not end_iter.ends_tag(texttag):
                end_iter.forward_char()
            buffer_ = self.textview.tv.get_buffer()
Dicson's avatar
Dicson committed
118
            word = buffer_.get_text(begin_iter, end_iter, True)
119
            nick = word.rstrip().rstrip(app.config.get('after_nickname'))
120
            if nick.startswith('* '):
Yann Leboulanger's avatar
Yann Leboulanger committed
121
                nick = nick.lstrip('* ').split(' ')[0] + ' '
122 123
            nick = nick.lstrip(app.config.get('before_nickname'))
            nicks = app.contacts.get_nick_list(self.chat_control.account,
124
                self.chat_control.room_jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
125
            if nick[:-1] not in nicks:
126
                return
127

Dicson's avatar
Dicson committed
128
            message_buffer = self.chat_control.msg_textview.get_buffer()
Yann Leboulanger's avatar
Yann Leboulanger committed
129
            # delete PLACEHOLDER
130
            start, end = message_buffer.get_bounds()
Yann Leboulanger's avatar
Yann Leboulanger committed
131
            placeholder = self.chat_control.msg_textview.PLACEHOLDER
132 133
            text = message_buffer.get_text(start, end, False)
            if text == placeholder or len(text) == 0:
Yann Leboulanger's avatar
Yann Leboulanger committed
134
                message_buffer.set_text('', -1)
135 136 137 138
                nick = nick + app.config.get('gc_refer_to_nick_char')
            elif len(text) > 0 and text[-1] != ' ':
                nick = ' ' + nick
            nick += ' '
Dicson's avatar
Dicson committed
139 140 141 142 143 144 145 146 147 148
            message_buffer.insert_at_cursor(nick)
            self.chat_control.msg_textview.grab_focus()

    def disconnect_from_chat_control(self):
        # disconnect signals from textbuffer tags
        for item in self.tags_id:
            if item[1].handler_is_connected(item[0]):
                item[1].disconnect(item[0])
        if self.textview.tv.handler_is_connected(self.id_):
            self.textview.tv.disconnect(self.id_)