triggers.py 30.6 KB
Newer Older
1 2 3 4
# -*- coding: utf-8 -*-
#
## plugins/triggers/triggers.py
##
5
## Copyright (C) 2011-2017 Yann Leboulanger <asterix AT lagaule.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##

22

23
import os
24

25 26
from gi.repository import Gtk

27
from gajim.common import app
28 29
from gajim.common import ged
from gajim.common import helpers
30 31
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log_calls
32
from gajim.plugins.helpers import get_builder
33
from gajim.plugins.gui import GajimPluginConfigDialog
34

35 36 37 38 39 40
# Since Gajim 1.1.0 _() has to be imported
try:
    from gajim.common.i18n import _
except ImportError:
    pass

41
try:
42
    from gajim.gtk.filechoosers import NativeFileChooserDialog, Filter
43 44 45 46 47 48 49 50 51 52 53 54

    NEW_FILECHOOSER = True

    class SoundChooserDialog(NativeFileChooserDialog):

        _title = _('Choose Sound')
        _filters = [Filter(_('All files'), '*', False),
                    Filter(_('WAV files'), '*.wav', True)]

except ImportError:
    from gajim.dialogs import SoundChooserDialog
    NEW_FILECHOOSER = False
55 56 57 58 59


class Triggers(GajimPlugin):
    @log_calls('TriggersPlugin')
    def init(self):
60
        self.description = _('Configure Gajim\'s behaviour with conditions for each contact')
61 62 63 64 65 66
        self.config_dialog = TriggersPluginConfigDialog(self)
        self.config_default_values = {}

        self.events_handlers = {'notification': (ged.PREGUI, self._nec_notif),
            'decrypted-message-received': (ged.PREGUI2,
            self._nec_decrypted_message_received),
67
            'gc-message-received': (ged.PREGUI2, self._nec_gc_message_received),
68 69 70 71
            'presence-received': (ged.PREGUI, self._nec_presence_received)}

    def _check_rule_recipients(self, obj, rule):
        rule_recipients = [t.strip() for t in rule['recipients'].split(',')]
72 73 74 75
        if rule['recipient_type'] == 'groupchat':
            if obj.jid in rule_recipients:
                return True
            return False
76 77 78
        if rule['recipient_type'] == 'contact' and obj.jid not in \
        rule_recipients:
            return False
79 80
        contact = app.contacts.get_first_contact_from_jid(obj.conn.name,
            obj.jid)
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
        if not contact:  # PM?
            return False
        contact_groups = contact.groups
        group_found = False
        for group in contact_groups:
            if group in rule_recipients:
                group_found = True
                break
        if rule['recipient_type'] == 'group' and not group_found:
            return False

        return True

    def _check_rule_status(self, obj, rule):
        rule_statuses = rule['status'].split()
96
        our_status = app.SHOW_LIST[obj.conn.connected]
97 98 99 100 101 102 103 104 105
        if rule['status'] != 'all' and our_status not in rule_statuses:
            return False

        return True

    def _check_rule_tab_opened(self, obj, rule):
        if rule['tab_opened'] == 'both':
            return True
        tab_opened = False
106
        if app.interface.msg_win_mgr.get_control(obj.jid, obj.conn.name):
107 108 109 110 111 112 113 114
            tab_opened = True
        if tab_opened and rule['tab_opened'] == 'no':
            return False
        elif not tab_opened and rule['tab_opened'] == 'yes':
            return False

        return True

115 116 117 118 119 120 121 122 123 124
    def _check_rule_has_focus(self, obj, rule):
        if rule['has_focus'] == 'both':
            return True
        if rule['tab_opened'] == 'no':
            # Does not apply in this case
            return True
        ctrl = app.interface.msg_win_mgr.get_control(obj.jid, obj.conn.name)
        if not ctrl:
            # Does not apply in this case
            return True
125
        has_focus = ctrl.parent_win.window.has_focus()
126 127 128 129 130 131 132
        if has_focus and rule['has_focus'] == 'no':
            return False
        elif not has_focus and rule['has_focus'] == 'yes':
            return False

        return True

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    def check_rule_all(self, event, obj, rule):
        # Check notification type
        if rule['event'] != event:
            return False

        # notification type is ok. Now check recipient
        if not self._check_rule_recipients(obj, rule):
            return False

        # recipient is ok. Now check our status
        if not self._check_rule_status(obj, rule):
            return False

        # our_status is ok. Now check opened chat window
        if not self._check_rule_tab_opened(obj, rule):
            return False

150 151 152 153
        # tab_opened is ok. Now check opened chat window
        if not self._check_rule_has_focus(obj, rule):
            return False

154 155 156 157 158 159
        # All is ok
        return True

    def check_rule_apply_notif(self, obj, rule):
        # Check notification type
        notif_type = ''
160
        if obj.notif_type in ('msg', 'gc-msg'):
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
            notif_type = 'message_received'
        elif obj.notif_type == 'pres':
            if obj.base_event.old_show < 2 and obj.base_event.new_show > 1:
                notif_type = 'contact_connected'
            elif obj.base_event.old_show > 1 and obj.base_event.new_show < 2:
                notif_type = 'contact_disconnected'
            else:
                notif_type = 'contact_status_change'

        return self.check_rule_all(notif_type, obj, rule)

    def check_rule_apply_decrypted_msg(self, obj, rule):
        return self.check_rule_all('message_received', obj, rule)

    def check_rule_apply_connected(self, obj, rule):
        return self.check_rule_all('contact_connected', obj, rule)

    def check_rule_apply_disconnected(self, obj, rule):
        return self.check_rule_all('contact_disconnected', obj, rule)

    def check_rule_apply_status_changed(self, obj, rule):
        return self.check_rule_all('contact_status_change', obj, rule)

    def apply_rule_notif(self, obj, rule):
        if rule['sound'] == 'no':
            obj.do_sound = False
        elif rule['sound'] == 'yes':
            obj.do_sound = True
            obj.sound_event = ''
            obj.sound_file = rule['sound_file']

192
        if rule['popup'] == 'no' or obj.control_focused:
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
            obj.do_popup = False
        elif rule['popup'] == 'yes':
            obj.do_popup = True

        if rule['run_command']:
            obj.do_command = True
            obj.command = rule['command']
        else:
            obj.do_command = False

        if rule['systray'] == 'no':
            obj.show_in_notification_area = False
        elif rule['systray'] == 'yes':
            obj.show_in_notification_area = True

        if rule['roster'] == 'no':
            obj.show_in_roster = False
        elif rule['roster'] == 'yes':
            obj.show_in_roster = True

#        if rule['urgency_hint'] == 'no':
#            ?? not in obj actions
#        elif rule['urgency_hint'] == 'yes':

    def apply_rule_decrypted_message(self, obj, rule):
        if rule['auto_open'] == 'no':
            obj.popup = False
        elif rule['auto_open'] == 'yes':
            obj.popup = True

    def apply_rule_presence(self, obj, rule):
        if rule['auto_open'] == 'no':
            obj.popup = False
        elif rule['auto_open'] == 'yes':
            obj.popup = True

    def _nec_all(self, obj, check_func, apply_func):
        # check rules in order
        rules_num = [int(i) for i in self.config.keys()]
        rules_num.sort()
233
        to_remove = []
234 235 236 237
        for num in rules_num:
            rule = self.config[str(num)]
            if check_func(obj, rule):
                apply_func(obj, rule)
238 239
                if 'one_shot' in rule and rule['one_shot']:
                    to_remove.append(num)
240 241 242
                # Should we stop after first valid rule ?
                # break

243 244 245 246 247 248 249 250 251 252 253 254 255
        decal = 0
        num = 0
        while str(num) in self.config:
            if (num + decal) in to_remove:
                num2 = num
                while str(num2 + 1) in self.config:
                    self.config[str(num2)] = self.config[str(num2 + 1)].copy()
                    num2 += 1
                del self.config[str(num2)]
                decal += 1
            else:
                num += 1

256 257 258 259 260 261 262
    def _nec_notif(self, obj):
        self._nec_all(obj, self.check_rule_apply_notif, self.apply_rule_notif)

    def _nec_decrypted_message_received(self, obj):
        self._nec_all(obj, self.check_rule_apply_decrypted_msg,
            self.apply_rule_decrypted_message)

263 264 265 266
    def _nec_gc_message_received(self, obj):
        self._nec_all(obj, self.check_rule_apply_decrypted_msg,
            self.apply_rule_decrypted_message)

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    def _nec_presence_received(self, obj):
        if obj.old_show < 2 and obj.new_show > 1:
            check_func = self.check_rule_apply_connected
        elif obj.old_show > 1 and obj.new_show < 2:
            check_func = self.check_rule_apply_disconnected
        else:
            check_func = self.check_rule_apply_status_changed
        self._nec_all(obj, check_func, self.apply_rule_presence)


class TriggersPluginConfigDialog(GajimPluginConfigDialog):
    # {event: widgets_to_disable, }
    events_list = {
        'message_received': [],
        'contact_connected': ['use_systray_cb', 'disable_systray_cb',
            'use_roster_cb', 'disable_roster_cb'],
        'contact_disconnected': ['use_systray_cb', 'disable_systray_cb',
            'use_roster_cb', 'disable_roster_cb'],
        'contact_status_change': ['use_systray_cb', 'disable_systray_cb',
            'use_roster_cb', 'disable_roster_cb']
        #, 'gc_msg_highlight': [], 'gc_msg': []}
    }
289
    recipient_types_list = ['contact', 'group', 'groupchat', 'all']
290
    config_options = ['event', 'recipient_type', 'recipients', 'status',
291 292 293
        'tab_opened', 'has_focus', 'sound', 'sound_file', 'popup', 'auto_open',
        'run_command', 'command', 'systray', 'roster', 'urgency_hint',
        'one_shot']
294 295

    def init(self):
296 297 298 299 300 301 302
        path = self.plugin.local_file_path('config_dialog.ui')
        self._ui = get_builder(path, widgets=['plugin_box', 'liststore1', 'liststore2'])

        box = self.get_content_area()
        box.pack_start(self._ui.plugin_box, True, True, 0)

        self._ui.connect_signals(self)
303 304 305
        self.connect('hide', self.on_hide)

    def on_run(self):
306
        # Fill window
307
        for w in ('conditions_treeview', 'config_box', 'event_combobox',
308
        'recipient_type_combobox', 'recipient_list_entry', 'delete_button',
309
        'online_cb', 'away_cb', 'xa_cb', 'dnd_cb', 'invisible_cb',
310
        'use_sound_cb', 'disable_sound_cb', 'use_popup_cb',
311 312
        'disable_popup_cb', 'use_auto_open_cb', 'disable_auto_open_cb',
        'use_systray_cb', 'disable_systray_cb', 'use_roster_cb',
313 314
        'disable_roster_cb', 'tab_opened_cb', 'not_tab_opened_cb',
        'has_focus_cb', 'not_has_focus_cb', 'sound_entry', 'sound_file_box',
315 316
        'up_button', 'down_button', 'run_command_cb', 'command_entry',
        'one_shot_cb', 'use_urgency_hint_cb', 'disable_urgency_hint_cb'):
317
            self._ui.__dict__[w] = self._ui.get_object(w)
318 319 320 321 322

        self.config = {}
        for n in self.plugin.config:
            self.config[int(n)] = self.plugin.config[n]

323
        if not self._ui.conditions_treeview.get_column(0):
324
            # Window never opened
325 326
            model = Gtk.ListStore(int, str)
            model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
327
            self._ui.conditions_treeview.set_model(model)
328

329
            # '#' Means number
330
            col = Gtk.TreeViewColumn(_('#'))
331
            self._ui.conditions_treeview.append_column(col)
332
            renderer = Gtk.CellRendererText()
333
            col.pack_start(renderer, expand=False)
334
            col.add_attribute(renderer, 'text', 0)
335

336
            col = Gtk.TreeViewColumn(_('Condition'))
337
            self._ui.conditions_treeview.append_column(col)
338
            renderer = Gtk.CellRendererText()
339
            col.pack_start(renderer, expand=True)
340
            col.add_attribute(renderer, 'text', 1)
341
        else:
342
            model = self._ui.conditions_treeview.get_model()
343 344 345 346 347 348 349 350

        model.clear()

        # Fill conditions_treeview
        num = 0
        while num in self.config:
            iter_ = model.append((num, ''))
            path = model.get_path(iter_)
351
            self._ui.conditions_treeview.set_cursor(path)
352 353 354 355 356 357
            self.active_num = num
            self.initiate_rule_state()
            self.set_treeview_string()
            num += 1

        # No rule selected at init time
358
        self._ui.conditions_treeview.get_selection().unselect_all()
359
        self.active_num = -1
360 361 362 363
        self._ui.config_box.set_sensitive(False)
        self._ui.delete_button.set_sensitive(False)
        self._ui.down_button.set_sensitive(False)
        self._ui.up_button.set_sensitive(False)
364 365 366 367 368 369 370 371 372 373

    def initiate_rule_state(self):
        """
        Set values for all widgets
        """
        if self.active_num < 0:
            return
        # event
        value = self.config[self.active_num]['event']
        if value:
374
            self._ui.event_combobox.set_active(list(self.events_list.keys()).index(
375
                value))
376
        else:
377
            self._ui.event_combobox.set_active(-1)
378 379 380
        # recipient_type
        value = self.config[self.active_num]['recipient_type']
        if value:
381
            self._ui.recipient_type_combobox.set_active(
382 383
                self.recipient_types_list.index(value))
        else:
384
            self._ui.recipient_type_combobox.set_active(-1)
385 386 387 388
        # recipient
        value = self.config[self.active_num]['recipients']
        if not value:
            value = ''
389
        self._ui.recipient_list_entry.set_text(value)
390 391 392
        # status
        value = self.config[self.active_num]['status']
        if value == 'all':
393
            self._ui.all_status_rb.set_active(True)
394
        else:
395
            self._ui.special_status_rb.set_active(True)
396 397 398
            values = value.split()
            for v in ('online', 'away', 'xa', 'dnd', 'invisible'):
                if v in values:
399
                    self._ui.__dict__[v + '_cb'].set_active(True)
400
                else:
401
                    self._ui.__dict__[v + '_cb'].set_active(False)
402

403
        self.on_status_radiobutton_toggled(self._ui.all_status_rb)
404 405 406

        # tab_opened
        value = self.config[self.active_num]['tab_opened']
407 408
        self._ui.tab_opened_cb.set_active(True)
        self._ui.not_tab_opened_cb.set_active(True)
409
        if value == 'no':
410
            self._ui.tab_opened_cb.set_active(False)
411
        elif value == 'yes':
412
            self._ui.not_tab_opened_cb.set_active(False)
413

414 415 416 417
        # has_focus
        if 'has_focus' not in self.config[self.active_num]:
            self.config[self.active_num]['has_focus'] = 'both'
        value = self.config[self.active_num]['has_focus']
418 419
        self._ui.has_focus_cb.set_active(True)
        self._ui.not_has_focus_cb.set_active(True)
420
        if value == 'no':
421
            self._ui.has_focus_cb.set_active(False)
422
        elif value == 'yes':
423
            self._ui.not_has_focus_cb.set_active(False)
424

425 426
        # sound_file
        value = self.config[self.active_num]['sound_file']
427
        self._ui.sound_entry.set_text(value)
428 429 430 431 432 433

        # sound, popup, auto_open, systray, roster
        for option in ('sound', 'popup', 'auto_open', 'systray', 'roster',
        'urgency_hint'):
            value = self.config[self.active_num][option]
            if value == 'yes':
434
                self._ui.__dict__['use_' + option + '_cb'].set_active(True)
435
            else:
436
                self._ui.__dict__['use_' + option + '_cb'].set_active(False)
437
            if value == 'no':
438
                self._ui.__dict__['disable_' + option + '_cb'].set_active(True)
439
            else:
440
                self._ui.__dict__['disable_' + option + '_cb'].set_active(False)
441 442 443

        # run_command
        value = self.config[self.active_num]['run_command']
444
        self._ui.run_command_cb.set_active(value)
445 446 447

        # command
        value = self.config[self.active_num]['command']
448
        self._ui.command_entry.set_text(value)
449

450 451 452 453 454
        # one shot
        if 'one_shot' in self.config[self.active_num]:
            value = self.config[self.active_num]['one_shot']
        else:
            value = False
455
        self._ui.one_shot_cb.set_active(value)
456

457
    def set_treeview_string(self):
458
        (model, iter_) = self._ui.conditions_treeview.get_selection().get_selected()
459 460
        if not iter_:
            return
461
        ind = self._ui.event_combobox.get_active()
462 463
        event = ''
        if ind > -1:
464 465
            event = self._ui.event_combobox.get_model()[ind][0]
        ind = self._ui.recipient_type_combobox.get_active()
466 467
        recipient_type = ''
        if ind > -1:
468
            recipient_type = self._ui.recipient_type_combobox.get_model()[ind][0]
469 470
        recipient = ''
        if recipient_type != 'everybody':
471 472
            recipient = self._ui.recipient_list_entry.get_text()
        if self._ui.all_status_rb.get_active():
473 474
            status = ''
        else:
475
            status = _('and I am: ')
476
            for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
477
                if self._ui.__dict__[st + '_cb'].get_active():
478 479 480 481 482
                    status += helpers.get_uf_show(st) + ' '
        model[iter_][1] = _('When event: %(event)s for category: '
                            '%(recipient_type)s %(recipient)s %(status)s') % {
                                'event': event, 'recipient_type': recipient_type,
                                'recipient': recipient, 'status': status}
483 484 485 486 487 488 489

    def on_conditions_treeview_cursor_changed(self, widget):
        (model, iter_) = widget.get_selection().get_selected()
        if not iter_:
            self.active_num = ''
            return
        self.active_num = model[iter_][0]
490
        if self.active_num == 0:
491
            self._ui.up_button.set_sensitive(False)
492
        else:
493 494
            self._ui.up_button.set_sensitive(True)
        _max = self._ui.conditions_treeview.get_model().iter_n_children(None)
495
        if self.active_num == _max - 1:
496
            self._ui.down_button.set_sensitive(False)
497
        else:
498
            self._ui.down_button.set_sensitive(True)
499
        self.initiate_rule_state()
500 501
        self._ui.config_box.set_sensitive(True)
        self._ui.delete_button.set_sensitive(True)
502 503

    def on_new_button_clicked(self, widget):
504 505
        model = self._ui.conditions_treeview.get_model()
        num = self._ui.conditions_treeview.get_model().iter_n_children(None)
506
        self.config[num] = {'event': 'message_received', 'recipient_type': 'all',
507
            'recipients': '', 'status': 'all', 'tab_opened': 'both',
508 509 510
            'has_focus': 'both', 'sound': '', 'sound_file': '', 'popup': '',
            'auto_open': '', 'run_command': False, 'command': '', 'systray': '',
            'roster': '', 'one_shot': False, 'urgency_hint': False}
511 512
        iter_ = model.append((num, ''))
        path = model.get_path(iter_)
513
        self._ui.conditions_treeview.set_cursor(path)
514 515
        self.active_num = num
        self.set_treeview_string()
516
        self._ui.config_box.set_sensitive(True)
517 518

    def on_delete_button_clicked(self, widget):
519
        (model, iter_) = self._ui.conditions_treeview.get_selection().get_selected()
520 521 522 523 524 525 526 527 528 529 530 531 532
        if not iter_:
            return
        # up all others
        iter2 = model.iter_next(iter_)
        num = self.active_num
        while iter2:
            num = model[iter2][0]
            model[iter2][0] = num - 1
            self.config[num - 1] = self.config[num].copy()
            iter2 = model.iter_next(iter2)
        model.remove(iter_)
        del self.config[num]
        self.active_num = ''
533 534 535 536
        self._ui.config_box.set_sensitive(False)
        self._ui.delete_button.set_sensitive(False)
        self._ui.up_button.set_sensitive(False)
        self._ui.down_button.set_sensitive(False)
537 538

    def on_up_button_clicked(self, widget):
539
        (model, iter_) = self._ui.conditions_treeview.get_selection().get_selected()
540 541 542 543 544 545 546 547 548 549 550
        if not iter_:
            return
        conf = self.config[self.active_num].copy()
        self.config[self.active_num] = self.config[self.active_num - 1]
        self.config[self.active_num - 1] = conf

        model[iter_][0] = self.active_num - 1
        # get previous iter
        path = model.get_path(iter_)
        iter_ = model.get_iter((path[0] - 1,))
        model[iter_][0] = self.active_num
551
        self.on_conditions_treeview_cursor_changed(self._ui.conditions_treeview)
552 553

    def on_down_button_clicked(self, widget):
554
        (model, iter_) = self._ui.conditions_treeview.get_selection().get_selected()
555 556 557 558 559 560 561 562 563
        if not iter_:
            return
        conf = self.config[self.active_num].copy()
        self.config[self.active_num] = self.config[self.active_num + 1]
        self.config[self.active_num + 1] = conf

        model[iter_][0] = self.active_num + 1
        iter_ = model.iter_next(iter_)
        model[iter_][0] = self.active_num
564
        self.on_conditions_treeview_cursor_changed(self._ui.conditions_treeview)
565 566 567 568

    def on_event_combobox_changed(self, widget):
        if self.active_num < 0:
            return
569
        active = self._ui.event_combobox.get_active()
570
        if active == -1:
571
            return
572
        else:
573
            event = list(self.events_list.keys())[active]
574 575 576
        self.config[self.active_num]['event'] = event
        for w in ('use_systray_cb', 'disable_systray_cb', 'use_roster_cb',
        'disable_roster_cb'):
577
            self._ui.__dict__[w].set_sensitive(True)
578
        for w in self.events_list[event]:
579 580
            self._ui.__dict__[w].set_sensitive(False)
            self._ui.__dict__[w].set_state(False)
581 582 583 584 585 586
        self.set_treeview_string()

    def on_recipient_type_combobox_changed(self, widget):
        if self.active_num < 0:
            return
        recipient_type = self.recipient_types_list[
587
            self._ui.recipient_type_combobox.get_active()]
588 589
        self.config[self.active_num]['recipient_type'] = recipient_type
        if recipient_type == 'all':
590
            self._ui.recipient_list_entry.set_sensitive(False)
591
        else:
592
            self._ui.recipient_list_entry.set_sensitive(True)
593 594 595 596 597
        self.set_treeview_string()

    def on_recipient_list_entry_changed(self, widget):
        if self.active_num < 0:
            return
598
        recipients = widget.get_text()
599 600 601 602 603 604 605 606 607
        #TODO: do some check
        self.config[self.active_num]['recipients'] = recipients
        self.set_treeview_string()

    def set_status_config(self):
        if self.active_num < 0:
            return
        status = ''
        for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
608
            if self._ui.__dict__[st + '_cb'].get_active():
609 610 611 612 613 614 615 616 617
                status += st + ' '
        if status:
            status = status[:-1]
        self.config[self.active_num]['status'] = status
        self.set_treeview_string()

    def on_status_radiobutton_toggled(self, widget):
        if self.active_num < 0:
            return
618 619
        if self._ui.all_status_rb.get_active():
            self._ui.status_expander.set_expanded(False)
620 621 622
            self.config[self.active_num]['status'] = 'all'
            # 'All status' clicked
            for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
623
                self._ui.__dict__[st + '_cb'].set_sensitive(False)
624
        else:
625
            self._ui.status_expander.set_expanded(True)
626 627 628
            self.set_status_config()
            # 'special status' clicked
            for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
629
                self._ui.__dict__[st + '_cb'].set_sensitive(True)
630 631 632 633 634 635 636 637 638 639 640 641

        self.set_treeview_string()

    def on_status_cb_toggled(self, widget):
        if self.active_num < 0:
            return
        self.set_status_config()

    # tab_opened OR (not xor) not_tab_opened must be active
    def on_tab_opened_cb_toggled(self, widget):
        if self.active_num < 0:
            return
642 643 644 645
        if self._ui.tab_opened_cb.get_active():
            self._ui.has_focus_cb.set_sensitive(True)
            self._ui.not_has_focus_cb.set_sensitive(True)
            if self._ui.not_tab_opened_cb.get_active():
646 647 648 649
                self.config[self.active_num]['tab_opened'] = 'both'
            else:
                self.config[self.active_num]['tab_opened'] = 'yes'
        else:
650 651 652
            self._ui.has_focus_cb.set_sensitive(False)
            self._ui.not_has_focus_cb.set_sensitive(False)
            self._ui.not_tab_opened_cb.set_active(True)
653 654 655 656 657
            self.config[self.active_num]['tab_opened'] = 'no'

    def on_not_tab_opened_cb_toggled(self, widget):
        if self.active_num < 0:
            return
658 659
        if self._ui.not_tab_opened_cb.get_active():
            if self._ui.tab_opened_cb.get_active():
660 661 662 663
                self.config[self.active_num]['tab_opened'] = 'both'
            else:
                self.config[self.active_num]['tab_opened'] = 'no'
        else:
664
            self._ui.tab_opened_cb.set_active(True)
665 666
            self.config[self.active_num]['tab_opened'] = 'yes'

667 668 669 670
    # has_focus OR (not xor) not_has_focus must be active
    def on_has_focus_cb_toggled(self, widget):
        if self.active_num < 0:
            return
671 672
        if self._ui.has_focus_cb.get_active():
            if self._ui.not_has_focus_cb.get_active():
673 674 675 676
                self.config[self.active_num]['has_focus'] = 'both'
            else:
                self.config[self.active_num]['has_focus'] = 'yes'
        else:
677
            self._ui.not_has_focus_cb.set_active(True)
678 679 680 681 682
            self.config[self.active_num]['has_focus'] = 'no'

    def on_not_has_focus_cb_toggled(self, widget):
        if self.active_num < 0:
            return
683 684
        if self._ui.not_has_focus_cb.get_active():
            if self._ui.has_focus_cb.get_active():
685 686 687 688
                self.config[self.active_num]['has_focus'] = 'both'
            else:
                self.config[self.active_num]['has_focus'] = 'no'
        else:
689
            self._ui.has_focus_cb.set_active(True)
690 691
            self.config[self.active_num]['has_focus'] = 'yes'

692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
    def on_use_it_toggled(self, widget, oposite_widget, option):
        if widget.get_active():
            if oposite_widget.get_active():
                oposite_widget.set_active(False)
            self.config[self.active_num][option] = 'yes'
        elif oposite_widget.get_active():
            self.config[self.active_num][option] = 'no'
        else:
            self.config[self.active_num][option] = ''

    def on_disable_it_toggled(self, widget, oposite_widget, option):
        if widget.get_active():
            if oposite_widget.get_active():
                oposite_widget.set_active(False)
            self.config[self.active_num][option] = 'no'
        elif oposite_widget.get_active():
            self.config[self.active_num][option] = 'yes'
        else:
            self.config[self.active_num][option] = ''

    def on_use_sound_cb_toggled(self, widget):
713
        self.on_use_it_toggled(widget, self._ui.disable_sound_cb, 'sound')
714
        if widget.get_active():
715
            self._ui.sound_file_box.set_sensitive(True)
716
        else:
717
            self._ui.sound_file_box.set_sensitive(False)
718 719

    def on_browse_for_sounds_button_clicked(self, widget, data=None):
720 721 722 723 724 725 726 727 728 729 730
        if NEW_FILECHOOSER:
            self._new_filechooser()
        else:
            self._old_filechooser(widget, data)

    def _new_filechooser(self):
        if self.active_num < 0:
            return

        def on_ok(path_to_snd_file):
            self.config[self.active_num]['sound_file'] = path_to_snd_file
731
            self._ui.sound_entry.set_text(path_to_snd_file)
732

733
        path_to_snd_file = self._ui.sound_entry.get_text()
734 735 736 737 738 739
        path_to_snd_file = os.path.join(os.getcwd(), path_to_snd_file)
        SoundChooserDialog(on_ok,
                           path=path_to_snd_file,
                           transient_for=self)

    def _old_filechooser(self, widget, data=None):
740 741 742 743 744 745 746 747
        if self.active_num < 0:
            return

        def on_ok(widget, path_to_snd_file):
            dialog.destroy()
            if not path_to_snd_file:
                path_to_snd_file = ''
            self.config[self.active_num]['sound_file'] = path_to_snd_file
748
            self._ui.sound_entry.set_text(path_to_snd_file)
749

750
        path_to_snd_file = self._ui.sound_entry.get_text()
751 752 753 754
        path_to_snd_file = os.path.join(os.getcwd(), path_to_snd_file)
        dialog = SoundChooserDialog(path_to_snd_file, on_ok)

    def on_play_button_clicked(self, widget):
755
        helpers.play_sound_file(self._ui.sound_entry.get_text())
756 757

    def on_disable_sound_cb_toggled(self, widget):
758
        self.on_disable_it_toggled(widget, self._ui.use_sound_cb, 'sound')
759 760

    def on_sound_entry_changed(self, widget):
761
        self.config[self.active_num]['sound_file'] = widget.get_text()
762 763

    def on_use_popup_cb_toggled(self, widget):
764
        self.on_use_it_toggled(widget, self._ui.disable_popup_cb, 'popup')
765 766

    def on_disable_popup_cb_toggled(self, widget):
767
        self.on_disable_it_toggled(widget, self._ui.use_popup_cb, 'popup')
768 769

    def on_use_auto_open_cb_toggled(self, widget):
770
        self.on_use_it_toggled(widget, self._ui.disable_auto_open_cb, 'auto_open')
771 772

    def on_disable_auto_open_cb_toggled(self, widget):
773
        self.on_disable_it_toggled(widget, self._ui.use_auto_open_cb, 'auto_open')
774 775 776 777

    def on_run_command_cb_toggled(self, widget):
        self.config[self.active_num]['run_command'] = widget.get_active()
        if widget.get_active():
778
            self._ui.command_entry.set_sensitive(True)
779
        else:
780
            self._ui.command_entry.set_sensitive(False)
781 782

    def on_command_entry_changed(self, widget):
783
        self.config[self.active_num]['command'] = widget.get_text()
784 785

    def on_use_systray_cb_toggled(self, widget):
786
        self.on_use_it_toggled(widget, self._ui.disable_systray_cb, 'systray')
787 788

    def on_disable_systray_cb_toggled(self, widget):
789
        self.on_disable_it_toggled(widget, self._ui.use_systray_cb, 'systray')
790 791

    def on_use_roster_cb_toggled(self, widget):
792
        self.on_use_it_toggled(widget, self._ui.disable_roster_cb, 'roster')
793 794

    def on_disable_roster_cb_toggled(self, widget):
795
        self.on_disable_it_toggled(widget, self._ui.use_roster_cb, 'roster')
796

797 798
    def on_one_shot_cb_toggled(self, widget):
        self.config[self.active_num]['one_shot'] = widget.get_active()
799
        self._ui.command_entry.set_sensitive(widget.get_active())
800

801
    def on_use_urgency_hint_cb_toggled(self, widget):
802
        self.on_use_it_toggled(widget, self._ui.disable_urgency_hint_cb,
803 804 805
            'uregency_hint')

    def on_disable_urgency_hint_cb_toggled(self, widget):
806
        self.on_disable_it_toggled(widget, self._ui.use_urgency_hint_cb,
807 808 809 810
            'uregency_hint')

    def on_hide(self, widget):
        # save config
811
        for n in list(self.plugin.config.keys()):
812 813 814
            del self.plugin.config[n]
        for n in self.config:
            self.plugin.config[str(n)] = self.config[n]