# -*- coding: utf-8 -*-
## src/dialogs.py
##
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2003-2014 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
##                         Travis Shirk <travis AT pobox.com>
## Copyright (C) 2005-2008 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
##                         Julien Pivotto <roidelapluie AT gmail.com>
##                         Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
## 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/>.
##

from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Pango

import os
import nbxmpp
import time
import locale

from gajim import gtkgui_helpers
from gajim import vcard
from gajim import conversation_textview
from gajim import dataforms_widget

from random import randrange
from gajim.common import pep
from gajim.common import ged
from gajim.common import const
from gajim.options_dialog import OptionsDialog
from gajim.common.const import Option, OptionKind, OptionType

from gajim.common import app
from gajim.common import helpers
from gajim.common import i18n
from gajim.common import dataforms
from gajim.common.const import AvatarSize
from gajim.common.caps_cache import muc_caps_cache
from gajim.common.exceptions import GajimGeneralException
from gajim.common.connection_handlers_events import MessageOutgoingEvent

if app.HAVE_SPELL:
    from gi.repository import Gspell

import logging
log = logging.getLogger('gajim.dialogs')


class EditGroupsDialog:
    """
    Class for the edit group dialog window
    """

    def __init__(self, list_):
        """
        list_ is a list of (contact, account) tuples
        """
        self.xml = gtkgui_helpers.get_gtk_builder('edit_groups_dialog.ui')
        self.dialog = self.xml.get_object('edit_groups_dialog')
        self.dialog.set_transient_for(app.interface.roster.window)
        self.list_ = list_
        self.changes_made = False
        self.treeview = self.xml.get_object('groups_treeview')
        if len(list_) == 1:
            contact = list_[0][0]
            self.xml.get_object('nickname_label').set_markup(
                    _('Contact name: <i>%s</i>') % contact.get_shown_name())
            self.xml.get_object('jid_label').set_markup(
                    _('JID: <i>%s</i>') % contact.jid)
        else:
            self.xml.get_object('nickname_label').set_no_show_all(True)
            self.xml.get_object('nickname_label').hide()
            self.xml.get_object('jid_label').set_no_show_all(True)
            self.xml.get_object('jid_label').hide()

        self.xml.connect_signals(self)
        self.init_list()

        self.dialog.show_all()
        if self.changes_made:
            for (contact, account) in self.list_:
                app.connections[account].update_contact(contact.jid,
                    contact.name, contact.groups)

    def on_edit_groups_dialog_response(self, widget, response_id):
        if response_id == Gtk.ResponseType.CLOSE:
            self.dialog.destroy()

    def remove_group(self, group):
        """
        Remove group group from all contacts and all their brothers
        """
        for (contact, account) in self.list_:
            app.interface.roster.remove_contact_from_groups(contact.jid,
                account, [group])

        # FIXME: Ugly workaround.
        app.interface.roster.draw_group(_('General'), account)

    def add_group(self, group):
        """
        Add group group to all contacts and all their brothers
        """
        for (contact, account) in self.list_:
            app.interface.roster.add_contact_to_groups(contact.jid, account,
                [group])

        # FIXME: Ugly workaround.
        # Maybe we haven't been in any group (defaults to General)
        app.interface.roster.draw_group(_('General'), account)

    def on_add_button_clicked(self, widget):
        group = self.xml.get_object('group_entry').get_text()
        if not group:
            return
        # Do not allow special groups
        if group in helpers.special_groups:
            return
        # check if it already exists
        model = self.treeview.get_model()
        iter_ = model.get_iter_first()
        while iter_:
            if model.get_value(iter_, 0) == group:
                return
            iter_ = model.iter_next(iter_)
        self.changes_made = True
        model.append((group, True, False))
        self.add_group(group)
        self.init_list() # Re-draw list to sort new item

    def group_toggled_cb(self, cell, path):
        self.changes_made = True
        model = self.treeview.get_model()
        if model[path][2]:
            model[path][2] = False
            model[path][1] = True
        else:
            model[path][1] = not model[path][1]
        group = model[path][0]
        if model[path][1]:
            self.add_group(group)
        else:
            self.remove_group(group)

    def init_list(self):
        store = Gtk.ListStore(str, bool, bool)
        self.treeview.set_model(store)
        for column in self.treeview.get_columns():
            # Clear treeview when re-drawing
            self.treeview.remove_column(column)
        accounts = []
        # Store groups in a list so we can sort them and the number of contacts in
        # it
        groups = {}
        for (contact, account) in self.list_:
            if account not in accounts:
                accounts.append(account)
                for g in app.groups[account].keys():
                    if g in groups:
                        continue
                    groups[g] = 0
            c_groups = contact.groups
            for g in c_groups:
                groups[g] += 1
        group_list = []
        # Remove special groups if they are empty
        for group in groups:
            if group not in helpers.special_groups or groups[group] > 0:
                group_list.append(group)
        group_list.sort()
        for group in group_list:
            iter_ = store.append()
            store.set(iter_, 0, group) # Group name
            if groups[group] == 0:
                store.set(iter_, 1, False)
            else:
                store.set(iter_, 1, True)
                if groups[group] == len(self.list_):
                    # all contacts are in this group
                    store.set(iter_, 2, False)
                else:
                    store.set(iter_, 2, True)
        column = Gtk.TreeViewColumn(_('Group'))
        column.set_expand(True)
        self.treeview.append_column(column)
        renderer = Gtk.CellRendererText()
        column.pack_start(renderer, True)
        column.add_attribute(renderer, 'text', 0)

        column = Gtk.TreeViewColumn(_('In the group'))
        column.set_expand(False)
        self.treeview.append_column(column)
        renderer = Gtk.CellRendererToggle()
        column.pack_start(renderer, True)
        renderer.set_property('activatable', True)
        renderer.connect('toggled', self.group_toggled_cb)
        column.add_attribute(renderer, 'active', 1)
        column.add_attribute(renderer, 'inconsistent', 2)

class PassphraseDialog:
    """
    Class for Passphrase dialog
    """
    def __init__(self, titletext, labeltext, checkbuttontext=None,
    ok_handler=None, cancel_handler=None, transient_for=None):
        self.xml = gtkgui_helpers.get_gtk_builder('passphrase_dialog.ui')
        self.window = self.xml.get_object('passphrase_dialog')
        self.passphrase_entry = self.xml.get_object('passphrase_entry')
        self.passphrase = -1
        self.window.set_title(titletext)
        self.xml.get_object('message_label').set_text(labeltext)

        self.ok = False

        self.cancel_handler = cancel_handler
        self.ok_handler = ok_handler
        okbutton = self.xml.get_object('ok_button')
        okbutton.connect('clicked', self.on_okbutton_clicked)
        cancelbutton = self.xml.get_object('cancel_button')
        cancelbutton.connect('clicked', self.on_cancelbutton_clicked)

        self.xml.connect_signals(self)
        if transient_for is None:
            transient_for = app.app.get_active_window()
        self.window.set_transient_for(transient_for)
        self.window.show_all()

        self.check = bool(checkbuttontext)
        checkbutton =   self.xml.get_object('save_passphrase_checkbutton')
        if self.check:
            checkbutton.set_label(checkbuttontext)
        else:
            checkbutton.hide()

    def on_okbutton_clicked(self, widget):
        if not self.ok_handler:
            return

        passph = self.passphrase_entry.get_text()

        if self.check:
            checked = self.xml.get_object('save_passphrase_checkbutton').\
                    get_active()
        else:
            checked = False

        self.ok = True

        self.window.destroy()

        if isinstance(self.ok_handler, tuple):
            self.ok_handler[0](passph, checked, *self.ok_handler[1:])
        else:
            self.ok_handler(passph, checked)

    def on_cancelbutton_clicked(self, widget):
        self.window.destroy()

    def on_passphrase_dialog_destroy(self, widget):
        if self.cancel_handler and not self.ok:
            self.cancel_handler()

class ChooseGPGKeyDialog:
    """
    Class for GPG key dialog
    """

    def __init__(self, title_text, prompt_text, secret_keys, on_response,
            selected=None, transient_for=None):
        '''secret_keys : {keyID: userName, ...}'''
        self.on_response = on_response
        xml = gtkgui_helpers.get_gtk_builder('choose_gpg_key_dialog.ui')
        self.window = xml.get_object('choose_gpg_key_dialog')
        self.window.set_title(title_text)
        self.window.set_transient_for(transient_for)
        self.keys_treeview = xml.get_object('keys_treeview')
        prompt_label = xml.get_object('prompt_label')
        prompt_label.set_text(prompt_text)
        model = Gtk.ListStore(str, str)
        model.set_sort_func(1, self.sort_keys)
        model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
        self.keys_treeview.set_model(model)
        #columns
        renderer = Gtk.CellRendererText()
        self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
            renderer, text=0)
        col = self.keys_treeview.get_column(0)
        col.set_sort_column_id(0)
        renderer = Gtk.CellRendererText()
        self.keys_treeview.insert_column_with_attributes(-1, _('Contact name'),
            renderer, text=1)
        col = self.keys_treeview.get_column(1)
        col.set_sort_column_id(1)
        self.keys_treeview.set_search_column(1)
        self.fill_tree(secret_keys, selected)
        self.window.connect('response', self.on_dialog_response)
        self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
        self.window.show_all()

    def sort_keys(self, model, iter1, iter2, data=None):
        value1 = model[iter1][1]
        value2 = model[iter2][1]
        if value1 == _('None'):
            return -1
        elif value2 == _('None'):
            return 1
        elif value1 < value2:
            return -1
        return 1

    def on_dialog_response(self, dialog, response):
        selection = self.keys_treeview.get_selection()
        (model, iter_) = selection.get_selected()
        if iter_ and response == Gtk.ResponseType.OK:
            keyID = [ model[iter_][0], model[iter_][1] ]
        else:
            keyID = None
        self.on_response(keyID)
        self.window.destroy()

    def fill_tree(self, list_, selected):
        model = self.keys_treeview.get_model()
        for keyID in list_.keys():
            iter_ = model.append((keyID, list_[keyID]))
            if keyID == selected:
                path = model.get_path(iter_)
                self.keys_treeview.set_cursor(path)


class ChangeActivityDialog:
    PAGELIST = ['doing_chores', 'drinking', 'eating', 'exercising', 'grooming',
            'having_appointment', 'inactive', 'relaxing', 'talking', 'traveling',
            'working']

    def __init__(self, on_response, activity=None, subactivity=None, text=''):
        self.on_response = on_response
        self.activity = activity
        self.subactivity = subactivity
        self.text = text
        self.xml = gtkgui_helpers.get_gtk_builder('change_activity_dialog.ui')
        self.window = self.xml.get_object('change_activity_dialog')
        self.window.set_transient_for(app.interface.roster.window)

        self.checkbutton = self.xml.get_object('enable_checkbutton')
        self.notebook = self.xml.get_object('notebook')
        self.entry = self.xml.get_object('description_entry')

        rbtns = {}
        group = None

        for category in pep.ACTIVITIES:
            item = self.xml.get_object(category + '_image')
            item.set_from_pixbuf(
                    gtkgui_helpers.load_activity_icon(category).get_pixbuf())
            item.set_tooltip_text(pep.ACTIVITIES[category]['category'])

            vbox = self.xml.get_object(category + '_vbox')
            vbox.set_border_width(5)

            # Other
            act = category + '_other'

            if group:
                rbtns[act] = Gtk.RadioButton()
                rbtns[act].join_group(group)
            else:
                rbtns[act] = group = Gtk.RadioButton()

            hbox = Gtk.HBox(homogeneous=False, spacing=5)
            hbox.pack_start(gtkgui_helpers.load_activity_icon(category,
                activity), False, False, 0)
            lbl = Gtk.Label(label='<b>' + pep.ACTIVITIES[category]['category'] \
                + '</b>')
            lbl.set_use_markup(True)
            hbox.pack_start(lbl, False, False, 0)
            rbtns[act].add(hbox)
            rbtns[act].connect('toggled', self.on_rbtn_toggled,
                    [category, 'other'])
            vbox.pack_start(rbtns[act], False, False, 0)

            activities = []
            for activity in pep.ACTIVITIES[category]:
                activities.append(activity)
            activities.sort()
            for activity in activities:
                if activity == 'category':
                    continue

                act = category + '_' + activity

                if group:
                    rbtns[act] = Gtk.RadioButton()
                    rbtns[act].join_group(group)
                else:
                    rbtns[act] = group = Gtk.RadioButton()

                hbox = Gtk.HBox(homogeneous=False, spacing=5)
                hbox.pack_start(gtkgui_helpers.load_activity_icon(category,
                        activity), False, False, 0)
                hbox.pack_start(Gtk.Label(label=pep.ACTIVITIES[category][activity]),
                        False, False, 0)
                rbtns[act].connect('toggled', self.on_rbtn_toggled,
                        [category, activity])
                rbtns[act].add(hbox)
                vbox.pack_start(rbtns[act], False, False, 0)


        self.default_radio = rbtns['doing_chores_other']

        if self.activity in pep.ACTIVITIES:
            if not self.subactivity in pep.ACTIVITIES[self.activity]:
                self.subactivity = 'other'

            rbtns[self.activity + '_' + self.subactivity].set_active(True)

            self.checkbutton.set_active(True)
            self.notebook.set_sensitive(True)
            self.entry.set_sensitive(True)

            self.notebook.set_current_page(
                    self.PAGELIST.index(self.activity))

            self.entry.set_text(text)

        else:
            self.checkbutton.set_active(False)

        self.xml.connect_signals(self)
        self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
        self.window.show_all()

    def on_enable_checkbutton_toggled(self, widget):
        self.notebook.set_sensitive(widget.get_active())
        self.entry.set_sensitive(widget.get_active())
        if not self.activity:
            self.default_radio.set_active(True)

    def on_rbtn_toggled(self, widget, data):
        if widget.get_active():
            self.activity = data[0]
            self.subactivity = data[1]

    def on_ok_button_clicked(self, widget):
        """
        Return activity and messsage (None if no activity selected)
        """
        if self.checkbutton.get_active():
            self.on_response(self.activity, self.subactivity,
                    self.entry.get_text())
        else:
            self.on_response(None, None, '')
        self.window.destroy()

    def on_cancel_button_clicked(self, widget):
        self.window.destroy()

class ChangeMoodDialog:
    COLS = 11

    def __init__(self, on_response, mood=None, text=''):
        self.on_response = on_response
        self.mood = mood
        self.text = text
        self.xml = gtkgui_helpers.get_gtk_builder('change_mood_dialog.ui')

        self.window = self.xml.get_object('change_mood_dialog')
        self.window.set_transient_for(app.interface.roster.window)
        self.window.set_title(_('Set Mood'))

        table = self.xml.get_object('mood_icons_table')
        self.label = self.xml.get_object('mood_label')
        self.entry = self.xml.get_object('description_entry')

        no_mood_button = self.xml.get_object('no_mood_button')
        no_mood_button.set_mode(False)
        no_mood_button.connect('clicked',
                self.on_mood_button_clicked, None)

        x = 1
        y = 0
        self.mood_buttons = {}

        # Order them first
        self.MOODS = []
        for mood in pep.MOODS:
            self.MOODS.append(mood)
        self.MOODS.sort()

        for mood in self.MOODS:
            self.mood_buttons[mood] = Gtk.RadioButton()
            self.mood_buttons[mood].join_group(no_mood_button)
            self.mood_buttons[mood].set_mode(False)
            self.mood_buttons[mood].add(gtkgui_helpers.load_mood_icon(mood))
            self.mood_buttons[mood].set_relief(Gtk.ReliefStyle.NONE)
            self.mood_buttons[mood].set_tooltip_text(pep.MOODS[mood])
            self.mood_buttons[mood].connect('clicked',
                self.on_mood_button_clicked, mood)
            table.attach(self.mood_buttons[mood], x, y, 1, 1)

            # Calculate the next position
            x += 1
            if x >= self.COLS:
                x = 0
                y += 1

        if self.mood in pep.MOODS:
            self.mood_buttons[self.mood].set_active(True)
            self.label.set_text(pep.MOODS[self.mood])
            self.entry.set_sensitive(True)
            if self.text:
                self.entry.set_text(self.text)
        else:
            self.label.set_text(_('None'))
            self.entry.set_text('')
            self.entry.set_sensitive(False)

        self.xml.connect_signals(self)
        self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
        self.window.show_all()

    def on_mood_button_clicked(self, widget, data):
        if data:
            self.label.set_text(pep.MOODS[data])
            self.entry.set_sensitive(True)
        else:
            self.label.set_text(_('None'))
            self.entry.set_text('')
            self.entry.set_sensitive(False)
        self.mood = data

    def on_ok_button_clicked(self, widget):
        '''Return mood and messsage (None if no mood selected)'''
        message = self.entry.get_text()
        self.on_response(self.mood, message)
        self.window.destroy()

    def on_cancel_button_clicked(self, widget):
        self.window.destroy()

class TimeoutDialog:
    """
    Class designed to be derivated to create timeout'd dialogs (dialogs that
    closes automatically after a timeout)
    """
    def __init__(self, timeout):
        self.countdown_left = timeout
        self.countdown_enabled = True
        self.title_text = ''

    def run_timeout(self):
        if self.countdown_left > 0:
            self.countdown()
            GLib.timeout_add_seconds(1, self.countdown)

    def on_timeout(self):
        """
        To be implemented in derivated classes
        """
        pass

    def countdown(self):
        if self.countdown_enabled:
            if self.countdown_left <= 0:
                self.on_timeout()
                return False
            self.dialog.set_title('%s [%s]' % (self.title_text,
                    str(self.countdown_left)))
            self.countdown_left -= 1
            return True
        else:
            self.dialog.set_title(self.title_text)
            return False

class ChangeStatusMessageDialog(TimeoutDialog):
    def __init__(self, on_response, show=None, show_pep=True):
        countdown_time = app.config.get('change_status_window_timeout')
        TimeoutDialog.__init__(self, countdown_time)
        self.show = show
        self.pep_dict = {}
        self.show_pep = show_pep
        self.on_response = on_response
        self.xml = gtkgui_helpers.get_gtk_builder('change_status_message_dialog.ui')
        self.dialog = self.xml.get_object('change_status_message_dialog')
        self.dialog.set_transient_for(app.interface.roster.window)
        msg = None
        if show:
            uf_show = helpers.get_uf_show(show)
            self.title_text = _('%s Status Message') % uf_show
            msg = app.config.get_per('statusmsg', '_last_' + self.show,
                                                               'message')
            self.pep_dict['activity'] = app.config.get_per('statusmsg',
                '_last_' + self.show, 'activity')
            self.pep_dict['subactivity'] = app.config.get_per('statusmsg',
                '_last_' + self.show, 'subactivity')
            self.pep_dict['activity_text'] = app.config.get_per('statusmsg',
                '_last_' + self.show, 'activity_text')
            self.pep_dict['mood'] = app.config.get_per('statusmsg',
                '_last_' + self.show, 'mood')
            self.pep_dict['mood_text'] = app.config.get_per('statusmsg',
                '_last_' + self.show, 'mood_text')
        else:
            self.title_text = _('Status Message')
        self.dialog.set_title(self.title_text)

        message_textview = self.xml.get_object('message_textview')
        self.message_buffer = message_textview.get_buffer()
        self.message_buffer.connect('changed', self.on_message_buffer_changed)
        if not msg:
            msg = ''
        msg = helpers.from_one_line(msg)
        self.message_buffer.set_text(msg)

        # have an empty string selectable, so user can clear msg
        self.preset_messages_dict = {'': ['', '', '', '', '', '']}
        for msg_name in app.config.get_per('statusmsg'):
            if msg_name.startswith('_last_'):
                continue
            opts = []
            for opt in ['message', 'activity', 'subactivity', 'activity_text',
                                    'mood', 'mood_text']:
                opts.append(app.config.get_per('statusmsg', msg_name, opt))
            opts[0] = helpers.from_one_line(opts[0])
            self.preset_messages_dict[msg_name] = opts
        sorted_keys_list = helpers.get_sorted_keys(self.preset_messages_dict)

        self.message_liststore = Gtk.ListStore(str) # msg_name
        self.message_combobox = self.xml.get_object('message_combobox')
        self.message_combobox.set_model(self.message_liststore)
        cellrenderertext = Gtk.CellRendererText()
        self.message_combobox.pack_start(cellrenderertext, True)
        self.message_combobox.add_attribute(cellrenderertext, 'text', 0)
        for msg_name in sorted_keys_list:
            self.message_liststore.append((msg_name,))

        if show_pep:
            self.draw_activity()
            self.draw_mood()
        else:
            # remove acvtivity / mood lines
            self.xml.get_object('activity_label').set_no_show_all(True)
            self.xml.get_object('activity_button').set_no_show_all(True)
            self.xml.get_object('mood_label').set_no_show_all(True)
            self.xml.get_object('mood_button').set_no_show_all(True)
            self.xml.get_object('activity_label').hide()
            self.xml.get_object('activity_button').hide()
            self.xml.get_object('mood_label').hide()
            self.xml.get_object('mood_button').hide()

        self.xml.connect_signals(self)
        self.run_timeout()
        self.dialog.connect('response', self.on_dialog_response)
        self.dialog.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
        self.dialog.show_all()

    def draw_activity(self):
        """
        Set activity button
        """
        img = self.xml.get_object('activity_image')
        label = self.xml.get_object('activity_button_label')
        if 'activity' in self.pep_dict and self.pep_dict['activity'] in \
           pep.ACTIVITIES:
            if 'subactivity' in self.pep_dict and self.pep_dict['subactivity'] \
            in pep.ACTIVITIES[self.pep_dict['activity']]:
                img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
                    self.pep_dict['activity'], self.pep_dict['subactivity']).\
                        get_pixbuf())
            else:
                img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
                    self.pep_dict['activity']).get_pixbuf())
            if self.pep_dict['activity_text']:
                label.set_text(self.pep_dict['activity_text'])
            else:
                label.set_text('')
        else:
            img.set_from_pixbuf(None)
            label.set_text('')

    def draw_mood(self):
        """
        Set mood button
        """
        img = self.xml.get_object('mood_image')
        label = self.xml.get_object('mood_button_label')
        if 'mood' in self.pep_dict and self.pep_dict['mood'] in pep.MOODS:
            img.set_from_pixbuf(gtkgui_helpers.load_mood_icon(
                self.pep_dict['mood']).get_pixbuf())
            if self.pep_dict['mood_text']:
                label.set_text(self.pep_dict['mood_text'])
            else:
                label.set_text('')
        else:
            img.set_from_pixbuf(None)
            label.set_text('')

    def on_timeout(self):
        # Prevent GUI freeze when the combobox menu is opened on close
        self.message_combobox.popdown()
        self.dialog.response(Gtk.ResponseType.OK)

    def on_dialog_response(self, dialog, response):
        if response == Gtk.ResponseType.OK:
            beg, end = self.message_buffer.get_bounds()
            message = self.message_buffer.get_text(beg, end, True).strip()
            message = helpers.remove_invalid_xml_chars(message)
            msg = helpers.to_one_line(message)
            if self.show:
                app.config.set_per('statusmsg', '_last_' + self.show,
                    'message', msg)
                if self.show_pep:
                    app.config.set_per('statusmsg', '_last_' + self.show,
                        'activity', self.pep_dict['activity'])
                    app.config.set_per('statusmsg', '_last_' + self.show,
                        'subactivity', self.pep_dict['subactivity'])
                    app.config.set_per('statusmsg', '_last_' + self.show,
                        'activity_text', self.pep_dict['activity_text'])
                    app.config.set_per('statusmsg', '_last_' + self.show,
                        'mood', self.pep_dict['mood'])
                    app.config.set_per('statusmsg', '_last_' + self.show,
                        'mood_text', self.pep_dict['mood_text'])
        else:
            message = None # user pressed Cancel button or X wm button
        self.dialog.destroy()
        self.on_response(message, self.pep_dict)

    def on_message_combobox_changed(self, widget):
        self.countdown_enabled = False
        model = widget.get_model()
        active = widget.get_active()
        if active < 0:
            return None
        name = model[active][0]
        self.message_buffer.set_text(self.preset_messages_dict[name][0])
        self.pep_dict['activity'] = self.preset_messages_dict[name][1]
        self.pep_dict['subactivity'] = self.preset_messages_dict[name][2]
        self.pep_dict['activity_text'] = self.preset_messages_dict[name][3]
        self.pep_dict['mood'] = self.preset_messages_dict[name][4]
        self.pep_dict['mood_text'] = self.preset_messages_dict[name][5]
        self.draw_activity()
        self.draw_mood()

    def on_change_status_message_dialog_key_press_event(self, widget, event):
        self.countdown_enabled = False
        if event.keyval == Gdk.KEY_Return or \
           event.keyval == Gdk.KEY_KP_Enter: # catch CTRL+ENTER
            if (event.get_state() & Gdk.ModifierType.CONTROL_MASK):
                self.dialog.response(Gtk.ResponseType.OK)
                # Stop the event
                return True

    def on_message_buffer_changed(self, widget):
        self.countdown_enabled = False
        self.toggle_sensitiviy_of_save_as_preset()

    def toggle_sensitiviy_of_save_as_preset(self):
        btn = self.xml.get_object('save_as_preset_button')
        if self.message_buffer.get_char_count() == 0:
            btn.set_sensitive(False)
        else:
            btn.set_sensitive(True)

    def on_save_as_preset_button_clicked(self, widget):
        self.countdown_enabled = False
        start_iter, finish_iter = self.message_buffer.get_bounds()
        status_message_to_save_as_preset = self.message_buffer.get_text(
                start_iter, finish_iter, True)
        def on_ok(msg_name):
            msg_text = status_message_to_save_as_preset
            msg_text_1l = helpers.to_one_line(msg_text)
            if not msg_name: # msg_name was ''
                msg_name = msg_text_1l

            def on_ok2():
                self.preset_messages_dict[msg_name] = [
                    msg_text, self.pep_dict.get('activity'),
                    self.pep_dict.get('subactivity'),
                    self.pep_dict.get('activity_text'),
                    self.pep_dict.get('mood'), self.pep_dict.get('mood_text')]
                app.config.set_per('statusmsg', msg_name, 'message',
                    msg_text_1l)
                app.config.set_per('statusmsg', msg_name, 'activity',
                    self.pep_dict.get('activity'))
                app.config.set_per('statusmsg', msg_name, 'subactivity',
                    self.pep_dict.get('subactivity'))
                app.config.set_per('statusmsg', msg_name, 'activity_text',
                    self.pep_dict.get('activity_text'))
                app.config.set_per('statusmsg', msg_name, 'mood',
                    self.pep_dict.get('mood'))
                app.config.set_per('statusmsg', msg_name, 'mood_text',
                    self.pep_dict.get('mood_text'))
            if msg_name in self.preset_messages_dict:
                ConfirmationDialog(_('Overwrite Status Message?'),
                    _('This name is already used. Do you want to overwrite this '
                    'status message?'), on_response_ok=on_ok2, transient_for=self.dialog)
                return
            app.config.add_per('statusmsg', msg_name)
            on_ok2()
            iter_ = self.message_liststore.append((msg_name,))
            # select in combobox the one we just saved
            self.message_combobox.set_active_iter(iter_)
        InputDialog(_('Save as Preset Status Message'),
            _('Please type a name for this status message'), is_modal=False,
            ok_handler=on_ok)

    def on_activity_button_clicked(self, widget):
        self.countdown_enabled = False
        def on_response(activity, subactivity, text):
            self.pep_dict['activity'] = activity or ''
            self.pep_dict['subactivity'] = subactivity or ''
            self.pep_dict['activity_text'] = text
            self.draw_activity()
        ChangeActivityDialog(on_response, self.pep_dict['activity'],
            self.pep_dict['subactivity'], self.pep_dict['activity_text'])

    def on_mood_button_clicked(self, widget):
        self.countdown_enabled = False
        def on_response(mood, text):
            self.pep_dict['mood'] = mood or ''
            self.pep_dict['mood_text'] = text
            self.draw_mood()
        ChangeMoodDialog(on_response, self.pep_dict['mood'],
                                         self.pep_dict['mood_text'])

class AddNewContactWindow:
    """
    Class for AddNewContactWindow
    """

    uid_labels = {'jabber': _('JID:'),
        'aim': _('AIM Address:'),
        'gadu-gadu': _('GG Number:'),
        'icq': _('ICQ Number:'),
        'msn': _('MSN Address:'),
        'yahoo': _('Yahoo! Address:')}

    def __init__(self, account=None, jid=None, user_nick=None, group=None):
        self.account = account
        self.adding_jid = False
        if account is None:
            # fill accounts with active accounts
            accounts = []
            for account in app.connections.keys():
                if app.connections[account].connected > 1:
                    accounts.append(account)
            if not accounts:
                return
            if len(accounts) == 1:
                self.account = account
        else:
            accounts = [self.account]
        if self.account:
            location = app.interface.instances[self.account]
        else:
            location = app.interface.instances
        if 'add_contact' in location:
            location['add_contact'].window.present()
            # An instance is already opened
            return
        location['add_contact'] = self
        self.xml = gtkgui_helpers.get_gtk_builder('add_new_contact_window.ui')
        self.xml.connect_signals(self)
        self.window = self.xml.get_object('add_new_contact_window')
        for w in ('account_combobox', 'account_hbox', 'account_label',
        'uid_label', 'uid_entry', 'protocol_combobox', 'protocol_jid_combobox',
        'protocol_hbox', 'nickname_entry', 'message_scrolledwindow',
        'save_message_checkbutton', 'register_hbox', 'subscription_table',
        'add_button', 'message_textview', 'connected_label',
        'group_comboboxentry', 'auto_authorize_checkbutton'):
            self.__dict__[w] = self.xml.get_object(w)
        if account and len(app.connections) >= 2:
            self.default_desc = _('Please fill in the data of the contact you want '
                                  'to add to your account <b>%s</b>') % account
        else:
            self.default_desc = _('Please fill in the data of the contact you '
                'want to add')
        self.xml.get_object('prompt_label').set_markup(self.default_desc)
        self.agents = {'jabber': []}
        self.gateway_prompt = {}
        # types to which we are not subscribed but account has an agent for it
        self.available_types = []
        for acct in accounts:
            for j in app.contacts.get_jid_list(acct):
                if app.jid_is_transport(j):
                    type_ = app.get_transport_name_from_jid(j, False)
                    if not type_:
                        continue
                    if type_ in self.agents:
                        self.agents[type_].append(j)
                    else:
                        self.agents[type_] = [j]
                    self.gateway_prompt[j] = {'desc': None, 'prompt': None}
        # Now add the one to which we can register
        for acct in accounts:
            for type_ in app.connections[acct].available_transports:
                if type_ in self.agents:
                    continue
                self.agents[type_] = []
                for jid_ in app.connections[acct].available_transports[type_]:
                    if not jid_ in self.agents[type_]:
                        self.agents[type_].append(jid_)
                        self.gateway_prompt[jid_] = {'desc': None,
                            'prompt': None}
                self.available_types.append(type_)
        # Combobox with transport/jabber icons
        liststore = Gtk.ListStore(str, str, str)
        cell = Gtk.CellRendererPixbuf()
        self.protocol_combobox.pack_start(cell, False)
        self.protocol_combobox.add_attribute(cell, 'icon_name', 1)
        cell = Gtk.CellRendererText()
        cell.set_property('xpad', 5)
        self.protocol_combobox.pack_start(cell, True)
        self.protocol_combobox.add_attribute(cell, 'text', 0)
        self.protocol_combobox.set_model(liststore)
        uf_type = {'jabber': 'XMPP', 'aim': 'AIM', 'gadu-gadu': 'Gadu Gadu',
            'icq': 'ICQ', 'msn': 'MSN', 'yahoo': 'Yahoo'}
        # Jabber as first
        online_icon = gtkgui_helpers.get_iconset_name_for('online')
        liststore.append(['XMPP', online_icon, 'jabber'])
        for type_ in self.agents:
            if type_ == 'jabber':
                continue
            if type_ in uf_type:
                liststore.append([uf_type[type_], type_ + '-online', type_])
            else:
                liststore.append([type_, type_ + '-online', type_])

            if account:
                for service in self.agents[type_]:
                    app.connections[account].request_gateway_prompt(service)
        self.protocol_combobox.set_active(0)
        self.auto_authorize_checkbutton.show()
        liststore = Gtk.ListStore(str)
        self.protocol_jid_combobox.set_model(liststore)
        if jid:
            self.jid_escaped = True
            type_ = app.get_transport_name_from_jid(jid)
            if not type_:
                type_ = 'jabber'
            if type_ == 'jabber':
                self.uid_entry.set_text(jid)
            else:
                uid, transport = app.get_name_and_server_from_jid(jid)
                self.uid_entry.set_text(uid.replace('%', '@', 1))
            # set protocol_combobox
            model = self.protocol_combobox.get_model()
            iter_ = model.get_iter_first()
            i = 0
            while iter_:
                if model[iter_][2] == type_:
                    self.protocol_combobox.set_active(i)
                    break
                iter_ = model.iter_next(iter_)
                i += 1

            # set protocol_jid_combobox
            self.protocol_jid_combobox.set_active(0)
            model = self.protocol_jid_combobox.get_model()
            iter_ = model.get_iter_first()
            i = 0
            while iter_:
                if model[iter_][0] == transport:
                    self.protocol_jid_combobox.set_active(i)
                    break
                iter_ = model.iter_next(iter_)
                i += 1
            if user_nick:
                self.nickname_entry.set_text(user_nick)
            self.nickname_entry.grab_focus()
        else:
            self.jid_escaped = False
            self.uid_entry.grab_focus()
        group_names = []
        for acct in accounts:
            for g in app.groups[acct].keys():
                if g not in helpers.special_groups and g not in group_names:
                    group_names.append(g)
        group_names.sort()
        i = 0
        for g in group_names:
            self.group_comboboxentry.append_text(g)
            if group == g:
                self.group_comboboxentry.set_active(i)
            i += 1

        self.window.set_transient_for(app.interface.roster.window)
        self.window.show_all()

        if self.account:
            self.account_label.hide()
            self.account_hbox.hide()
        else:
            liststore = Gtk.ListStore(str, str)
            for acct in accounts:
                liststore.append([acct, acct])
            self.account_combobox.set_model(liststore)
            self.account_combobox.set_active(0)

        if self.account:
            message_buffer = self.message_textview.get_buffer()
            msg = helpers.from_one_line(helpers.get_subscription_request_msg(
                self.account))
            message_buffer.set_text(msg)

        app.ged.register_event_handler('gateway-prompt-received', ged.GUI1,
            self._nec_gateway_prompt_received)
        app.ged.register_event_handler('presence-received', ged.GUI1,
            self._nec_presence_received)

    def on_add_new_contact_window_destroy(self, widget):
        if self.account:
            location = app.interface.instances[self.account]
        else:
            location = app.interface.instances
        del location['add_contact']
        app.ged.remove_event_handler('presence-received', ged.GUI1,
            self._nec_presence_received)
        app.ged.remove_event_handler('gateway-prompt-received', ged.GUI1,
            self._nec_gateway_prompt_received)

    def on_register_button_clicked(self, widget):
        model = self.protocol_jid_combobox.get_model()
        row = self.protocol_jid_combobox.get_active()
        jid = model[row][0]
        app.connections[self.account].request_register_agent_info(jid)

    def on_add_new_contact_window_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape: # ESCAPE
            self.window.destroy()

    def on_cancel_button_clicked(self, widget):
        """
        When Cancel button is clicked
        """
        self.window.destroy()

    def on_add_button_clicked(self, widget):
        """
        When Subscribe button is clicked
        """
        jid = self.uid_entry.get_text().strip()
        if not jid:
            return

        model = self.protocol_combobox.get_model()
        row = self.protocol_combobox.get_active_iter()
        type_ = model[row][2]
        if type_ != 'jabber':
            model = self.protocol_jid_combobox.get_model()
            row = self.protocol_jid_combobox.get_active()
            transport = model[row][0]
            if self.account and not self.jid_escaped:
                self.adding_jid = (jid, transport, type_)
                app.connections[self.account].request_gateway_prompt(
                    transport, jid)
            else:
                jid = jid.replace('@', '%') + '@' + transport
                self._add_jid(jid, type_)
        else:
            self._add_jid(jid, type_)

    def _add_jid(self, jid, type_):
        # check if jid is conform to RFC and stringprep it
        try:
            jid = helpers.parse_jid(jid)
        except helpers.InvalidFormat as s:
            pritext = _('Invalid User ID')
            ErrorDialog(pritext, str(s))
            return

        # No resource in jid
        if jid.find('/') >= 0:
            pritext = _('Invalid User ID')
            ErrorDialog(pritext, _('The user ID must not contain a resource.'))
            return

        if jid == app.get_jid_from_account(self.account):
            pritext = _('Invalid User ID')
            ErrorDialog(pritext, _('You cannot add yourself to your roster.'))
            return

        nickname = self.nickname_entry.get_text() or ''
        # get value of account combobox, if account was not specified
        if not self.account:
            model = self.account_combobox.get_model()
            index = self.account_combobox.get_active()
            self.account = model[index][1]

        # Check if jid is already in roster
        if jid in app.contacts.get_jid_list(self.account):
            c = app.contacts.get_first_contact_from_jid(self.account, jid)
            if _('Not in Roster') not in c.groups and c.sub in ('both', 'to'):
                ErrorDialog(_('Contact already in roster'),
                    _('This contact is already listed in your roster.'))
                return

        if type_ == 'jabber':
            message_buffer = self.message_textview.get_buffer()
            start_iter = message_buffer.get_start_iter()
            end_iter = message_buffer.get_end_iter()
            message = message_buffer.get_text(start_iter, end_iter, True)
            if self.save_message_checkbutton.get_active():
                msg = helpers.to_one_line(message)
                app.config.set_per('accounts', self.account,
                    'subscription_request_msg', msg)
        else:
            message= ''
        group = self.group_comboboxentry.get_child().get_text()
        groups = []
        if group:
            groups = [group]
        auto_auth = self.auto_authorize_checkbutton.get_active()
        app.interface.roster.req_sub(self, jid, message, self.account,
            groups=groups, nickname=nickname, auto_auth=auto_auth)
        self.window.destroy()

    def on_account_combobox_changed(self, widget):
        model = widget.get_model()
        iter_ = widget.get_active_iter()
        account = model[iter_][0]
        message_buffer = self.message_textview.get_buffer()
        message_buffer.set_text(helpers.get_subscription_request_msg(account))

    def on_protocol_jid_combobox_changed(self, widget):
        model = widget.get_model()
        iter_ = widget.get_active_iter()
        if not iter_:
            return
        jid_ = model[iter_][0]
        model = self.protocol_combobox.get_model()
        iter_ = self.protocol_combobox.get_active_iter()
        type_ = model[iter_][2]
        desc = None
        if self.agents[type_] and jid_ in self.gateway_prompt:
            desc = self.gateway_prompt[jid_]['desc']
        if not desc:
            desc = self.default_desc
        self.xml.get_object('prompt_label').set_markup(desc)

        prompt = None
        if self.agents[type_] and jid_ in self.gateway_prompt:
            prompt = self.gateway_prompt[jid_]['prompt']
        if not prompt:
            if type_ in self.uid_labels:
                prompt = self.uid_labels[type_]
            else:
                prompt = _('User ID:')
        self.uid_label.set_text(prompt)

    def on_protocol_combobox_changed(self, widget):
        model = widget.get_model()
        iter_ = widget.get_active_iter()
        type_ = model[iter_][2]
        model = self.protocol_jid_combobox.get_model()
        model.clear()
        if len(self.agents[type_]):
            for jid_ in self.agents[type_]:
                model.append([jid_])
            self.protocol_jid_combobox.set_active(0)
        desc = None
        if self.agents[type_]:
            jid_ = self.agents[type_][0]
            if jid_ in self.gateway_prompt:
                desc = self.gateway_prompt[jid_]['desc']
        if not desc:
            desc = self.default_desc
        self.xml.get_object('prompt_label').set_markup(desc)
        if len(self.agents[type_]) > 1:
            self.protocol_jid_combobox.show()
        else:
            self.protocol_jid_combobox.hide()
        prompt = None
        if self.agents[type_]:
            jid_ = self.agents[type_][0]
            if jid_ in self.gateway_prompt:
                prompt = self.gateway_prompt[jid_]['prompt']
        if not prompt:
            if type_ in self.uid_labels:
                prompt = self.uid_labels[type_]
            else:
                prompt = _('User ID:')
        self.uid_label.set_text(prompt)

        if type_ == 'jabber':
            self.message_scrolledwindow.show()
            self.save_message_checkbutton.show()
        else:
            self.message_scrolledwindow.hide()
            self.save_message_checkbutton.hide()
        if type_ in self.available_types:
            self.register_hbox.show()
            self.auto_authorize_checkbutton.hide()
            self.connected_label.hide()
            self.subscription_table.hide()
            self.add_button.set_sensitive(False)
        else:
            self.register_hbox.hide()
            if type_ != 'jabber':
                model = self.protocol_jid_combobox.get_model()
                row = self.protocol_jid_combobox.get_active()
                jid = model[row][0]
                contact = app.contacts.get_first_contact_from_jid(
                    self.account, jid)
                if contact.show in ('offline', 'error'):
                    self.subscription_table.hide()
                    self.connected_label.show()
                    self.add_button.set_sensitive(False)
                    self.auto_authorize_checkbutton.hide()
                    return
            self.subscription_table.show()
            self.auto_authorize_checkbutton.show()
            self.connected_label.hide()
            self.add_button.set_sensitive(True)

    def transport_signed_in(self, jid):
        model = self.protocol_jid_combobox.get_model()
        row = self.protocol_jid_combobox.get_active()
        _jid = model[row][0]
        if _jid == jid:
            self.register_hbox.hide()
            self.connected_label.hide()
            self.subscription_table.show()
            self.auto_authorize_checkbutton.show()
            self.add_button.set_sensitive(True)

    def transport_signed_out(self, jid):
        model = self.protocol_jid_combobox.get_model()
        row = self.protocol_jid_combobox.get_active()
        _jid = model[row][0]
        if _jid == jid:
            self.subscription_table.hide()
            self.auto_authorize_checkbutton.hide()
            self.connected_label.show()
            self.add_button.set_sensitive(False)

    def _nec_presence_received(self, obj):
        if app.jid_is_transport(obj.jid):
            if obj.old_show == 0 and obj.new_show > 1:
                self.transport_signed_in(obj.jid)
            elif obj.old_show > 1 and obj.new_show == 0:
                self.transport_signed_out(obj.jid)

    def _nec_gateway_prompt_received(self, obj):
        if self.adding_jid:
            jid, transport, type_ = self.adding_jid
            if obj.stanza.getError():
                ErrorDialog(_('Error while adding transport contact'),
                    _('This error occured while adding a contact for transport '
                    '%(transport)s:\n\n%(error)s') % {'transport': transport,
                    'error': obj.stanza.getErrorMsg()})
                return
            if obj.prompt_jid:
                self._add_jid(obj.prompt_jid, type_)
            else:
                jid = jid.replace('@', '%') + '@' + transport
                self._add_jid(jid, type_)
        elif obj.jid in self.gateway_prompt:
            if obj.desc:
                self.gateway_prompt[obj.jid]['desc'] = obj.desc
            if obj.prompt:
                self.gateway_prompt[obj.jid]['prompt'] = obj.prompt

class AboutDialog(Gtk.AboutDialog):
    def __init__(self):
        Gtk.AboutDialog.__init__(self)
        self.set_transient_for(app.interface.roster.window)
        self.set_name('Gajim')
        self.set_version(app.version)
        self.set_copyright('Copyright © 2003-2018 Gajim Team')
        self.set_license_type(Gtk.License.GPL_3_0_ONLY)
        self.set_website('https://gajim.org/')

        gtk_ver = '%i.%i.%i' % (
            Gtk.get_major_version(),
            Gtk.get_minor_version(),
            Gtk.get_micro_version())
        gobject_ver = '.'.join(map(str, GObject.pygobject_version))

        comments = []
        comments.append(_('A GTK+ XMPP client'))
        comments.append(_('GTK+ Version: %s' % gtk_ver))
        comments.append(_('PyGObject Version: %s') % gobject_ver)
        comments.append(_('python-nbxmpp Version: %s') % nbxmpp.__version__)
        self.set_comments("\n".join(comments))

        self.add_credit_section(_('Current Developers'), const.DEVS_CURRENT)
        self.add_credit_section(_('Past Developers'), const.DEVS_PAST)
        self.add_credit_section(_('Artists'), const.ARTISTS)

        thanks = const.THANKS[:]
        thanks.append('')
        thanks.append(_('Last but not least'))
        thanks.append(_('we would like to thank all the package maintainers.'))
        self.add_credit_section(_('Thanks'), thanks)

        self.set_translator_credits(_('translator-credits'))
        self.set_logo_icon_name('org.gajim.Gajim')

        self.connect(
            'response', lambda dialog, *args: Gtk.AboutDialog.do_close(dialog))

        self.show_all()

        # GTK Bug, We have to manually hide the License Button
        # Bug exists only on Windows
        if os.name == 'nt':
            self.get_action_area().get_children()[1].hide()


class Dialog(Gtk.Dialog):
    def __init__(self, parent, title, buttons, default=None,
    on_response_ok=None, on_response_cancel=None):
        Gtk.Dialog.__init__(self, title, parent,
            Gtk.DialogFlags.DESTROY_WITH_PARENT)

        self.user_response_ok = on_response_ok
        self.user_response_cancel = on_response_cancel
        self.set_border_width(6)
        self.get_content_area().set_spacing(12)
        self.set_resizable(False)

        for stock, response in buttons:
            b = self.add_button(stock, response)

        if default is not None:
            self.set_default_response(default)
        else:
            self.set_default_response(buttons[-1][1])

        self.connect('response', self.on_response)

    def on_response(self, widget, response_id):
        if response_id == Gtk.ResponseType.OK:
            if self.user_response_ok:
                if isinstance(self.user_response_ok, tuple):
                    self.user_response_ok[0](*self.user_response_ok[1:])
                else:
                    self.user_response_ok()
            self.destroy()
        elif response_id == Gtk.ResponseType.CANCEL:
            if self.user_response_cancel:
                if isinstance(self.user_response_cancel, tuple):
                    self.user_response_cancel[0](*self.user_response_ok[1:])
                else:
                    self.user_response_cancel()
            self.destroy()

    def just_destroy(self, widget):
        self.destroy()

    def get_button(self, index):
        buttons = self.get_action_area().get_children()
        return index < len(buttons) and buttons[index] or None


class HigDialog(Gtk.MessageDialog):
    def __init__(self, parent, type_, buttons, pritext, sectext,
    on_response_ok=None, on_response_cancel=None, on_response_yes=None,
    on_response_no=None):
        self.call_cancel_on_destroy = True
        Gtk.MessageDialog.__init__(self, transient_for=parent,
           modal=True, destroy_with_parent=True,
           message_type=type_, buttons=buttons, text=pritext)

        self.format_secondary_markup(sectext)

        self.possible_responses = {Gtk.ResponseType.OK: on_response_ok,
            Gtk.ResponseType.CANCEL: on_response_cancel,
            Gtk.ResponseType.YES: on_response_yes,
            Gtk.ResponseType.NO: on_response_no}

        self.connect('response', self.on_response)
        self.connect('destroy', self.on_dialog_destroy)

    def on_response(self, dialog, response_id):
        if not response_id in self.possible_responses:
            return
        if not self.possible_responses[response_id]:
            self.destroy()
        elif isinstance(self.possible_responses[response_id], tuple):
            if len(self.possible_responses[response_id]) == 1:
                self.possible_responses[response_id][0](dialog)
            else:
                self.possible_responses[response_id][0](dialog,
                    *self.possible_responses[response_id][1:])
        else:
            self.possible_responses[response_id](dialog)


    def on_dialog_destroy(self, widget):
        if not self.call_cancel_on_destroy:
            return
        cancel_handler = self.possible_responses[Gtk.ResponseType.CANCEL]
        if not cancel_handler:
            return False
        if isinstance(cancel_handler, tuple):
            cancel_handler[0](None, *cancel_handler[1:])
        else:
            cancel_handler(None)

    def popup(self):
        """
        Show dialog
        """
        vb = self.get_children()[0].get_children()[0] # Give focus to top vbox
#        vb.set_flags(Gtk.CAN_FOCUS)
        vb.grab_focus()
        self.show_all()

class FileChooserDialog(Gtk.FileChooserDialog):
    """
    Non-blocking FileChooser Dialog around Gtk.FileChooserDialog
    """
    def __init__(self, title_text, action, buttons, default_response,
    select_multiple=False, current_folder=None, on_response_ok=None,
    on_response_cancel=None, preview=False, transient_for=None):

        Gtk.FileChooserDialog.__init__(self, title=title_text,
            parent=transient_for, action=action)
        self.add_button(buttons[0],buttons[1])
        if len(buttons) ==4:
            self.add_button(buttons[2],buttons[3])
        self.set_default_response(default_response)
        self.set_select_multiple(select_multiple)
        if current_folder and os.path.isdir(current_folder):
            self.set_current_folder(current_folder)
        else:
            self.set_current_folder(os.path.expanduser('~'))
        self.response_ok, self.response_cancel = \
                on_response_ok, on_response_cancel
        # in gtk+-2.10 clicked signal on some of the buttons in a dialog
        # is emitted twice, so we cannot rely on 'clicked' signal
        self.connect('response', self.on_dialog_response)

        if preview:
            self.set_use_preview_label(False)
            self.set_preview_widget(Gtk.Image())
            self.connect('selection-changed', self.update_preview)

        self.show_all()

    def on_dialog_response(self, dialog, response):
        if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.CLOSE):
            if self.response_cancel:
                if isinstance(self.response_cancel, tuple):
                    self.response_cancel[0](dialog, *self.response_cancel[1:])
                else:
                    self.response_cancel(dialog)
            else:
                self.just_destroy(dialog)
        elif response == Gtk.ResponseType.OK:
            if self.response_ok:
                if isinstance(self.response_ok, tuple):
                    self.response_ok[0](dialog, *self.response_ok[1:])
                else:
                    self.response_ok(dialog)
            else:
                self.just_destroy(dialog)

    def update_preview(self, widget):
        path_to_file = widget.get_preview_filename()
        preview = widget.get_preview_widget()
        if path_to_file is None or os.path.isdir(path_to_file):
            # nothing to preview or directory
            # make sure you clean image do show nothing
            preview.clear()
            return
        try:
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path_to_file, 200, 200)
        except GObject.GError:
            preview.clear()
            return
        widget.get_preview_widget().set_from_pixbuf(pixbuf)

    def just_destroy(self, widget):
        self.destroy()

class AspellDictError:
    def __init__(self, lang):
        ErrorDialog(
            _('Dictionary for language "%s" not available') % lang,
            _('You have to install the dictionary "%s" to use spellchecking, '
              'or choose another language by setting the speller_language '
              'option.\n\n'
              'Highlighting misspelled words feature will not be used') % lang)

class ConfirmationDialog(HigDialog):
    """
    HIG compliant confirmation dialog
    """

    def __init__(self, pritext, sectext='', on_response_ok=None,
    on_response_cancel=None, transient_for=None):
        self.user_response_ok = on_response_ok
        self.user_response_cancel = on_response_cancel
        HigDialog.__init__(self, transient_for,
           Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, pritext, sectext,
           self.on_response_ok, self.on_response_cancel)
        self.popup()

    def on_response_ok(self, widget):
        if self.user_response_ok:
            if isinstance(self.user_response_ok, tuple):
                self.user_response_ok[0](*self.user_response_ok[1:])
            else:
                self.user_response_ok()
        self.call_cancel_on_destroy = False
        self.destroy()

    def on_response_cancel(self, widget):
        if self.user_response_cancel:
            if isinstance(self.user_response_cancel, tuple):
                self.user_response_cancel[0](*self.user_response_ok[1:])
            else:
                self.user_response_cancel()
        self.call_cancel_on_destroy = False
        self.destroy()

class NonModalConfirmationDialog(HigDialog):
    """
    HIG compliant non modal confirmation dialog
    """

    def __init__(self, pritext, sectext='', on_response_ok=None,
    on_response_cancel=None):
        self.user_response_ok = on_response_ok
        self.user_response_cancel = on_response_cancel
        if hasattr(app.interface, 'roster') and app.interface.roster:
            parent = app.interface.roster.window
        else:
            parent = None
        HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
            Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
            self.on_response_cancel)
        self.set_modal(False)

    def on_response_ok(self, widget):
        if self.user_response_ok:
            if isinstance(self.user_response_ok, tuple):
                self.user_response_ok[0](*self.user_response_ok[1:])
            else:
                self.user_response_ok()
        self.call_cancel_on_destroy = False
        self.destroy()

    def on_response_cancel(self, widget):
        if self.user_response_cancel:
            if isinstance(self.user_response_cancel, tuple):
                self.user_response_cancel[0](*self.user_response_cancel[1:])
            else:
                self.user_response_cancel()
        self.call_cancel_on_destroy = False
        self.destroy()

class WarningDialog(HigDialog):
    """
    HIG compliant warning dialog
    """

    def __init__(self, pritext, sectext='', transient_for=None):
        if not transient_for and hasattr(app.interface, 'roster') and \
        app.interface.roster:
            transient_for = app.interface.roster.window
        HigDialog.__init__(self, transient_for, Gtk.MessageType.WARNING,
            Gtk.ButtonsType.OK, pritext, sectext)
        self.set_modal(False)
        self.popup()

class InformationDialog(HigDialog):
    """
    HIG compliant info dialog
    """

    def __init__(self, pritext, sectext='', transient_for=None):
        if transient_for:
            parent = transient_for
        elif hasattr(app.interface, 'roster') and app.interface.roster:
            parent = app.interface.roster.window
        else:
            parent = None
        HigDialog.__init__(self, parent, Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
            pritext, sectext)
        self.set_modal(False)
        self.popup()

class ErrorDialog(HigDialog):
    """
    HIG compliant error dialog
    """

    def __init__(self, pritext, sectext='', on_response_ok=None,
    on_response_cancel=None, transient_for=None):
        if transient_for:
            parent = transient_for
        elif hasattr(app.interface, 'roster') and app.interface.roster:
            parent = app.interface.roster.window
        else:
            parent = None
        HigDialog.__init__(self, parent, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
            pritext, sectext, on_response_ok=on_response_ok,
            on_response_cancel=on_response_cancel)
        self.popup()

class YesNoDialog(HigDialog):
    """
    HIG compliant YesNo dialog
    """

    def __init__(self, pritext, sectext='', checktext='', text_label=None,
    on_response_yes=None, on_response_no=None, type_=Gtk.MessageType.QUESTION,
    transient_for=None):
        self.user_response_yes = on_response_yes
        self.user_response_no = on_response_no
        if transient_for:
            parent = transient_for
        elif hasattr(app.interface, 'roster') and app.interface.roster:
            parent = app.interface.roster.window
        else:
            parent = None
        HigDialog.__init__(self, parent, type_, Gtk.ButtonsType.YES_NO, pritext,
            sectext, on_response_yes=self.on_response_yes,
            on_response_no=self.on_response_no)

        vbox = self.get_content_area()
        if checktext:
            self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
            vbox.pack_start(self.checkbutton, False, True, 0)
        else:
            self.checkbutton = None
        if text_label:
            label = Gtk.Label(label=text_label)
            vbox.pack_start(label, False, True, 0)
            buff = Gtk.TextBuffer()
            self.textview = Gtk.TextView.new_with_buffer(buff)
            frame = Gtk.Frame()
            frame.set_shadow_type(Gtk.ShadowType.IN)
            frame.add(self.textview)
            vbox.pack_start(frame, False, True, 0)
        else:
            self.textview = None
        self.set_modal(False)
        self.popup()

    def on_response_yes(self, widget):
        if self.user_response_yes:
            if self.textview:
                buff = self.textview.get_buffer()
                start, end = buff.get_bounds()
                txt = self.textview.get_buffer().get_text(start, end, True)

            if isinstance(self.user_response_yes, tuple):
                if self.textview:
                    self.user_response_yes[0](self.is_checked(), txt,
                        *self.user_response_yes[1:])
                else:
                    self.user_response_yes[0](self.is_checked(),
                        *self.user_response_yes[1:])
            else:
                if self.textview:
                    self.user_response_yes(self.is_checked(), txt)
                else:
                    self.user_response_yes(self.is_checked())
        self.call_cancel_on_destroy = False
        self.destroy()

    def on_response_no(self, widget):
        if self.user_response_no:
            if self.textview:
                buff = self.textview.get_buffer()
                start, end = buff.get_bounds()
                txt = self.textview.get_buffer().get_text(start, end, True)

            if isinstance(self.user_response_no, tuple):
                if self.textview:
                    self.user_response_no[0](txt, *self.user_response_no[1:])
                else:
                    self.user_response_no[0](*self.user_response_no[1:])
            else:
                if self.textview:
                    self.user_response_no(txt)
                else:
                    self.user_response_no()
        self.call_cancel_on_destroy = False
        self.destroy()

    def is_checked(self):
        """
        Get active state of the checkbutton
        """
        if not self.checkbutton:
            return False
        return self.checkbutton.get_active()

class ConfirmationDialogCheck(ConfirmationDialog):
    """
    HIG compliant confirmation dialog with checkbutton
    """

    def __init__(self, pritext, sectext='', checktext='', on_response_ok=None,
    on_response_cancel=None, is_modal=True, transient_for=None):
        self.user_response_ok = on_response_ok
        self.user_response_cancel = on_response_cancel

        if transient_for:
            parent = transient_for
        elif hasattr(app.interface, 'roster') and app.interface.roster:
            parent = app.interface.roster.window
        else:
            parent = None
        HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
           Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
           self.on_response_cancel)

        self.set_default_response(Gtk.ResponseType.OK)

        ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
        ok_button.grab_focus()

        self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
        self.get_content_area().pack_start(self.checkbutton, False, True, 0)
        self.set_modal(is_modal)
        self.popup()

    def on_response_ok(self, widget):
        if self.user_response_ok:
            if isinstance(self.user_response_ok, tuple):
                self.user_response_ok[0](self.is_checked(),
                    *self.user_response_ok[1:])
            else:
                self.user_response_ok(self.is_checked())
        self.call_cancel_on_destroy = False
        self.destroy()

    def on_response_cancel(self, widget):
        if self.user_response_cancel:
            if isinstance(self.user_response_cancel, tuple):
                self.user_response_cancel[0](self.is_checked(),
                    *self.user_response_cancel[1:])
            else:
                self.user_response_cancel(self.is_checked())
        self.call_cancel_on_destroy = False
        self.destroy()

    def is_checked(self):
        """
        Get active state of the checkbutton
        """
        return self.checkbutton.get_active()

class ConfirmationDialogDoubleCheck(ConfirmationDialog):
    """
    HIG compliant confirmation dialog with 2 checkbuttons
    """

    def __init__(self, pritext, sectext='', checktext1='', checktext2='',
    tooltip1='', tooltip2='', on_response_ok=None, on_response_cancel=None,
    is_modal=True):
        self.user_response_ok = on_response_ok
        self.user_response_cancel = on_response_cancel

        if hasattr(app.interface, 'roster') and app.interface.roster:
            parent = app.interface.roster.window
        else:
            parent = None
        HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
           Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
           self.on_response_cancel)

        self.set_default_response(Gtk.ResponseType.OK)

        ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
        ok_button.grab_focus()

        vbox = self.get_content_area()
        if checktext1:
            self.checkbutton1 = Gtk.CheckButton.new_with_mnemonic(checktext1)
            if tooltip1:
                self.checkbutton1.set_tooltip_text(tooltip1)
            vbox.pack_start(self.checkbutton1, False, True, 0)
        else:
            self.checkbutton1 = None
        if checktext2:
            self.checkbutton2 = Gtk.CheckButton.new_with_mnemonic(checktext2)
            if tooltip2:
                self.checkbutton2.set_tooltip_text(tooltip2)
            vbox.pack_start(self.checkbutton2, False, True, 0)
        else:
            self.checkbutton2 = None

        self.set_modal(is_modal)
        self.popup()

    def on_response_ok(self, widget):
        if self.user_response_ok:
            if isinstance(self.user_response_ok, tuple):
                self.user_response_ok[0](self.is_checked(),
                    *self.user_response_ok[1:])
            else:
                self.user_response_ok(self.is_checked())
        self.call_cancel_on_destroy = False
        self.destroy()

    def on_response_cancel(self, widget):
        if self.user_response_cancel:
            if isinstance(self.user_response_cancel, tuple):
                self.user_response_cancel[0](*self.user_response_cancel[1:])
            else:
                self.user_response_cancel()
        self.call_cancel_on_destroy = False
        self.destroy()

    def is_checked(self):
        ''' Get active state of the checkbutton '''
        if self.checkbutton1:
            is_checked_1 = self.checkbutton1.get_active()
        else:
            is_checked_1 = False
        if self.checkbutton2:
            is_checked_2 = self.checkbutton2.get_active()
        else:
            is_checked_2 = False
        return [is_checked_1, is_checked_2]

class PlainConnectionDialog(ConfirmationDialogDoubleCheck):
    """
    Dialog that is shown when using an insecure connection
    """
    def __init__(self, account, on_ok, on_cancel):
        pritext = _('Insecure connection')
        sectext = _('You are about to connect to the account %(account)s '
            '(%(server)s) insecurely. This means conversations will not be '
            'encrypted, and is strongly discouraged.\nAre you sure you want '
            'to do that?') % {'account': account,
            'server': app.get_hostname_from_account(account)}
        checktext1 = _('Yes, I really want to connect insecurely')
        tooltip1 = _('Gajim will NOT connect unless you check this box')
        checktext2 = _('_Do not ask me again')
        ConfirmationDialogDoubleCheck.__init__(self, pritext, sectext,
            checktext1, checktext2, tooltip1=tooltip1, on_response_ok=on_ok,
            on_response_cancel=on_cancel, is_modal=False)
        self.ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
        self.ok_button.set_sensitive(False)
        self.checkbutton1.connect('clicked', self.on_checkbutton_clicked)
        self.set_title(_('Insecure connection'))

    def on_checkbutton_clicked(self, widget):
        self.ok_button.set_sensitive(widget.get_active())

class ConfirmationDialogDoubleRadio(ConfirmationDialog):
    """
    HIG compliant confirmation dialog with 2 radios
    """

    def __init__(self, pritext, sectext='', radiotext1='', radiotext2='',
    on_response_ok=None, on_response_cancel=None, is_modal=True, transient_for=None):
        self.user_response_ok = on_response_ok
        self.user_response_cancel = on_response_cancel

        if transient_for is None:
            transient_for = app.app.get_active_window()
        HigDialog.__init__(self, transient_for, Gtk.MessageType.QUESTION,
                Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
                self.on_response_cancel)

        self.set_default_response(Gtk.ResponseType.OK)

        ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
        ok_button.grab_focus()

        vbox = self.get_content_area()
        self.radiobutton1 = Gtk.RadioButton(label=radiotext1)
        vbox.pack_start(self.radiobutton1, False, True, 0)

        self.radiobutton2 = Gtk.RadioButton(group=self.radiobutton1,
                label=radiotext2)
        vbox.pack_start(self.radiobutton2, False, True, 0)

        self.set_modal(is_modal)
        self.popup()

    def on_response_ok(self, widget):
        if self.user_response_ok:
            if isinstance(self.user_response_ok, tuple):
                self.user_response_ok[0](self.is_checked(),
                        *self.user_response_ok[1:])
            else:
                self.user_response_ok(self.is_checked())
        self.call_cancel_on_destroy = False
        self.destroy()

    def on_response_cancel(self, widget):
        if self.user_response_cancel:
            if isinstance(self.user_response_cancel, tuple):
                self.user_response_cancel[0](*self.user_response_cancel[1:])
            else:
                self.user_response_cancel()
        self.call_cancel_on_destroy = False
        self.destroy()

    def is_checked(self):
        ''' Get active state of the checkbutton '''
        if self.radiobutton1:
            is_checked_1 = self.radiobutton1.get_active()
        else:
            is_checked_1 = False
        if self.radiobutton2:
            is_checked_2 = self.radiobutton2.get_active()
        else:
            is_checked_2 = False
        return [is_checked_1, is_checked_2]

class FTOverwriteConfirmationDialog(ConfirmationDialog):
    """
    HIG compliant confirmation dialog to overwrite or resume a file transfert
    """

    def __init__(self, pritext, sectext='', propose_resume=True,
    on_response=None, transient_for=None):
        if transient_for:
            parent = transient_for
        elif hasattr(app.interface, 'roster') and app.interface.roster:
            parent = app.interface.roster.window
        else:
            parent = None
        HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
            Gtk.ButtonsType.CANCEL, pritext, sectext)

        self.on_response = on_response

        if propose_resume:
            b = Gtk.Button(label='', stock=Gtk.STOCK_REFRESH)
            align = b.get_children()[0]
            hbox = align.get_children()[0]
            label = hbox.get_children()[1]
            label.set_text(_('_Resume'))
            label.set_use_underline(True)
            self.add_action_widget(b, 100)

        b = Gtk.Button(label='', stock=Gtk.STOCK_SAVE_AS)
        align = b.get_children()[0]
        hbox = align.get_children()[0]
        label = hbox.get_children()[1]
        label.set_text(_('Re_place'))
        label.set_use_underline(True)
        self.add_action_widget(b, 200)

        self.connect('response', self.on_dialog_response)
        self.show_all()

    def on_dialog_response(self, dialog, response):
        if self.on_response:
            if isinstance(self.on_response, tuple):
                self.on_response[0](response, *self.on_response[1:])
            else:
                self.on_response(response)
        self.call_cancel_on_destroy = False
        self.destroy()

class CommonInputDialog:
    """
    Common Class for Input dialogs
    """

    def __init__(self, title, label_str, is_modal, ok_handler, cancel_handler,
                 transient_for=None):
        self.dialog = self.xml.get_object('input_dialog')
        label = self.xml.get_object('label')
        self.dialog.set_title(title)
        label.set_markup(label_str)
        self.cancel_handler = cancel_handler
        self.vbox = self.xml.get_object('vbox')
        if transient_for:
            self.dialog.set_transient_for(transient_for)
        else:
            self.dialog.set_transient_for(app.interface.roster.window)

        self.ok_handler = ok_handler
        okbutton = self.xml.get_object('okbutton')
        okbutton.connect('clicked', self.on_okbutton_clicked)
        cancelbutton = self.xml.get_object('cancelbutton')
        cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
        self.xml.connect_signals(self)
        self.dialog.show_all()

    def on_input_dialog_destroy(self, widget):
        if self.cancel_handler:
            self.cancel_handler()

    def on_okbutton_clicked(self, widget):
        user_input = self.get_text()
        if user_input:
            user_input = user_input
        self.cancel_handler = None
        self.dialog.destroy()
        if isinstance(self.ok_handler, tuple):
            self.ok_handler[0](user_input, *self.ok_handler[1:])
        else:
            self.ok_handler(user_input)

    def on_cancelbutton_clicked(self, widget):
        self.dialog.destroy()

    def destroy(self):
        self.dialog.destroy()

class InputDialog(CommonInputDialog):
    """
    Class for Input dialog
    """

    def __init__(self, title, label_str, input_str=None, is_modal=True,
                 ok_handler=None, cancel_handler=None, transient_for=None):
        self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
        CommonInputDialog.__init__(self, title, label_str, is_modal,
                                   ok_handler, cancel_handler,
                                   transient_for=transient_for)
        self.input_entry = self.xml.get_object('input_entry')
        if input_str:
            self.set_entry(input_str)

    def on_input_dialog_delete_event(self, widget, event):
        '''
        may be implemented by subclasses
        '''
        pass

    def set_entry(self, value):
        self.input_entry.set_text(value)
        self.input_entry.select_region(0, -1) # select all

    def get_text(self):
        return self.input_entry.get_text()

class InputDialogCheck(InputDialog):
    """
    Class for Input dialog
    """

    def __init__(self, title, label_str, checktext='', input_str=None,
                 is_modal=True, ok_handler=None, cancel_handler=None,
                 transient_for=None):
        self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
        InputDialog.__init__(self, title, label_str, input_str=input_str,
                             is_modal=is_modal, ok_handler=ok_handler,
                             cancel_handler=cancel_handler,
                             transient_for=transient_for)
        self.input_entry = self.xml.get_object('input_entry')
        if input_str:
            self.input_entry.set_text(input_str)
            self.input_entry.select_region(0, -1) # select all

        if checktext:
            self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
            self.vbox.pack_start(self.checkbutton, False, True, 0)
            self.checkbutton.show()

    def on_okbutton_clicked(self, widget):
        user_input = self.get_text()
        if user_input:
            user_input = user_input
        self.cancel_handler = None
        self.dialog.destroy()
        if isinstance(self.ok_handler, tuple):
            self.ok_handler[0](user_input, self.is_checked(), *self.ok_handler[1:])
        else:
            self.ok_handler(user_input, self.is_checked())

    def get_text(self):
        return self.input_entry.get_text()

    def is_checked(self):
        """
        Get active state of the checkbutton
        """
        try:
            return self.checkbutton.get_active()
        except Exception:
            # There is no checkbutton
            return False

class ChangeNickDialog(InputDialogCheck):
    """
    Class for changing room nickname in case of conflict
    """

    def __init__(self, account, room_jid, title, prompt, check_text=None,
                 change_nick=False, transient_for=None):
        """
        change_nick must be set to True when we are already occupant of the room
        and we are changing our nick
        """
        InputDialogCheck.__init__(self, title, '', checktext=check_text,
                                  input_str='', is_modal=True, ok_handler=None,
                                  cancel_handler=None,
                                  transient_for=transient_for)
        self.room_queue = [(account, room_jid, prompt, change_nick)]
        self.check_next()

    def on_input_dialog_delete_event(self, widget, event):
        self.on_cancelbutton_clicked(widget)
        return True

    def setup_dialog(self):
        self.gc_control = app.interface.msg_win_mgr.get_gc_control(
                self.room_jid, self.account)
        if not self.gc_control and \
        self.room_jid in app.interface.minimized_controls[self.account]:
            self.gc_control = \
                app.interface.minimized_controls[self.account][self.room_jid]
        if not self.gc_control:
            self.check_next()
            return
        label = self.xml.get_object('label')
        label.set_markup(self.prompt)
        self.set_entry(self.gc_control.nick + \
                app.config.get('gc_proposed_nick_char'))

    def check_next(self):
        if len(self.room_queue) == 0:
            self.cancel_handler = None
            self.dialog.destroy()
            if 'change_nick_dialog' in app.interface.instances:
                del app.interface.instances['change_nick_dialog']
            return
        self.account, self.room_jid, self.prompt, self.change_nick = \
            self.room_queue.pop(0)
        self.setup_dialog()

        if app.new_room_nick is not None and not app.gc_connected[
        self.account][self.room_jid] and self.gc_control.nick != \
        app.new_room_nick:
            self.dialog.hide()
            self.on_ok(app.new_room_nick, True)
        else:
            self.dialog.show()

    def on_okbutton_clicked(self, widget):
        nick = self.get_text()
        if nick:
            nick = nick
        # send presence to room
        try:
            nick = helpers.parse_resource(nick)
        except Exception:
            # invalid char
            ErrorDialog(_('Invalid nickname'),
                    _('The nickname contains invalid characters.'))
            return
        self.on_ok(nick, self.is_checked())

    def on_ok(self, nick, is_checked):
        if is_checked:
            app.new_room_nick = nick
        app.connections[self.account].join_gc(nick, self.room_jid, None,
            change_nick=self.change_nick)
        if app.gc_connected[self.account][self.room_jid]:
            # We are changing nick, we will change self.nick when we receive
            # presence that inform that it works
            self.gc_control.new_nick = nick
        else:
            # We are connecting, we will not get a changed nick presence so
            # change it NOW. We don't already have a nick so it's harmless
            self.gc_control.nick = nick
        self.check_next()

    def on_cancelbutton_clicked(self, widget):
        self.gc_control.new_nick = ''
        self.check_next()

    def add_room(self, account, room_jid, prompt, change_nick=False):
        if (account, room_jid, prompt, change_nick) not in self.room_queue:
            self.room_queue.append((account, room_jid, prompt, change_nick))

class InputTextDialog(CommonInputDialog):
    """
    Class for multilines Input dialog (more place than InputDialog)
    """

    def __init__(self, title, label_str, input_str=None, is_modal=True,
                 ok_handler=None, cancel_handler=None, transient_for=None):
        self.xml = gtkgui_helpers.get_gtk_builder('input_text_dialog.ui')
        CommonInputDialog.__init__(self, title, label_str, is_modal,
                                   ok_handler, cancel_handler,
                                   transient_for=transient_for)
        self.input_buffer = self.xml.get_object('input_textview').get_buffer()
        if input_str:
            self.input_buffer.set_text(input_str)
            start_iter, end_iter = self.input_buffer.get_bounds()
            self.input_buffer.select_range(start_iter, end_iter) # select all

    def get_text(self):
        start_iter, end_iter = self.input_buffer.get_bounds()
        return self.input_buffer.get_text(start_iter, end_iter, True)

class DoubleInputDialog:
    """
    Class for Double Input dialog
    """

    def __init__(self, title, label_str1, label_str2, input_str1=None,
    input_str2=None, is_modal=True, ok_handler=None, cancel_handler=None,
    transient_for=None):
        self.xml = gtkgui_helpers.get_gtk_builder('dubbleinput_dialog.ui')
        self.dialog = self.xml.get_object('dubbleinput_dialog')
        label1 = self.xml.get_object('label1')
        self.input_entry1 = self.xml.get_object('input_entry1')
        label2 = self.xml.get_object('label2')
        self.input_entry2 = self.xml.get_object('input_entry2')
        self.dialog.set_title(title)
        label1.set_markup(label_str1)
        label2.set_markup(label_str2)
        self.cancel_handler = cancel_handler
        if input_str1:
            self.input_entry1.set_text(input_str1)
            self.input_entry1.select_region(0, -1) # select all
        if input_str2:
            self.input_entry2.set_text(input_str2)
            self.input_entry2.select_region(0, -1) # select all
        if transient_for:
            self.dialog.set_transient_for(transient_for)

        self.dialog.set_modal(is_modal)

        self.ok_handler = ok_handler
        okbutton = self.xml.get_object('okbutton')
        okbutton.connect('clicked', self.on_okbutton_clicked)
        cancelbutton = self.xml.get_object('cancelbutton')
        cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
        self.xml.connect_signals(self)
        self.dialog.show_all()

    def on_dubbleinput_dialog_destroy(self, widget):
        if not self.cancel_handler:
            return False
        if isinstance(self.cancel_handler, tuple):
            self.cancel_handler[0](*self.cancel_handler[1:])
        else:
            self.cancel_handler()

    def on_okbutton_clicked(self, widget):
        user_input1 = self.input_entry1.get_text()
        user_input2 = self.input_entry2.get_text()
        self.cancel_handler = None
        self.dialog.destroy()
        if not self.ok_handler:
            return
        if isinstance(self.ok_handler, tuple):
            self.ok_handler[0](user_input1, user_input2, *self.ok_handler[1:])
        else:
            self.ok_handler(user_input1, user_input2)

    def on_cancelbutton_clicked(self, widget):
        self.dialog.destroy()
        if not self.cancel_handler:
            return
        if isinstance(self.cancel_handler, tuple):
            self.cancel_handler[0](*self.cancel_handler[1:])
        else:
            self.cancel_handler()

class SubscriptionRequestWindow(Gtk.ApplicationWindow):
    def __init__(self, jid, text, account, user_nick=None):
        Gtk.ApplicationWindow.__init__(self)
        self.set_name('SubscriptionRequest')
        self.set_application(app.app)
        self.set_show_menubar(False)
        self.set_resizable(False)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_title(_('Subscription Request'))

        xml = gtkgui_helpers.get_gtk_builder('subscription_request_window.ui')
        self.add(xml.get_object('subscription_box'))
        self.jid = jid
        self.account = account
        self.user_nick = user_nick
        if len(app.connections) >= 2:
            prompt_text = \
                _('Subscription request for account %(account)s from %(jid)s')\
                % {'account': account, 'jid': self.jid}
        else:
            prompt_text = _('Subscription request from %s') % self.jid

        from_label = xml.get_object('from_label')
        from_label.set_text(prompt_text)

        textview = xml.get_object('message_textview')
        textview.get_buffer().set_text(text)

        self.set_default(xml.get_object('authorize_button'))
        xml.connect_signals(self)
        self.show_all()

    def on_subscription_request_window_destroy(self, widget):
        """
        Close window
        """
        if self.jid in app.interface.instances[self.account]['sub_request']:
            # remove us from open windows
            del app.interface.instances[self.account]['sub_request'][self.jid]

    def on_close_button_clicked(self, widget):
        self.destroy()

    def on_authorize_button_clicked(self, widget):
        """
        Accept the request
        """
        app.connections[self.account].send_authorization(self.jid)
        self.destroy()
        contact = app.contacts.get_contact(self.account, self.jid)
        if not contact or _('Not in Roster') in contact.groups:
            AddNewContactWindow(self.account, self.jid, self.user_nick)

    def on_contact_info_activate(self, widget):
        """
        Ask vcard
        """
        if self.jid in app.interface.instances[self.account]['infos']:
            app.interface.instances[self.account]['infos'][self.jid].window.present()
        else:
            contact = app.contacts.create_contact(jid=self.jid, account=self.account)
            app.interface.instances[self.account]['infos'][self.jid] = \
                     vcard.VcardWindow(contact, self.account)
            # Remove jabber page
            app.interface.instances[self.account]['infos'][self.jid].xml.\
                     get_object('information_notebook').remove_page(0)

    def on_start_chat_activate(self, widget):
        """
        Open chat
        """
        app.interface.new_chat_from_jid(self.account, self.jid)

    def on_deny_button_clicked(self, widget):
        """
        Refuse the request
        """
        app.connections[self.account].refuse_authorization(self.jid)
        contact = app.contacts.get_contact(self.account, self.jid)
        if contact and _('Not in Roster') in contact.get_shown_groups():
            app.interface.roster.remove_contact(self.jid, self.account)
        self.destroy()

class JoinGroupchatWindow(Gtk.ApplicationWindow):
    def __init__(self, account, room_jid, password=None, automatic=None,
    transient_for=None):
        Gtk.ApplicationWindow.__init__(self)
        self.set_name('JoinGroupchat')
        self.set_application(app.app)
        self.set_show_menubar(False)
        self.set_resizable(False)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_title(_('Join Groupchat'))
        if transient_for:
            self.set_transient_for(transient_for)

        self.automatic = automatic
        self.password = password
        self.requested_jid = None
        self.room_jid = room_jid
        self.account = account
        self.minimal_mode = 0 # 2: very mini, 1: with room, 0: show all
        if room_jid is not None:
            if '@' in room_jid:
                self.minimal_mode = 2
            else:
                self.minimal_mode = 1

        glade_objects = ['grid', 'nick_entry', 'account_combo', 'jid_label',
                         'bookmark_switch', 'autojoin_switch', 'headerbar',
                         'account_label', 'password_entry', 'password_label',
                         'join_button', 'button_box', 'server_label',
                         'server_combo', 'recent_label', 'recent_combo',
                         'room_label', 'room_entry', 'search_button']

        minimal_widgets = ['jid_label']
        room_widgets = ['room_label', 'room_entry']

        extended_widgets = ['server_label', 'server_combo', 'recent_label',
                            'recent_combo', 'room_label', 'room_entry',
                            'search_button']
        self.builder = gtkgui_helpers.get_gtk_builder(
            'join_groupchat_window.ui')
        for obj in glade_objects:
            setattr(self, obj, self.builder.get_object(obj))

        self.add(self.grid)

        if os.environ.get('GTK_CSD', '1') == '1':
            self.set_titlebar(self.headerbar)
        else:
            self.button_box.show()

        # Show widgets depending on the mode the window is in
        if self.minimal_mode > 0:
            for widget in minimal_widgets:
                getattr(self, widget).show()
            if self.minimal_mode == 1:
                for widget in room_widgets:
                    getattr(self, widget).show()
            self.jid_label.set_text(room_jid)
        else:
            for widget in extended_widgets:
                getattr(self, widget).show()
            self._fill_recent_and_servers(account)

        if account is None:
            connected_accounts = app.get_connected_accounts()
            for acc in connected_accounts:
                self.account_combo.append_text(acc)
        else:
            connected_accounts = [account]
            self.account_combo.append_text(account)

        self.builder.connect_signals(self)
        self.connect('key-press-event', self._on_key_press_event)
        self.connect('destroy', self._on_destroy)

        if self.minimal_mode == 0:
            app.ged.register_event_handler('agent-info-received', ged.GUI1,
                                           self._nec_agent_info_received)
            app.ged.register_event_handler('agent-info-error-received', ged.GUI1,
                                           self._nec_agent_info_error_received)

        # Show account combobox if there is more than one account
        if len(connected_accounts) > 1:
            self.account_combo.show()
            self.account_label.show()

        # Select first account
        self.account_combo.set_active(0)
        if self.minimal_mode == 0:
            self.recent_combo.set_active(0)

        if self.password is not None:
            self.password_entry.set_text(self.password)

        # Set bookmark switch sensitive if server supports bookmarks
        acc = self.account_combo.get_active_text()
        if not app.connections[acc].private_storage_supported:
            self.bookmark_switch.set_sensitive(False)
            self.autojoin_switch.set_sensitive(False)

        # Show password field if we are in extended mode or
        # The MUC is passwordprotected
        if self.minimal_mode == 0 or muc_caps_cache.supports(
        room_jid, 'muc_passwordprotected'):
            self.password_entry.show()
            self.password_label.show()

        self.show_all()

    def set_room(self, room_jid):
        room, server = app.get_name_and_server_from_jid(room_jid)
        self.room_entry.set_text(room)
        self.server_combo.get_child().set_text(server)

    def _fill_recent_and_servers(self, account):
        recent = app.get_recent_groupchats(account)
        servers = []
        for groupchat in recent:
            text = '%s on %s@%s' % (groupchat.nickname,
                                    groupchat.room,
                                    groupchat.server)
            self.recent_combo.append_text(text)
            servers.append(groupchat.server)

        for server in set(servers):
            self.server_combo.append_text(server)

        # Add own Server to ComboBox
        muc_domain = app.get_muc_domain(account)
        if muc_domain is not None:
            self.server_combo.insert_text(0, muc_domain)

    def _on_recent_changed(self, combo):
        text = combo.get_active_text()
        if text is None:
            self.server_combo.set_active(0)
            return
        nickname, _, room_jid = text.split()
        room, server = app.get_name_and_server_from_jid(room_jid)
        self.room_entry.set_text(room)
        self.nick_entry.set_text(nickname)
        self.server_combo.get_child().set_text(server)

    def _on_account_combo_changed(self, combo):
        account = combo.get_active_text()
        self.nick_entry.set_text(app.nicks[account])

    def _on_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.destroy()
        if event.keyval == Gdk.KEY_Return:
            self._on_join_clicked()
            return True

    def _on_join_clicked(self, *args):
        account = self.account_combo.get_active_text()
        nickname = self.nick_entry.get_text()

        if self.minimal_mode < 2:
            if self.minimal_mode == 0:
                server = self.server_combo.get_active_text()
            else: # mode 1
                server = self.room_jid
            room = self.room_entry.get_text()
            if room == '':
                ErrorDialog(_('Invalid Room'),
                            _('Please choose a room'), transient_for=self)
                return
            self.room_jid = '%s@%s' % (room, server)

        if app.in_groupchat(account, self.room_jid):
            # If we already in the groupchat, join_gc_room will bring
            # it to front
            app.interface.join_gc_room(account, self.room_jid, nickname, '')
            self.destroy()
            return

        if nickname == '':
            ErrorDialog(_('Invalid Nickname'),
                        _('Please choose a nickname'), transient_for=self)
            return

        try:
            helpers.parse_resource(nickname)
        except helpers.InvalidFormat as error:
            ErrorDialog(_('Invalid Nickname'), str(error), transient_for=self)
            return

        try:
            helpers.parse_jid(self.room_jid)
        except helpers.InvalidFormat as error:
            ErrorDialog(_('Invalid JID'), str(error), transient_for=self)
            return

        if not app.account_is_connected(account):
            ErrorDialog(
                _('You are not connected to the server'),
                _('You can not join a group chat unless you are connected.'),
                transient_for=self)
            return

        password = self.password_entry.get_text()
        self._add_bookmark(account, nickname, password)
        app.add_recent_groupchat(account, self.room_jid, nickname)

        if self.automatic:
            app.automatic_rooms[self.account][self.room_jid] = self.automatic

        app.interface.join_gc_room(account, self.room_jid, nickname, password)
        self.destroy()

    def _on_cancel_clicked(self, *args):
        self.destroy()

    def _on_bookmark_activate(self, switch, param):
        self.autojoin_switch.set_sensitive(switch.get_active())

    def _add_bookmark(self, account, nickname, password):
        if not app.connections[account].private_storage_supported:
            return

        add_bookmark = self.bookmark_switch.get_active()
        if not add_bookmark:
            return

        autojoin = int(self.autojoin_switch.get_active())

        # Add as bookmark, with autojoin and not minimized
        name = app.get_nick_from_jid(self.room_jid)
        app.interface.add_gc_bookmark(
            account, name, self.room_jid, autojoin, 1, password, nickname)

    def _on_destroy(self, *args):
        if self.minimal_mode == 0:
            del app.interface.instances[self.account]['join_gc']
            app.ged.remove_event_handler('agent-info-received', ged.GUI1,
                                         self._nec_agent_info_received)
            app.ged.remove_event_handler('agent-info-error-received', ged.GUI1,
                                         self._nec_agent_info_error_received)

    def _on_search_clicked(self, widget):
        server = self.server_combo.get_active_text().strip()
        self.requested_jid = server
        app.connections[self.account].discoverInfo(server)

    def _nec_agent_info_error_received(self, obj):
        if obj.conn.name != self.account:
            return
        if obj.jid != self.requested_jid:
            return
        self.requested_jid = None
        ErrorDialog(_('Wrong server'),
                    _('%s is not a groupchat server') % obj.jid,
                    transient_for=self)

    def _nec_agent_info_received(self, obj):
        if obj.conn.name != self.account:
            return
        if obj.jid != self.requested_jid:
            return
        self.requested_jid = None
        if nbxmpp.NS_MUC not in obj.features:
            ErrorDialog(_('Wrong server'),
                        _('%s is not a groupchat server') % obj.jid,
                        transient_for=self)
            return
        if obj.jid in app.interface.instances[self.account]['disco']:
            app.interface.instances[self.account]['disco'][obj.jid].window.\
                present()
        else:
            try:
                # Object will add itself to the window dict
                from gajim.disco import ServiceDiscoveryWindow
                ServiceDiscoveryWindow(
                    self.account, obj.jid,
                    initial_identities=[{'category': 'conference',
                                         'type': 'text'}])
            except GajimGeneralException:
                pass

class SynchroniseSelectAccountDialog:
    def __init__(self, account):
        # 'account' can be None if we are about to create our first one
        if not account or app.connections[account].connected < 2:
            ErrorDialog(_('You are not connected to the server'),
                _('Without a connection, you can not synchronise your contacts.'))
            raise GajimGeneralException('You are not connected to the server')
        self.account = account
        self.xml = gtkgui_helpers.get_gtk_builder('synchronise_select_account_dialog.ui')
        self.dialog = self.xml.get_object('synchronise_select_account_dialog')
        self.dialog.set_transient_for(app.interface.instances['accounts'])
        self.accounts_treeview = self.xml.get_object('accounts_treeview')
        model = Gtk.ListStore(str, str, bool)
        self.accounts_treeview.set_model(model)
        # columns
        renderer = Gtk.CellRendererText()
        self.accounts_treeview.insert_column_with_attributes(-1, _('Name'),
            renderer, text=0)
        renderer = Gtk.CellRendererText()
        self.accounts_treeview.insert_column_with_attributes(-1, _('Server'),
            renderer, text=1)

        self.xml.connect_signals(self)
        self.init_accounts()
        self.dialog.show_all()

    def on_accounts_window_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.window.destroy()

    def init_accounts(self):
        """
        Initialize listStore with existing accounts
        """
        model = self.accounts_treeview.get_model()
        model.clear()
        for remote_account in app.connections:
            if remote_account == self.account:
                # Do not show the account we're sync'ing
                continue
            iter_ = model.append()
            model.set(iter_, 0, remote_account, 1,
                app.get_hostname_from_account(remote_account))

    def on_cancel_button_clicked(self, widget):
        self.dialog.destroy()

    def on_ok_button_clicked(self, widget):
        sel = self.accounts_treeview.get_selection()
        (model, iter_) = sel.get_selected()
        if not iter_:
            return
        remote_account = model.get_value(iter_, 0)

        if app.connections[remote_account].connected < 2:
            ErrorDialog(_('This account is not connected to the server'),
                _('You cannot synchronize with an account unless it is connected.'))
            return
        else:
            try:
                SynchroniseSelectContactsDialog(self.account, remote_account)
            except GajimGeneralException:
                # if we showed ErrorDialog, there will not be dialog instance
                return
        self.dialog.destroy()

    @staticmethod
    def on_destroy(widget):
        del app.interface.instances['import_contacts']

class SynchroniseSelectContactsDialog:
    def __init__(self, account, remote_account):
        self.local_account = account
        self.remote_account = remote_account
        self.xml = gtkgui_helpers.get_gtk_builder(
            'synchronise_select_contacts_dialog.ui')
        self.dialog = self.xml.get_object('synchronise_select_contacts_dialog')
        self.contacts_treeview = self.xml.get_object('contacts_treeview')
        model = Gtk.ListStore(bool, str)
        self.contacts_treeview.set_model(model)
        # columns
        renderer1 = Gtk.CellRendererToggle()
        renderer1.set_property('activatable', True)
        renderer1.connect('toggled', self.toggled_callback)
        self.contacts_treeview.insert_column_with_attributes(-1,
            _('Synchronise'), renderer1, active=0)
        renderer2 = Gtk.CellRendererText()
        self.contacts_treeview.insert_column_with_attributes(-1, _('Name'),
            renderer2, text=1)

        self.xml.connect_signals(self)
        self.init_contacts()
        self.dialog.show_all()

    def toggled_callback(self, cell, path):
        model = self.contacts_treeview.get_model()
        iter_ = model.get_iter(path)
        model[iter_][0] = not cell.get_active()

    def on_contacts_window_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.window.destroy()

    def init_contacts(self):
        """
        Initialize listStore with existing accounts
        """
        model = self.contacts_treeview.get_model()
        model.clear()

        # recover local contacts
        local_jid_list = app.contacts.get_contacts_jid_list(self.local_account)

        remote_jid_list = app.contacts.get_contacts_jid_list(
                self.remote_account)
        for remote_jid in remote_jid_list:
            if remote_jid not in local_jid_list:
                iter_ = model.append()
                model.set(iter_, 0, True, 1, remote_jid)

    def on_cancel_button_clicked(self, widget):
        self.dialog.destroy()

    def on_ok_button_clicked(self, widget):
        model = self.contacts_treeview.get_model()
        iter_ = model.get_iter_first()
        while iter_:
            if model[iter_][0]:
                # it is selected
                remote_jid = model[iter_][1]
                message = 'I\'m synchronizing my contacts from my %s account, could you please add this address to your contact list?' % \
                    app.get_hostname_from_account(self.remote_account)
                remote_contact = app.contacts.get_first_contact_from_jid(
                        self.remote_account, remote_jid)
                # keep same groups and same nickname
                app.interface.roster.req_sub(self, remote_jid, message,
                    self.local_account, groups = remote_contact.groups,
                    nickname = remote_contact.name, auto_auth = True)
            iter_ = model.iter_next(iter_)
        self.dialog.destroy()

class StartChatDialog(Gtk.ApplicationWindow):
    def __init__(self):
        Gtk.ApplicationWindow.__init__(self)
        self.set_name('StartChatDialog')
        self.set_application(app.app)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_show_menubar(False)
        self.set_title(_('Start new Conversation'))
        self.set_default_size(-1, 400)
        self.ready_to_destroy = False

        self.builder = gtkgui_helpers.get_gtk_builder(
            'start_chat_dialog.ui')
        self.listbox = self.builder.get_object('listbox')
        self.search_entry = self.builder.get_object('search_entry')
        self.box = self.builder.get_object('box')

        self.add(self.box)

        self.new_contact_row_visible = False
        self.new_contact_rows = {}
        self.new_groupchat_rows = {}
        self.accounts = app.connections.keys()
        self.add_contacts()
        self.add_groupchats()

        self.search_entry.connect('search-changed',
                                  self._on_search_changed)
        self.search_entry.connect('next-match',
                                  self._select_new_match, 'next')
        self.search_entry.connect('previous-match',
                                  self._select_new_match, 'prev')
        self.search_entry.connect('stop-search',
                                  lambda *args: self.search_entry.set_text(''))

        self.listbox.set_filter_func(self._filter_func, None)
        self.listbox.set_sort_func(self._sort_func, None)
        self.listbox.connect('row-activated', self._on_row_activated)

        self.connect('key-press-event', self._on_key_press)
        self.connect('destroy', self._destroy)

        self.select_first_row()
        self.show_all()

    def add_contacts(self):
        show_account = len(self.accounts) > 1
        for account in self.accounts:
            self.new_contact_rows[account] = None
            for jid in app.contacts.get_jid_list(account):
                contact = app.contacts.get_contact_with_highest_priority(
                    account, jid)
                if contact.is_groupchat():
                    continue
                row = ContactRow(account, contact, jid,
                                 contact.get_shown_name(), show_account)
                self.listbox.add(row)

    def add_groupchats(self):
        show_account = len(self.accounts) > 1
        for account in self.accounts:
            self.new_groupchat_rows[account] = None
            bookmarks = app.connections[account].bookmarks
            groupchats = {}
            for bookmark in bookmarks:
                groupchats[bookmark['jid']] = bookmark['name']

            for jid in app.contacts.get_gc_list(account):
                if jid in groupchats:
                    continue
                groupchats[jid] = None

            for jid in groupchats:
                name = groupchats[jid]
                if not name:
                    name = app.get_nick_from_jid(jid)
                row = ContactRow(account, None, jid, name,
                                 show_account, True)
                self.listbox.add(row)

    def _on_row_activated(self, listbox, row):
        row = row.get_child()
        self._start_new_chat(row)

    def _on_key_press(self, widget, event):
        if event.keyval in (Gdk.KEY_Down, Gdk.KEY_Tab):
            self.search_entry.emit('next-match')
            return True
        elif (event.state == Gdk.ModifierType.SHIFT_MASK and
              event.keyval == Gdk.KEY_ISO_Left_Tab):
            self.search_entry.emit('previous-match')
            return True
        elif event.keyval == Gdk.KEY_Up:
            self.search_entry.emit('previous-match')
            return True
        elif event.keyval == Gdk.KEY_Escape:
            if self.search_entry.get_text() != '':
                self.search_entry.emit('stop-search')
            else:
                self.destroy()
            return True
        elif event.keyval == Gdk.KEY_Return:
            row = self.listbox.get_selected_row()
            if row is not None:
                row.emit('activate')
            return True
        else:
            self.search_entry.grab_focus_without_selecting()

    def _start_new_chat(self, row):
        if row.new:
            if not app.account_is_connected(row.account):
                app.interface.raise_dialog('start-chat-not-connected')
                return
            try:
                helpers.parse_jid(row.jid)
            except helpers.InvalidFormat as e:
                app.interface.raise_dialog('invalid-jid-with-error', str(e))
                return

        if row.groupchat:
            app.interface.join_gc_minimal(row.account, row.jid)
        else:
            app.interface.new_chat_from_jid(row.account, row.jid)

        self.ready_to_destroy = True

    def _on_search_changed(self, entry):
        search_text = entry.get_text()
        if '@' in search_text:
            self._add_new_jid_row()
            self._update_new_jid_rows(search_text)
        else:
            self._remove_new_jid_row()
        self.listbox.invalidate_filter()

    def _add_new_jid_row(self):
        if self.new_contact_row_visible:
            return
        for account in self.new_contact_rows:
            show_account = len(self.accounts) > 1
            row = ContactRow(account, None, '', None, show_account)
            self.new_contact_rows[account] = row
            group_row = ContactRow(account, None, '', None, show_account, True)
            self.new_groupchat_rows[account] = group_row
            self.listbox.add(row)
            self.listbox.add(group_row)
            row.get_parent().show_all()
        self.new_contact_row_visible = True

    def _remove_new_jid_row(self):
        if not self.new_contact_row_visible:
            return
        for account in self.new_contact_rows:
            self.listbox.remove(self.new_contact_rows[account].get_parent())
            self.listbox.remove(self.new_groupchat_rows[account].get_parent())
        self.new_contact_row_visible = False

    def _update_new_jid_rows(self, search_text):
        for account in self.new_contact_rows:
            self.new_contact_rows[account].update_jid(search_text)
            self.new_groupchat_rows[account].update_jid(search_text)

    def _select_new_match(self, entry, direction):
        selected_row = self.listbox.get_selected_row()
        index = selected_row.get_index()

        if direction == 'next':
            index += 1
        else:
            index -= 1

        while True:
            new_selected_row = self.listbox.get_row_at_index(index)
            if new_selected_row is None:
                return
            if new_selected_row.get_child_visible():
                self.listbox.select_row(new_selected_row)
                new_selected_row.grab_focus()
                return
            if direction == 'next':
                index += 1
            else:
                index -= 1

    def select_first_row(self):
        first_row = self.listbox.get_row_at_y(0)
        self.listbox.select_row(first_row)

    def _filter_func(self, row, user_data):
        search_text = self.search_entry.get_text().lower()
        search_text_list = search_text.split()
        row_text = row.get_child().get_search_text().lower()
        for text in search_text_list:
            if text not in row_text:
                GLib.timeout_add(50, self.select_first_row)
                return
        GLib.timeout_add(50, self.select_first_row)
        return True

    @staticmethod
    def _sort_func(row1, row2, user_data):
        name1 = row1.get_child().get_search_text()
        name2 = row2.get_child().get_search_text()
        account1 = row1.get_child().account
        account2 = row2.get_child().account
        is_groupchat1 = row1.get_child().groupchat
        is_groupchat2 = row2.get_child().groupchat
        new1 = row1.get_child().new
        new2 = row2.get_child().new

        result = locale.strcoll(account1.lower(), account2.lower())
        if result != 0:
            return result

        if new1 != new2:
            return 1 if new1 else -1

        if is_groupchat1 != is_groupchat2:
            return 1 if is_groupchat1 else -1

        return locale.strcoll(name1.lower(), name2.lower())

    @staticmethod
    def _destroy(*args):
        del app.interface.instances['start_chat']

class ContactRow(Gtk.Grid):
    def __init__(self, account, contact, jid, name, show_account,
                 groupchat=False):
        Gtk.Grid.__init__(self)
        self.set_column_spacing(12)
        self.set_size_request(260, -1)
        self.account = account
        self.account_label = app.config.get_per(
            'accounts', account, 'account_label') or account
        self.show_account = show_account
        self.jid = jid
        self.contact = contact
        self.name = name
        self.groupchat = groupchat
        self.new = jid == ''

        if self.groupchat:
            muc_icon = gtkgui_helpers.get_iconset_name_for(
                'muc-inactive' if self.new else 'muc-active')
            image = Gtk.Image.new_from_icon_name(muc_icon, Gtk.IconSize.DND)
        else:
            scale = self.get_scale_factor()
            avatar = app.contacts.get_avatar(
                account, jid, AvatarSize.ROSTER, scale)
            if avatar is None:
                image = Gtk.Image.new_from_icon_name(
                    'avatar-default', Gtk.IconSize.DND)
            else:
                image = Gtk.Image.new_from_surface(avatar)

        image.set_size_request(AvatarSize.ROSTER, AvatarSize.ROSTER)
        self.add(image)

        middle_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        middle_box.set_hexpand(True)

        if self.name is None:
            if self.groupchat:
                self.name = _('New Groupchat')
            else:
                self.name = _('New Contact')

        self.name_label = Gtk.Label(self.name)
        self.name_label.set_ellipsize(Pango.EllipsizeMode.END)
        self.name_label.set_xalign(0)
        self.name_label.set_width_chars(25)
        self.name_label.set_halign(Gtk.Align.START)
        self.name_label.get_style_context().add_class('bold16')

        status = contact.show if contact else 'offline'
        css_class = helpers.get_css_show_color(status)
        if css_class is not None:
            self.name_label.get_style_context().add_class(css_class)
        middle_box.add(self.name_label)

        self.jid_label = Gtk.Label(jid)
        self.jid_label.set_ellipsize(Pango.EllipsizeMode.END)
        self.jid_label.set_xalign(0)
        self.jid_label.set_width_chars(25)
        self.jid_label.set_halign(Gtk.Align.START)
        middle_box.add(self.jid_label)

        self.add(middle_box)

        if show_account:
            account_label = Gtk.Label(self.account_label)
            account_label.set_halign(Gtk.Align.START)
            account_label.set_valign(Gtk.Align.START)

            right_box = Gtk.Box()
            right_box.set_vexpand(True)
            right_box.add(account_label)
            self.add(right_box)

        self.show_all()

    def update_jid(self, jid):
        self.jid = jid
        self.jid_label.set_text(jid)

    def get_search_text(self):
        if self.contact is None and not self.groupchat:
            return self.jid
        if self.show_account:
            return '%s %s %s' % (self.name, self.jid, self.account_label)
        return '%s %s' % (self.name, self.jid)

class ChangePasswordDialog:
    def __init__(self, account, on_response, transient_for=None):
        # 'account' can be None if we are about to create our first one
        if not account or app.connections[account].connected < 2:
            ErrorDialog(_('You are not connected to the server'),
                _('Without a connection, you can not change your password.'))
            raise GajimGeneralException('You are not connected to the server')
        self.account = account
        self.on_response = on_response
        self.xml = gtkgui_helpers.get_gtk_builder('change_password_dialog.ui')
        self.dialog = self.xml.get_object('change_password_dialog')
        self.dialog.set_transient_for(transient_for)
        self.password1_entry = self.xml.get_object('password1_entry')
        self.password2_entry = self.xml.get_object('password2_entry')
        self.dialog.connect('response', self.on_dialog_response)

        self.dialog.show_all()

    def on_dialog_response(self, dialog, response):
        if response != Gtk.ResponseType.OK:
            dialog.destroy()
            self.on_response(None)
            return
        password1 = self.password1_entry.get_text()
        if not password1:
            ErrorDialog(_('Invalid password'), _('You must enter a password.'))
            return
        password2 = self.password2_entry.get_text()
        if password1 != password2:
            ErrorDialog(_('Passwords do not match'),
                _('The passwords typed in both fields must be identical.'))
            return
        dialog.destroy()
        self.on_response(password1)

class PopupNotificationWindow:
    def __init__(self, event_type, jid, account, msg_type='',
    path_to_image=None, title=None, text=None, timeout=-1):
        self.account = account
        self.jid = jid
        self.msg_type = msg_type
        self.index = len(app.interface.roster.popup_notification_windows)
        xml = gtkgui_helpers.get_gtk_builder('popup_notification_window.ui')
        self.window = xml.get_object('popup_notification_window')
        self.window.set_type_hint(Gdk.WindowTypeHint.TOOLTIP)
        self.window.set_name('NotificationPopup')
        close_button = xml.get_object('close_button')
        event_type_label = xml.get_object('event_type_label')
        event_description_label = xml.get_object('event_description_label')
        eventbox = xml.get_object('eventbox')
        image = xml.get_object('notification_image')

        if not text:
            text = app.get_name_from_jid(account, jid) # default value of text
        if not title:
            title = ''

        event_type_label.set_markup(
            '<span foreground="black" weight="bold">%s</span>' %
            GLib.markup_escape_text(title))

        css = '#NotificationPopup {background-color: black }'
        gtkgui_helpers.add_css_to_widget(self.window, css)

        # default image
        if not path_to_image:
            path_to_image = gtkgui_helpers.get_icon_path('gajim-chat_msg_recv', 48)

        if event_type == _('Contact Signed In'):
            bg_color = app.config.get('notif_signin_color')
        elif event_type == _('Contact Signed Out'):
            bg_color = app.config.get('notif_signout_color')
        elif event_type in (_('New Message'), _('New Single Message'),
            _('New Private Message'), _('New E-mail')):
            bg_color = app.config.get('notif_message_color')
        elif event_type == _('File Transfer Request'):
            bg_color = app.config.get('notif_ftrequest_color')
        elif event_type == _('File Transfer Error'):
            bg_color = app.config.get('notif_fterror_color')
        elif event_type in (_('File Transfer Completed'),
        _('File Transfer Stopped')):
            bg_color = app.config.get('notif_ftcomplete_color')
        elif event_type == _('Groupchat Invitation'):
            bg_color = app.config.get('notif_invite_color')
        elif event_type == _('Contact Changed Status'):
            bg_color = app.config.get('notif_status_color')
        else: # Unknown event! Shouldn't happen but deal with it
            bg_color = app.config.get('notif_other_color')

        background_class = '''
            .popup-style {
                border-image: none;
                background-image: none;
                background-color: %s }''' % bg_color

        gtkgui_helpers.add_css_to_widget(eventbox, background_class)
        eventbox.get_style_context().add_class('popup-style')

        gtkgui_helpers.add_css_to_widget(close_button, background_class)
        eventbox.get_style_context().add_class('popup-style')

        event_description_label.set_markup('<span foreground="black">%s</span>' %
            GLib.markup_escape_text(text))

        # set the image
        image.set_from_file(path_to_image)

        # position the window to bottom-right of screen
        window_width, self.window_height = self.window.get_size()
        app.interface.roster.popups_notification_height += self.window_height
        pos_x = app.config.get('notification_position_x')
        screen_w, screen_h = gtkgui_helpers.get_total_screen_geometry()
        if pos_x < 0:
            pos_x = screen_w - window_width + pos_x + 1
        pos_y = app.config.get('notification_position_y')
        if pos_y < 0:
            pos_y = screen_h - \
                app.interface.roster.popups_notification_height + pos_y + 1
        self.window.move(pos_x, pos_y)

        xml.connect_signals(self)
        self.window.show_all()
        if timeout > 0:
            GLib.timeout_add_seconds(timeout, self.on_timeout)

    def on_close_button_clicked(self, widget):
        self.adjust_height_and_move_popup_notification_windows()

    def on_timeout(self):
        self.adjust_height_and_move_popup_notification_windows()

    def adjust_height_and_move_popup_notification_windows(self):
        #remove
        app.interface.roster.popups_notification_height -= self.window_height
        self.window.destroy()

        if len(app.interface.roster.popup_notification_windows) > self.index:
            # we want to remove the destroyed window from the list
            app.interface.roster.popup_notification_windows.pop(self.index)

        # move the rest of popup windows
        app.interface.roster.popups_notification_height = 0
        current_index = 0
        for window_instance in app.interface.roster.popup_notification_windows:
            window_instance.index = current_index
            current_index += 1
            window_width, window_height = window_instance.window.get_size()
            app.interface.roster.popups_notification_height += window_height
            screen_w, screen_h = gtkgui_helpers.get_total_screen_geometry()
            window_instance.window.move(screen_w - window_width,
                screen_h - \
                app.interface.roster.popups_notification_height)

    def on_popup_notification_window_button_press_event(self, widget, event):
        if event.button != 1:
            self.window.destroy()
            return
        app.interface.handle_event(self.account, self.jid, self.msg_type)
        self.adjust_height_and_move_popup_notification_windows()

class SingleMessageWindow:
    """
    SingleMessageWindow can send or show a received singled message depending on
    action argument which can be 'send' or 'receive'
    """
    # Keep a reference on windows so garbage collector don't restroy them
    instances = []
    def __init__(self, account, to='', action='', from_whom='', subject='',
            message='', resource='', session=None, form_node=None):
        self.instances.append(self)
        self.account = account
        self.action = action

        self.subject = subject
        self.message = message
        self.to = to
        self.from_whom = from_whom
        self.resource = resource
        self.session = session

        self.xml = gtkgui_helpers.get_gtk_builder('single_message_window.ui')
        self.window = self.xml.get_object('single_message_window')
        self.count_chars_label = self.xml.get_object('count_chars_label')
        self.from_label = self.xml.get_object('from_label')
        self.from_entry = self.xml.get_object('from_entry')
        self.to_label = self.xml.get_object('to_label')
        self.to_entry = self.xml.get_object('to_entry')
        self.subject_entry = self.xml.get_object('subject_entry')
        self.message_scrolledwindow = self.xml.get_object(
                'message_scrolledwindow')
        self.message_textview = self.xml.get_object('message_textview')
        self.message_tv_buffer = self.message_textview.get_buffer()
        self.conversation_scrolledwindow = self.xml.get_object(
                'conversation_scrolledwindow')
        self.conversation_textview = conversation_textview.ConversationTextview(
            account, used_in_history_window=True)
        self.conversation_textview.tv.show()
        self.conversation_tv_buffer = self.conversation_textview.tv.get_buffer()
        self.xml.get_object('conversation_scrolledwindow').add(
                self.conversation_textview.tv)

        self.form_widget = None
        parent_box = self.xml.get_object('conversation_scrolledwindow').\
                           get_parent()
        if form_node:
            dataform = dataforms.ExtendForm(node=form_node)
            self.form_widget = dataforms_widget.DataFormWidget(dataform)
            self.form_widget.show_all()
            parent_box.add(self.form_widget)
            parent_box.child_set_property(self.form_widget, 'position',
                parent_box.child_get_property(self.xml.get_object(
                'conversation_scrolledwindow'), 'position'))
            self.action = 'form'

        self.send_button = self.xml.get_object('send_button')
        self.reply_button = self.xml.get_object('reply_button')
        self.send_and_close_button = self.xml.get_object('send_and_close_button')
        self.cancel_button = self.xml.get_object('cancel_button')
        self.close_button = self.xml.get_object('close_button')
        self.message_tv_buffer.connect('changed', self.update_char_counter)
        if isinstance(to, list):
            jid = ', '.join( [i[0].get_full_jid() for i in to])
            self.to_entry.set_text(jid)
            self.to_entry.set_sensitive(False)
        else:
            self.to_entry.set_text(to)

        if app.config.get('use_speller') and app.HAVE_SPELL and action == 'send':
            lang = app.config.get('speller_language')
            gspell_lang = Gspell.language_lookup(lang)
            if gspell_lang is None:
                AspellDictError(lang)
            else:
                spell_buffer = Gspell.TextBuffer.get_from_gtk_text_buffer(
                    self.message_textview.get_buffer())
                spell_buffer.set_spell_checker(Gspell.Checker.new(gspell_lang))
                spell_view = Gspell.TextView.get_from_gtk_text_view(
                    self.message_textview)
                spell_view.set_inline_spell_checking(True)
                spell_view.set_enable_language_menu(True)

        self.prepare_widgets_for(self.action)

        # set_text(None) raises TypeError exception
        if self.subject is None:
            self.subject = ''
        self.subject_entry.set_text(self.subject)


        if to == '':
            liststore = gtkgui_helpers.get_completion_liststore(self.to_entry)
            self.completion_dict = helpers.get_contact_dict_for_account(account)
            keys = sorted(self.completion_dict.keys())
            for jid in keys:
                contact = self.completion_dict[jid]
                status_icon = gtkgui_helpers.get_iconset_name_for(contact.show)
                liststore.append((status_icon, jid))
        else:
            self.completion_dict = {}
        self.xml.connect_signals(self)

        # get window position and size from config
        gtkgui_helpers.resize_window(self.window,
            app.config.get('single-msg-width'),
            app.config.get('single-msg-height'))
        gtkgui_helpers.move_window(self.window,
            app.config.get('single-msg-x-position'),
            app.config.get('single-msg-y-position'))

        self.window.show_all()

    def on_single_message_window_destroy(self, widget):
        self.instances.remove(self)
        c = app.contacts.get_contact_with_highest_priority(self.account,
                self.from_whom)
        if not c:
            # Groupchat is maybe already destroyed
            return
        if c.is_groupchat() and not self.from_whom in \
        app.interface.minimized_controls[self.account] and self.action == \
        'receive' and app.events.get_nb_roster_events(self.account,
        self.from_whom, types=['chat', 'normal']) == 0:
            app.interface.roster.remove_groupchat(self.from_whom, self.account)

    def set_cursor_to_end(self):
        end_iter = self.message_tv_buffer.get_end_iter()
        self.message_tv_buffer.place_cursor(end_iter)

    def save_pos(self):
        # save the window size and position
        x, y = self.window.get_position()
        app.config.set('single-msg-x-position', x)
        app.config.set('single-msg-y-position', y)
        width, height = self.window.get_size()
        app.config.set('single-msg-width', width)
        app.config.set('single-msg-height', height)

    def on_single_message_window_delete_event(self, window, ev):
        self.save_pos()

    def prepare_widgets_for(self, action):
        if len(app.connections) > 1:
            if action == 'send':
                title = _('Single Message using account %s') % self.account
            else:
                title = _('Single Message in account %s') % self.account
        else:
            title = _('Single Message')

        if action == 'send': # prepare UI for Sending
            title = _('Send %s') % title
            self.send_button.show()
            self.send_and_close_button.show()
            self.to_label.show()
            self.to_entry.show()
            self.reply_button.hide()
            self.from_label.hide()
            self.from_entry.hide()
            self.conversation_scrolledwindow.hide()
            self.message_scrolledwindow.show()

            if self.message: # we come from a reply?
                self.message_textview.grab_focus()
                self.cancel_button.hide()
                self.close_button.show()
                self.message_tv_buffer.set_text(self.message)
                GLib.idle_add(self.set_cursor_to_end)
            else: # we write a new message (not from reply)
                self.close_button.hide()
                if self.to: # do we already have jid?
                    self.subject_entry.grab_focus()

        elif action == 'receive': # prepare UI for Receiving
            title = _('Received %s') % title
            self.reply_button.show()
            self.from_label.show()
            self.from_entry.show()
            self.send_button.hide()
            self.send_and_close_button.hide()
            self.to_label.hide()
            self.to_entry.hide()
            self.conversation_scrolledwindow.show()
            self.message_scrolledwindow.hide()

            if self.message:
                self.conversation_textview.print_real_text(self.message)
            fjid = self.from_whom
            if self.resource:
                fjid += '/' + self.resource # Full jid of sender (with resource)
            self.from_entry.set_text(fjid)
            self.from_entry.set_property('editable', False)
            self.subject_entry.set_property('editable', False)
            self.reply_button.grab_focus()
            self.cancel_button.hide()
            self.close_button.show()
        elif action == 'form': # prepare UI for Receiving
            title = _('Form %s') % title
            self.send_button.show()
            self.send_and_close_button.show()
            self.to_label.show()
            self.to_entry.show()
            self.reply_button.hide()
            self.from_label.hide()
            self.from_entry.hide()
            self.conversation_scrolledwindow.hide()
            self.message_scrolledwindow.hide()

        self.window.set_title(title)

    def on_cancel_button_clicked(self, widget):
        self.save_pos()
        self.window.destroy()

    def on_close_button_clicked(self, widget):
        self.save_pos()
        self.window.destroy()

    def update_char_counter(self, widget):
        characters_no = self.message_tv_buffer.get_char_count()
        self.count_chars_label.set_text(str(characters_no))

    def send_single_message(self):
        if app.connections[self.account].connected <= 1:
            # if offline or connecting
            ErrorDialog(_('Connection not available'),
                _('Please make sure you are connected with "%s".') % self.account)
            return True
        if isinstance(self.to, list):
            sender_list = []
            for i in self.to:
                if i[0].resource:
                    sender_list.append(i[0].jid + '/' + i[0].resource)
                else:
                    sender_list.append(i[0].jid)
        else:
            sender_list = [j.strip() for j in self.to_entry.get_text().split(
                ',')]

        subject = self.subject_entry.get_text()
        begin, end = self.message_tv_buffer.get_bounds()
        message = self.message_tv_buffer.get_text(begin, end, True)

        if self.form_widget:
            form_node = self.form_widget.data_form
        else:
            form_node = None

        recipient_list = []

        for to_whom_jid in sender_list:
            if to_whom_jid in self.completion_dict:
                to_whom_jid = self.completion_dict[to_whom_jid].jid
            try:
                to_whom_jid = helpers.parse_jid(to_whom_jid)
            except helpers.InvalidFormat:
                ErrorDialog(_('Invalid JID'),
                    _('It is not possible to send a message to %s, this JID is not '
                    'valid.') % to_whom_jid)
                return True

            if '/announce/' in to_whom_jid:
                app.connections[self.account].send_motd(to_whom_jid, subject,
                    message)
                continue

            recipient_list.append(to_whom_jid)

        app.nec.push_outgoing_event(MessageOutgoingEvent(None,
            account=self.account, jid=recipient_list, message=message,
            type_='normal', subject=subject, form_node=form_node))

        self.subject_entry.set_text('') # we sent ok, clear the subject
        self.message_tv_buffer.set_text('') # we sent ok, clear the textview

    def on_send_button_clicked(self, widget):
        self.send_single_message()

    def on_reply_button_clicked(self, widget):
        # we create a new blank window to send and we preset RE: and to jid
        self.subject = _('RE: %s') % self.subject
        self.message = _('%s wrote:\n') % self.from_whom + self.message
        # add > at the begining of each line
        self.message = self.message.replace('\n', '\n> ') + '\n\n'
        self.window.destroy()
        SingleMessageWindow(self.account, to=self.from_whom, action='send',
            from_whom=self.from_whom, subject=self.subject, message=self.message,
            session=self.session)

    def on_send_and_close_button_clicked(self, widget):
        if self.send_single_message():
            return
        self.save_pos()
        self.window.destroy()

    def on_single_message_window_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape: # ESCAPE
            self.save_pos()
            self.window.destroy()


class XMLConsoleWindow(Gtk.Window):
    def __init__(self, account):
        Gtk.Window.__init__(self)
        self.account = account
        self.enabled = True
        self.presence = True
        self.message = True
        self.iq = True
        self.stream = True
        self.incoming = True
        self.outgoing = True
        self.filter_dialog = None

        glade_objects = ['textview', 'input', 'scrolled_input', 'headerbar',
                         'scrolled', 'actionbar', 'paned', 'box', 'menubutton']
        self.builder = gtkgui_helpers.get_gtk_builder('xml_console_window.ui')
        for obj in glade_objects:
            setattr(self, obj, self.builder.get_object(obj))

        self.set_titlebar(self.headerbar)
        jid = app.get_jid_from_account(account)
        self.headerbar.set_subtitle(jid)
        self.set_default_size(600, 600)
        self.add(self.box)

        self.paned.set_position(self.paned.get_property('max-position'))

        button = gtkgui_helpers.get_image_button(
            'edit-clear-all-symbolic', _('Clear'))
        button.connect('clicked', self.on_clear)
        self.actionbar.pack_start(button)

        button = gtkgui_helpers.get_image_button(
            'applications-system-symbolic', _('Filter'))
        button.connect('clicked', self.on_filter_options)
        self.actionbar.pack_start(button)

        button = gtkgui_helpers.get_image_button(
            'document-edit-symbolic', _('XML Input'), toggle=True)
        button.connect('toggled', self.on_input)
        self.actionbar.pack_start(button)

        button = gtkgui_helpers.get_image_button('emblem-ok-symbolic', _('Send'))
        button.connect('clicked', self.on_send)
        self.actionbar.pack_end(button)

        self.actionbar.pack_start(self.menubutton)

        self.create_tags()
        self.show_all()

        self.scrolled_input.hide()
        self.menubutton.hide()

        self.connect("destroy", self.on_destroy)
        self.connect('key_press_event', self.on_key_press_event)
        self.builder.connect_signals(self)

        app.ged.register_event_handler('stanza-received', ged.GUI1,
            self._nec_stanza_received)
        app.ged.register_event_handler('stanza-sent', ged.GUI1,
            self._nec_stanza_sent)

    def create_tags(self):
        buffer_ = self.textview.get_buffer()
        in_color = app.config.get('inmsgcolor')
        out_color = app.config.get('outmsgcolor')

        tags = ['presence', 'message', 'stream', 'iq']

        tag = buffer_.create_tag('incoming')
        tag.set_property('foreground', in_color)
        tag = buffer_.create_tag('outgoing')
        tag.set_property('foreground', out_color)

        for tag_name in tags:
            buffer_.create_tag(tag_name)

    def on_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.destroy()

    def on_row_activated(self, listbox, row):
        text = row.get_child().get_text()
        input_text = None
        if text == 'Presence':
            input_text = (
                '<presence>\n'
                '<show></show>\n'
                '<status></status>\n'
                '<priority></priority>\n'
                '</presence>')
        elif text == 'Message':
            input_text = (
                '<message to="" type="">\n'
                '<body></body>\n'
                '</message>')
        elif text == 'Iq':
            input_text = (
                '<iq to="" type="">\n'
                '<query xmlns=""></query>\n'
                '</iq>')

        if input_text is not None:
            buffer_ = self.input.get_buffer()
            buffer_.set_text(input_text)
            self.input.grab_focus()

    def on_send(self, *args):
        if app.connections[self.account].connected <= 1:
            # if offline or connecting
            ErrorDialog(_('Connection not available'),
                _('Please make sure you are connected with "%s".') % \
                self.account)
            return
        buffer_ = self.input.get_buffer()
        begin_iter, end_iter = buffer_.get_bounds()
        stanza = buffer_.get_text(begin_iter, end_iter, True)
        if stanza:
            app.connections[self.account].send_stanza(stanza)
            buffer_.set_text('')

    def on_input(self, button, *args):
        if button.get_active():
            self.paned.get_child2().show()
            self.menubutton.show()
            self.input.grab_focus()
        else:
            self.paned.get_child2().hide()
            self.menubutton.hide()

    def on_filter_options(self, *args):
        if self.filter_dialog:
            self.filter_dialog.present()
            return
        options = [
            Option(OptionKind.SWITCH, 'Presence',
                    OptionType.VALUE, self.presence,
                    callback=self.on_option, data='presence'),

            Option(OptionKind.SWITCH, 'Message',
                    OptionType.VALUE, self.message,
                    callback=self.on_option, data='message'),

            Option(OptionKind.SWITCH, 'Iq', OptionType.VALUE, self.iq,
                    callback=self.on_option, data='iq'),

            Option(OptionKind.SWITCH, 'Stream\nManagement',
                    OptionType.VALUE, self.stream,
                    callback=self.on_option, data='stream'),

            Option(OptionKind.SWITCH, 'In', OptionType.VALUE, self.incoming,
                    callback=self.on_option, data='incoming'),

            Option(OptionKind.SWITCH, 'Out', OptionType.VALUE, self.outgoing,
                    callback=self.on_option, data='outgoing'),
            ]

        self.filter_dialog = OptionsDialog(self, 'Filter',
            Gtk.DialogFlags.DESTROY_WITH_PARENT,
            options, self.account)
        self.filter_dialog.connect('destroy', self.on_filter_destroyed)

    def on_filter_destroyed(self, win):
        self.filter_dialog = None

    def on_clear(self, *args):
        buffer_ = self.textview.get_buffer().set_text('')

    def on_destroy(self, *args):
        del app.interface.instances[self.account]['xml_console']
        app.ged.remove_event_handler('stanza-received', ged.GUI1,
            self._nec_stanza_received)
        app.ged.remove_event_handler('stanza-sent', ged.GUI1,
            self._nec_stanza_sent)

    def on_enable(self, switch, param):
        self.enabled = switch.get_active()

    def on_option(self, value, data):
        setattr(self, data, value)
        value = not value
        table = self.textview.get_buffer().get_tag_table()
        tag = table.lookup(data)
        if data in ('incoming', 'outgoing'):
            if value:
                tag.set_priority(table.get_size() - 1)
            else:
                tag.set_priority(0)
        tag.set_property('invisible', value)

    def _nec_stanza_received(self, obj):
        if obj.conn.name != self.account:
            return
        self.print_stanza(obj.stanza_str, 'incoming')

    def _nec_stanza_sent(self, obj):
        if obj.conn.name != self.account:
            return
        self.print_stanza(obj.stanza_str, 'outgoing')

    def print_stanza(self, stanza, kind):
        # kind must be 'incoming' or 'outgoing'
        if not self.enabled:
            return
        if not stanza:
            return

        at_the_end = gtkgui_helpers.at_the_end(self.scrolled)

        buffer_ = self.textview.get_buffer()
        end_iter = buffer_.get_end_iter()

        type_ = kind
        if stanza.startswith('<presence'):
            type_ = 'presence'
        elif stanza.startswith('<message'):
            type_ = 'message'
        elif stanza.startswith('<iq'):
            type_ = 'iq'
        elif stanza.startswith('<r') or stanza.startswith('<a'):
            type_ = 'stream'

        stanza = '<!-- {kind} {time} -->\n{stanza}\n\n'.format(
            kind=kind.capitalize(),
            time=time.strftime('%c'),
            stanza=stanza.replace('><', '>\n<'))
        buffer_.insert_with_tags_by_name(end_iter, stanza, type_, kind)

        if at_the_end:
            GLib.idle_add(gtkgui_helpers.scroll_to_end, self.scrolled)

#Action that can be done with an incoming list of contacts
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
    'remove': _('remove')}
class RosterItemExchangeWindow:
    """
    Windows used when someone send you a exchange contact suggestion
    """

    def __init__(self, account, action, exchange_list, jid_from,
                    message_body=None):
        self.account = account
        self.action = action
        self.exchange_list = exchange_list
        self.message_body = message_body
        self.jid_from = jid_from

        show_dialog = False

        # Connect to gtk builder
        self.xml = gtkgui_helpers.get_gtk_builder(
            'roster_item_exchange_window.ui')
        self.window = self.xml.get_object('roster_item_exchange_window')

        # Add Widgets.
        for widget_to_add in ['accept_button_label', 'type_label',
        'body_scrolledwindow', 'body_textview', 'items_list_treeview']:
            self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)

        # Set labels
        # self.action can be 'add', 'modify' or 'remove'
        self.type_label.set_label(
            _('<b>%(jid)s</b> would like you to <b>%(action)s</b> some contacts '
            'in your roster.') % {'jid': jid_from,
            'action': TRANSLATED_ACTION[self.action]})
        if message_body:
            buffer_ = self.body_textview.get_buffer()
            buffer_.set_text(self.message_body)
        else:
            self.body_scrolledwindow.hide()
        # Treeview
        model = Gtk.ListStore(bool, str, str, str, str)
        self.items_list_treeview.set_model(model)
        # columns
        renderer1 = Gtk.CellRendererToggle()
        renderer1.set_property('activatable', True)
        renderer1.connect('toggled', self.toggled_callback)
        if self.action == 'add':
            title = _('Add')
        elif self.action == 'modify':
            title = _('Modify')
        elif self.action == 'delete':
            title = _('Delete')
        self.items_list_treeview.insert_column_with_attributes(-1, title,
            renderer1, active=0)
        renderer2 = Gtk.CellRendererText()
        self.items_list_treeview.insert_column_with_attributes(-1, _('JID'),
            renderer2, text=1)
        renderer3 = Gtk.CellRendererText()
        self.items_list_treeview.insert_column_with_attributes(-1, _('Name'),
            renderer3, text=2)
        renderer4 = Gtk.CellRendererText()
        self.items_list_treeview.insert_column_with_attributes(-1, _('Groups'),
            renderer4, text=3)

        # Init contacts
        model = self.items_list_treeview.get_model()
        model.clear()

        if action == 'add':
            for jid in self.exchange_list:
                groups = ''
                is_in_roster = True
                contact = app.contacts.get_contact_with_highest_priority(
                    self.account, jid)
                if not contact or _('Not in Roster') in contact.groups:
                    is_in_roster = False
                name = self.exchange_list[jid][0]
                num_list = len(self.exchange_list[jid][1])
                current = 0
                for group in self.exchange_list[jid][1]:
                    current += 1
                    if contact and not group in contact.groups:
                        is_in_roster = False
                    if current == num_list:
                        groups = groups + group
                    else:
                        groups = groups + group + ', '
                if not is_in_roster:
                    show_dialog = True
                    iter_ = model.append()
                    model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)

            # Change label for accept_button to action name instead of 'OK'.
            self.accept_button_label.set_label(_('Add'))
        elif action == 'modify':
            for jid in self.exchange_list:
                groups = ''
                is_in_roster = True
                is_right = True
                contact = app.contacts.get_contact_with_highest_priority(
                    self.account, jid)
                name = self.exchange_list[jid][0]
                if not contact:
                    is_in_roster = False
                    is_right = False
                else:
                    if name != contact.name:
                        is_right = False
                num_list = len(self.exchange_list[jid][1])
                current = 0
                for group in self.exchange_list[jid][1]:
                    current += 1
                    if contact and not group in contact.groups:
                        is_right = False
                    if current == num_list:
                        groups = groups + group
                    else:
                        groups = groups + group + ', '
                if not is_right and is_in_roster:
                    show_dialog = True
                    iter_ = model.append()
                    model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)

            # Change label for accept_button to action name instead of 'OK'.
            self.accept_button_label.set_label(_('Modify'))
        elif action == 'delete':
            for jid in self.exchange_list:
                groups = ''
                is_in_roster = True
                contact = app.contacts.get_contact_with_highest_priority(
                        self.account, jid)
                name = self.exchange_list[jid][0]
                if not contact:
                    is_in_roster = False
                num_list = len(self.exchange_list[jid][1])
                current = 0
                for group in self.exchange_list[jid][1]:
                    current += 1
                    if current == num_list:
                        groups = groups + group
                    else:
                        groups = groups + group + ', '
                if is_in_roster:
                    show_dialog = True
                    iter_ = model.append()
                    model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)

            # Change label for accept_button to action name instead of 'OK'.
            self.accept_button_label.set_label(_('Delete'))

        if show_dialog:
            self.window.show_all()
            self.xml.connect_signals(self)

    def toggled_callback(self, cell, path):
        model = self.items_list_treeview.get_model()
        iter_ = model.get_iter(path)
        model[iter_][0] = not cell.get_active()

    def on_accept_button_clicked(self, widget):
        model = self.items_list_treeview.get_model()
        iter_ = model.get_iter_first()
        if self.action == 'add':
            a = 0
            while iter_:
                if model[iter_][0]:
                    a+=1
                    # it is selected
                    #remote_jid = model[iter_][1]
                    message = _('%s suggested me to add you in my roster.'
                            % self.jid_from)
                    # keep same groups and same nickname
                    groups = model[iter_][3].split(', ')
                    if groups == ['']:
                        groups = []
                    jid = model[iter_][1]
                    if app.jid_is_transport(self.jid_from):
                        app.connections[self.account].automatically_added.append(
                                jid)
                    app.interface.roster.req_sub(self, jid, message,
                            self.account, groups=groups, nickname=model[iter_][2],
                            auto_auth=True)
                iter_ = model.iter_next(iter_)
            InformationDialog(i18n.ngettext('Added %d contact',
                'Added %d contacts', a, a, a))
        elif self.action == 'modify':
            a = 0
            while iter_:
                if model[iter_][0]:
                    a+=1
                    # it is selected
                    jid = model[iter_][1]
                    # keep same groups and same nickname
                    groups = model[iter_][3].split(', ')
                    if groups == ['']:
                        groups = []
                    for u in app.contacts.get_contact(self.account, jid):
                        u.name = model[iter_][2]
                    app.connections[self.account].update_contact(jid,
                            model[iter_][2], groups)
                    self.draw_contact(jid, self.account)
                    # Update opened chat
                    ctrl = app.interface.msg_win_mgr.get_control(jid, self.account)
                    if ctrl:
                        ctrl.update_ui()
                        win = app.interface.msg_win_mgr.get_window(jid,
                                self.account)
                        win.redraw_tab(ctrl)
                        win.show_title()
                iter_ = model.iter_next(iter_)
        elif self.action == 'delete':
            a = 0
            while iter_:
                if model[iter_][0]:
                    a+=1
                    # it is selected
                    jid = model[iter_][1]
                    app.connections[self.account].unsubscribe(jid)
                    app.interface.roster.remove_contact(jid, self.account)
                    app.contacts.remove_jid(self.account, jid)
                iter_ = model.iter_next(iter_)
            InformationDialog(i18n.ngettext('Removed %d contact',
                'Removed %d contacts', a, a, a))
        self.window.destroy()

    def on_cancel_button_clicked(self, widget):
        self.window.destroy()


class Archiving313PreferencesWindow:

    default_dict = {'always': 0, 'roster': 1, 'never': 2}
    default_dict_cb = {0: 'always', 1: 'roster', 2: 'never'}

    def __init__(self, account):
        self.account = account
        self.idle_id = None

        # Connect to glade
        self.xml = gtkgui_helpers.get_gtk_builder(
            'archiving_313_preferences_window.ui')
        self.window = self.xml.get_object('archiving_313_pref')

        # Add Widgets
        for widget in ('archive_items_liststore', 'default_cb'):
            setattr(self, widget, self.xml.get_object(widget))

        self.window.set_title(_('Archiving Preferences for %s') % self.account)

        app.ged.register_event_handler(
            'archiving-313-preferences-changed-received', ged.GUI1,
            self._nec_archiving_313_changed_received)
        app.ged.register_event_handler(
            'archiving-error-received', ged.GUI1, self._nec_archiving_error)

        self.default_cb.set_active(0)
        self.set_widget_state(False)
        self.window.show_all()
        self.xml.connect_signals(self)

        self.idle_id = GLib.timeout_add_seconds(3, self._nec_archiving_error)
        app.connections[self.account].request_archive_preferences()

    def on_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.window.destroy()

    def set_widget_state(self, state):
        for widget in ('default_cb', 'save_button', 'add_button',
            'remove_button'):
            self.xml.get_object(widget).set_sensitive(state)

    def _nec_archiving_313_changed_received(self, obj):
        if obj.conn.name != self.account:
            return
        try:
            GLib.source_remove(self.idle_id)
        except Exception as e:
            log.debug(e)
        self.set_widget_state(True)
        if obj.answer:
            def on_ok(dialog):
                self.window.destroy()
            dialog = HigDialog(
                self.window, Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
                _('Success!'), _('Your Archiving Preferences have been saved!'),
                on_response_ok=on_ok, on_response_cancel=on_ok)
            dialog.popup()
        self.default_cb.set_active(self.default_dict[obj.default])
        self.archive_items_liststore.clear()
        for items in obj.items:
            self.archive_items_liststore.append(items)

    def _nec_archiving_error(self, obj=None):
        if obj and obj.conn.name != self.account:
            return
        try:
            GLib.source_remove(self.idle_id)
        except Exception as e:
            log.debug(e)
        if not obj:
            msg = _('We got no response from the Server')
        else:
            msg = _('We received an error: {}').format(self.error_msg)

        dialog = HigDialog(
            self.window, Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
            _('Error!'), msg)
        dialog.popup()
        self.set_widget_state(True)
        return

    def on_add_item_button_clicked(self, widget):
        key_name = 'item_archiving_preferences'
        if key_name in app.interface.instances[self.account]:
            app.interface.instances[self.account][key_name].window.present()
        else:
            app.interface.instances[self.account][key_name] = \
                ItemArchiving313PreferencesWindow(
                    self.account, self, self.window)

    def on_remove_item_button_clicked(self, widget):
        archive_view = self.xml.get_object('archive_view')
        mod, path = archive_view.get_selection().get_selected_rows()
        if path:
            iter_ = mod.get_iter(path)
            self.archive_items_liststore.remove(iter_)

    def on_save_button_clicked(self, widget):
        self.set_widget_state(False)
        items = []
        default = self.default_dict_cb[self.default_cb.get_active()]
        for item in self.archive_items_liststore:
            items.append((item[0].lower(), item[1].lower()))
        self.idle_id = GLib.timeout_add_seconds(3, self._nec_archiving_error)
        app.connections[self.account]. \
            set_archive_preferences(items, default)

    def on_close_button_clicked(self, widget):
        self.window.destroy()

    def on_archiving_preferences_window_destroy(self, widget):
        app.ged.remove_event_handler(
            'archiving-313-preferences-changed-received', ged.GUI1,
            self._nec_archiving_313_changed_received)
        app.ged.remove_event_handler(
            'archiving-error-received', ged.GUI1, self._nec_archiving_error)
        if 'archiving_preferences' in app.interface.instances[self.account]:
            del app.interface.instances[self.account]['archiving_preferences']


class ItemArchiving313PreferencesWindow:

    def __init__(self, account, archive, transient):

        self.account = account
        self.archive = archive

        self.xml = gtkgui_helpers.get_gtk_builder(
            'archiving_313_preferences_item.ui')
        self.window = self.xml.get_object('item_dialog')
        self.window.set_transient_for(transient)
        # Add Widgets
        for widget in ('jid_entry', 'pref_cb'):
            setattr(self, widget, self.xml.get_object(widget))

        self.window.set_title(_('Add JID'))
        self.pref_cb.set_active(0)
        self.window.show_all()
        self.xml.connect_signals(self)

    def on_ok_button_clicked(self, widget):
        if self.pref_cb.get_active() == 0:
            pref = 'Always'
        else:
            pref = 'Never'
        text = self.jid_entry.get_text()
        if not text:
            self.window.destroy()
            return
        else:
            self.archive.archive_items_liststore.append((text, pref))
        self.window.destroy()

    def on_cancel_button_clicked(self, widget):
        self.window.destroy()

    def on_item_archiving_preferences_window_destroy(self, widget):
        key_name = 'item_archiving_preferences'
        if key_name in app.interface.instances[self.account]:
            del app.interface.instances[self.account][key_name]


class PrivacyListWindow:
    """
    Window that is used for creating NEW or EDITING already there privacy lists
    """

    def __init__(self, account, privacy_list_name, action):
        '''action is 'EDIT' or 'NEW' depending on if we create a new priv list
        or edit an already existing one'''
        self.account = account
        self.privacy_list_name = privacy_list_name

        # Dicts and Default Values
        self.active_rule = ''
        self.global_rules = {}
        self.list_of_groups = {}

        self.max_order = 0

        # Default Edit Values
        self.edit_rule_type = 'jid'
        self.allow_deny = 'allow'

        # Connect to gtk builder
        self.xml = gtkgui_helpers.get_gtk_builder('privacy_list_window.ui')
        self.window = self.xml.get_object('privacy_list_edit_window')

        # Add Widgets

        for widget_to_add in ('title_hbox', 'privacy_lists_title_label',
        'list_of_rules_label', 'add_edit_rule_label', 'delete_open_buttons_hbox',
        'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton',
        'list_of_rules_combobox', 'delete_open_buttons_hbox',
        'delete_rule_button', 'open_rule_button', 'edit_allow_radiobutton',
        'edit_deny_radiobutton', 'edit_type_jabberid_radiobutton',
        'edit_type_jabberid_entry', 'edit_type_group_radiobutton',
        'edit_type_group_combobox', 'edit_type_subscription_radiobutton',
        'edit_type_subscription_combobox', 'edit_type_select_all_radiobutton',
        'edit_queries_send_checkbutton', 'edit_send_messages_checkbutton',
        'edit_view_status_checkbutton', 'edit_all_checkbutton',
        'edit_order_spinbutton', 'new_rule_button', 'save_rule_button',
        'privacy_list_refresh_button', 'privacy_list_close_button',
        'edit_send_status_checkbutton', 'add_edit_vbox',
        'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton'):
            self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)

        self.privacy_lists_title_label.set_label(
                _('Privacy List <b><i>%s</i></b>') % \
                GLib.markup_escape_text(self.privacy_list_name))

        if len(app.connections) > 1:
            title = _('Privacy List for %s') % self.account
        else:
            title = _('Privacy List')

        self.delete_rule_button.set_sensitive(False)
        self.open_rule_button.set_sensitive(False)
        self.privacy_list_active_checkbutton.set_sensitive(False)
        self.privacy_list_default_checkbutton.set_sensitive(False)
        self.list_of_rules_combobox.set_sensitive(False)

        # set jabber id completion
        jids_list_store = Gtk.ListStore(GObject.TYPE_STRING)
        for jid in app.contacts.get_jid_list(self.account):
            jids_list_store.append([jid])
        jid_entry_completion = Gtk.EntryCompletion()
        jid_entry_completion.set_text_column(0)
        jid_entry_completion.set_model(jids_list_store)
        jid_entry_completion.set_popup_completion(True)
        self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
        if action == 'EDIT':
            self.refresh_rules()

        model = self.edit_type_group_combobox.get_model()
        count = 0
        for group in app.groups[self.account]:
            self.list_of_groups[group] = count
            count += 1
            model.append([group])
        self.edit_type_group_combobox.set_active(0)

        self.window.set_title(title)

        app.ged.register_event_handler('privacy-list-received', ged.GUI1,
            self._nec_privacy_list_received)
        app.ged.register_event_handler('privacy-list-active-default',
            ged.GUI1, self._nec_privacy_list_active_default)

        self.window.show_all()
        self.add_edit_vbox.hide()

        self.xml.connect_signals(self)

    def on_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.window.destroy()

    def on_privacy_list_edit_window_destroy(self, widget):
        key_name = 'privacy_list_%s' % self.privacy_list_name
        if key_name in app.interface.instances[self.account]:
            del app.interface.instances[self.account][key_name]
        app.ged.remove_event_handler('privacy-list-received', ged.GUI1,
            self._nec_privacy_list_received)
        app.ged.remove_event_handler('privacy-list-active-default',
            ged.GUI1, self._nec_privacy_list_active_default)

    def _nec_privacy_list_active_default(self, obj):
        if obj.conn.name != self.account:
            return
        if obj.active_list == self.privacy_list_name:
            self.privacy_list_active_checkbutton.set_active(True)
        else:
            self.privacy_list_active_checkbutton.set_active(False)
        if obj.default_list == self.privacy_list_name:
            self.privacy_list_default_checkbutton.set_active(True)
        else:
            self.privacy_list_default_checkbutton.set_active(False)

    def privacy_list_received(self, rules):
        model = self.list_of_rules_combobox.get_model()
        model.clear()
        self.global_rules = {}
        for rule in rules:
            if 'type' in rule:
                text_item = _('Order: %(order)s, action: %(action)s, type: %(type)s'
                        ', value: %(value)s') % {'order': rule['order'],
                        'action': rule['action'], 'type': rule['type'],
                        'value': rule['value']}
            else:
                text_item = _('Order: %(order)s, action: %(action)s') % \
                        {'order': rule['order'], 'action': rule['action']}
            if int(rule['order']) > self.max_order:
                self.max_order = int(rule['order'])
            self.global_rules[text_item] = rule
            model.append([text_item])
        if len(rules) == 0:
            self.title_hbox.set_sensitive(False)
            self.list_of_rules_combobox.set_sensitive(False)
            self.delete_rule_button.set_sensitive(False)
            self.open_rule_button.set_sensitive(False)
            self.privacy_list_active_checkbutton.set_sensitive(False)
            self.privacy_list_default_checkbutton.set_sensitive(False)
        else:
            self.list_of_rules_combobox.set_active(0)
            self.title_hbox.set_sensitive(True)
            self.list_of_rules_combobox.set_sensitive(True)
            self.delete_rule_button.set_sensitive(True)
            self.open_rule_button.set_sensitive(True)
            self.privacy_list_active_checkbutton.set_sensitive(True)
            self.privacy_list_default_checkbutton.set_sensitive(True)
        self.reset_fields()
        app.connections[self.account].get_active_default_lists()

    def _nec_privacy_list_received(self, obj):
        if obj.conn.name != self.account:
            return
        if obj.list_name != self.privacy_list_name:
            return
        self.privacy_list_received(obj.rules)

    def refresh_rules(self):
        app.connections[self.account].get_privacy_list(self.privacy_list_name)

    def on_delete_rule_button_clicked(self, widget):
        model = self.list_of_rules_combobox.get_model()
        iter_ = self.list_of_rules_combobox.get_active_iter()
        _rule = model[iter_][0]
        tags = []
        for rule in self.global_rules:
            if rule != _rule:
                tags.append(self.global_rules[rule])
        app.connections[self.account].set_privacy_list(
                self.privacy_list_name, tags)
        self.privacy_list_received(tags)
        self.add_edit_vbox.hide()
        if not tags: # we removed latest rule
            if 'privacy_lists' in app.interface.instances[self.account]:
                win = app.interface.instances[self.account]['privacy_lists']
                win.remove_privacy_list_from_combobox(self.privacy_list_name)
                win.draw_widgets()

    def on_open_rule_button_clicked(self, widget):
        self.add_edit_rule_label.set_label(
                _('<b>Edit a rule</b>'))
        active_num = self.list_of_rules_combobox.get_active()
        if active_num == -1:
            self.active_rule = ''
        else:
            model = self.list_of_rules_combobox.get_model()
            iter_ = self.list_of_rules_combobox.get_active_iter()
            self.active_rule = model[iter_][0]
        if self.active_rule != '':
            rule_info = self.global_rules[self.active_rule]
            self.edit_order_spinbutton.set_value(int(rule_info['order']))
            if 'type' in rule_info:
                if rule_info['type'] == 'jid':
                    self.edit_type_jabberid_radiobutton.set_active(True)
                    self.edit_type_jabberid_entry.set_text(rule_info['value'])
                elif rule_info['type'] == 'group':
                    self.edit_type_group_radiobutton.set_active(True)
                    if rule_info['value'] in self.list_of_groups:
                        self.edit_type_group_combobox.set_active(
                                self.list_of_groups[rule_info['value']])
                    else:
                        self.edit_type_group_combobox.set_active(0)
                elif rule_info['type'] == 'subscription':
                    self.edit_type_subscription_radiobutton.set_active(True)
                    sub_value = rule_info['value']
                    if sub_value == 'none':
                        self.edit_type_subscription_combobox.set_active(0)
                    elif sub_value == 'both':
                        self.edit_type_subscription_combobox.set_active(1)
                    elif sub_value == 'from':
                        self.edit_type_subscription_combobox.set_active(2)
                    elif sub_value == 'to':
                        self.edit_type_subscription_combobox.set_active(3)
                else:
                    self.edit_type_select_all_radiobutton.set_active(True)
            else:
                self.edit_type_select_all_radiobutton.set_active(True)
            self.edit_send_messages_checkbutton.set_active(False)
            self.edit_queries_send_checkbutton.set_active(False)
            self.edit_view_status_checkbutton.set_active(False)
            self.edit_send_status_checkbutton.set_active(False)
            self.edit_all_checkbutton.set_active(False)
            if not rule_info['child']:
                self.edit_all_checkbutton.set_active(True)
            else:
                if 'presence-out' in rule_info['child']:
                    self.edit_send_status_checkbutton.set_active(True)
                if 'presence-in' in rule_info['child']:
                    self.edit_view_status_checkbutton.set_active(True)
                if 'iq' in rule_info['child']:
                    self.edit_queries_send_checkbutton.set_active(True)
                if 'message' in rule_info['child']:
                    self.edit_send_messages_checkbutton.set_active(True)

            if rule_info['action'] == 'allow':
                self.edit_allow_radiobutton.set_active(True)
            else:
                self.edit_deny_radiobutton.set_active(True)
        self.add_edit_vbox.show()

    def on_edit_all_checkbutton_toggled(self, widget):
        if widget.get_active():
            self.edit_send_messages_checkbutton.set_active(True)
            self.edit_queries_send_checkbutton.set_active(True)
            self.edit_view_status_checkbutton.set_active(True)
            self.edit_send_status_checkbutton.set_active(True)
            self.edit_send_messages_checkbutton.set_sensitive(False)
            self.edit_queries_send_checkbutton.set_sensitive(False)
            self.edit_view_status_checkbutton.set_sensitive(False)
            self.edit_send_status_checkbutton.set_sensitive(False)
        else:
            self.edit_send_messages_checkbutton.set_active(False)
            self.edit_queries_send_checkbutton.set_active(False)
            self.edit_view_status_checkbutton.set_active(False)
            self.edit_send_status_checkbutton.set_active(False)
            self.edit_send_messages_checkbutton.set_sensitive(True)
            self.edit_queries_send_checkbutton.set_sensitive(True)
            self.edit_view_status_checkbutton.set_sensitive(True)
            self.edit_send_status_checkbutton.set_sensitive(True)

    def on_privacy_list_active_checkbutton_toggled(self, widget):
        if widget.get_active():
            app.connections[self.account].set_active_list(
                    self.privacy_list_name)
        else:
            app.connections[self.account].set_active_list(None)

    def on_privacy_list_default_checkbutton_toggled(self, widget):
        if widget.get_active():
            app.connections[self.account].set_default_list(
                    self.privacy_list_name)
        else:
            app.connections[self.account].set_default_list(None)

    def on_new_rule_button_clicked(self, widget):
        self.reset_fields()
        self.add_edit_vbox.show()

    def reset_fields(self):
        self.edit_type_jabberid_entry.set_text('')
        self.edit_allow_radiobutton.set_active(True)
        self.edit_type_jabberid_radiobutton.set_active(True)
        self.active_rule = ''
        self.edit_send_messages_checkbutton.set_active(False)
        self.edit_queries_send_checkbutton.set_active(False)
        self.edit_view_status_checkbutton.set_active(False)
        self.edit_send_status_checkbutton.set_active(False)
        self.edit_all_checkbutton.set_active(False)
        self.edit_order_spinbutton.set_value(self.max_order + 1)
        self.edit_type_group_combobox.set_active(0)
        self.edit_type_subscription_combobox.set_active(0)
        self.add_edit_rule_label.set_label(
                _('<b>Add a rule</b>'))

    def get_current_tags(self):
        if self.edit_type_jabberid_radiobutton.get_active():
            edit_type = 'jid'
            edit_value = self.edit_type_jabberid_entry.get_text()
        elif self.edit_type_group_radiobutton.get_active():
            edit_type = 'group'
            model = self.edit_type_group_combobox.get_model()
            iter_ = self.edit_type_group_combobox.get_active_iter()
            edit_value = model[iter_][0]
        elif self.edit_type_subscription_radiobutton.get_active():
            edit_type = 'subscription'
            subs = ['none', 'both', 'from', 'to']
            edit_value = subs[self.edit_type_subscription_combobox.get_active()]
        elif self.edit_type_select_all_radiobutton.get_active():
            edit_type = ''
            edit_value = ''
        edit_order = str(self.edit_order_spinbutton.get_value_as_int())
        if self.edit_allow_radiobutton.get_active():
            edit_deny = 'allow'
        else:
            edit_deny = 'deny'
        child = []
        if not self.edit_all_checkbutton.get_active():
            if self.edit_send_messages_checkbutton.get_active():
                child.append('message')
            if self.edit_queries_send_checkbutton.get_active():
                child.append('iq')
            if self.edit_send_status_checkbutton.get_active():
                child.append('presence-out')
            if self.edit_view_status_checkbutton.get_active():
                child.append('presence-in')
        if edit_type != '':
            return {'order': edit_order, 'action': edit_deny,
                'type': edit_type, 'value': edit_value, 'child': child}
        return {'order': edit_order, 'action': edit_deny, 'child': child}

    def on_save_rule_button_clicked(self, widget):
        tags=[]
        current_tags = self.get_current_tags()
        if int(current_tags['order']) > self.max_order:
            self.max_order = int(current_tags['order'])
        if self.active_rule == '':
            tags.append(current_tags)

        for rule in self.global_rules:
            if rule != self.active_rule:
                tags.append(self.global_rules[rule])
            else:
                tags.append(current_tags)

        app.connections[self.account].set_privacy_list(
                self.privacy_list_name, tags)
        self.refresh_rules()
        self.add_edit_vbox.hide()
        if 'privacy_lists' in app.interface.instances[self.account]:
            win = app.interface.instances[self.account]['privacy_lists']
            win.add_privacy_list_to_combobox(self.privacy_list_name)
            win.draw_widgets()

    def on_list_of_rules_combobox_changed(self, widget):
        self.add_edit_vbox.hide()

    def on_edit_type_radiobutton_changed(self, widget, radiobutton):
        active_bool = widget.get_active()
        if active_bool:
            self.edit_rule_type = radiobutton

    def on_edit_allow_radiobutton_changed(self, widget, radiobutton):
        active_bool = widget.get_active()
        if active_bool:
            self.allow_deny = radiobutton

    def on_close_button_clicked(self, widget):
        self.window.destroy()

class PrivacyListsWindow:
    """
    Window that is the main window for Privacy Lists; we can list there the
    privacy lists and ask to create a new one or edit an already there one
    """
    def __init__(self, account):
        self.account = account
        self.privacy_lists_save = []

        self.xml = gtkgui_helpers.get_gtk_builder('privacy_lists_window.ui')

        self.window = self.xml.get_object('privacy_lists_first_window')
        for widget_to_add in ('list_of_privacy_lists_combobox',
            'delete_privacy_list_button', 'open_privacy_list_button',
            'new_privacy_list_button', 'new_privacy_list_entry',
            'privacy_lists_refresh_button', 'close_privacy_lists_window_button'):
            self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)

        self.draw_privacy_lists_in_combobox([])
        self.privacy_lists_refresh()

        self.enabled = True

        if len(app.connections) > 1:
            title = _('Privacy Lists for %s') % self.account
        else:
            title = _('Privacy Lists')

        self.window.set_title(title)

        app.ged.register_event_handler('privacy-lists-received', ged.GUI1,
            self._nec_privacy_lists_received)
        app.ged.register_event_handler('privacy-lists-removed', ged.GUI1,
            self._nec_privacy_lists_removed)

        self.window.show_all()

        self.xml.connect_signals(self)

    def on_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.window.destroy()

    def on_privacy_lists_first_window_destroy(self, widget):
        if 'privacy_lists' in app.interface.instances[self.account]:
            del app.interface.instances[self.account]['privacy_lists']
        app.ged.remove_event_handler('privacy-lists-received', ged.GUI1,
            self._nec_privacy_lists_received)
        app.ged.remove_event_handler('privacy-lists-removed', ged.GUI1,
            self._nec_privacy_lists_removed)

    def remove_privacy_list_from_combobox(self, privacy_list):
        if privacy_list not in self.privacy_lists_save:
            return
        privacy_list_index = self.privacy_lists_save.index(privacy_list)
        self.list_of_privacy_lists_combobox.remove_text(privacy_list_index)
        self.privacy_lists_save.remove(privacy_list)

    def add_privacy_list_to_combobox(self, privacy_list):
        if privacy_list in self.privacy_lists_save:
            return
        model = self.list_of_privacy_lists_combobox.get_model()
        model.append([privacy_list])
        self.privacy_lists_save.append(privacy_list)

    def draw_privacy_lists_in_combobox(self, privacy_lists):
        self.list_of_privacy_lists_combobox.set_active(-1)
        self.list_of_privacy_lists_combobox.get_model().clear()
        self.privacy_lists_save = []
        for add_item in privacy_lists:
            self.add_privacy_list_to_combobox(add_item)
        self.draw_widgets()

    def draw_widgets(self):
        if len(self.privacy_lists_save) == 0:
            self.list_of_privacy_lists_combobox.set_sensitive(False)
            self.open_privacy_list_button.set_sensitive(False)
            self.delete_privacy_list_button.set_sensitive(False)
        else:
            self.list_of_privacy_lists_combobox.set_sensitive(True)
            self.list_of_privacy_lists_combobox.set_active(0)
            self.open_privacy_list_button.set_sensitive(True)
            self.delete_privacy_list_button.set_sensitive(True)

    def on_close_button_clicked(self, widget):
        self.window.destroy()

    def on_delete_privacy_list_button_clicked(self, widget):
        active_list = self.privacy_lists_save[
            self.list_of_privacy_lists_combobox.get_active()]
        app.connections[self.account].del_privacy_list(active_list)

    def privacy_list_removed(self, active_list):
        self.privacy_lists_save.remove(active_list)
        self.privacy_lists_received({'lists': self.privacy_lists_save})

    def _nec_privacy_lists_removed(self, obj):
        if obj.conn.name != self.account:
            return
        self.privacy_list_removed(obj.lists_list)

    def privacy_lists_received(self, lists):
        if not lists:
            return
        privacy_lists = []
        for privacy_list in lists['lists']:
            privacy_lists.append(privacy_list)
        self.draw_privacy_lists_in_combobox(privacy_lists)

    def _nec_privacy_lists_received(self, obj):
        if obj.conn.name != self.account:
            return
        self.privacy_lists_received(obj.lists_list)

    def privacy_lists_refresh(self):
        app.connections[self.account].get_privacy_lists()

    def on_new_privacy_list_button_clicked(self, widget):
        name = self.new_privacy_list_entry.get_text()
        if not name:
            ErrorDialog(_('Invalid List Name'),
                _('You must enter a name to create a privacy list.'),
                transient_for=self.window)
            return
        key_name = 'privacy_list_%s' % name
        if key_name in app.interface.instances[self.account]:
            app.interface.instances[self.account][key_name].window.present()
        else:
            app.interface.instances[self.account][key_name] = \
                PrivacyListWindow(self.account, name, 'NEW')
        self.new_privacy_list_entry.set_text('')

    def on_privacy_lists_refresh_button_clicked(self, widget):
        self.privacy_lists_refresh()

    def on_open_privacy_list_button_clicked(self, widget):
        name = self.privacy_lists_save[
                self.list_of_privacy_lists_combobox.get_active()]
        key_name = 'privacy_list_%s' % name
        if key_name in app.interface.instances[self.account]:
            app.interface.instances[self.account][key_name].window.present()
        else:
            app.interface.instances[self.account][key_name] = \
                     PrivacyListWindow(self.account, name, 'EDIT')

class InvitationReceivedDialog:
    def __init__(self, account, room_jid, contact_fjid, password=None,
    comment=None, is_continued=False):

        self.room_jid = room_jid
        self.account = account
        self.password = password
        self.is_continued = is_continued
        self.contact_fjid = contact_fjid

        jid = app.get_jid_without_resource(contact_fjid)

        pritext = _('''You are invited to a groupchat''')
        #Don't translate $Contact
        if is_continued:
            sectext = _('$Contact has invited you to join a discussion')
        else:
            sectext = _('$Contact has invited you to group chat %(room_jid)s')\
                % {'room_jid': room_jid}
        contact = app.contacts.get_first_contact_from_jid(account, jid)
        contact_text = contact and contact.name or jid
        sectext = i18n.direction_mark + sectext.replace('$Contact',
            contact_text)

        if comment: # only if not None and not ''
            comment = GLib.markup_escape_text(comment)
            comment = _('Comment: %s') % comment
            sectext += '\n\n%s' % comment
        sectext += '\n\n' + _('Do you want to accept the invitation?')

        def on_yes(checked, text):
            if self.is_continued:
                app.interface.join_gc_room(self.account, self.room_jid,
                    app.nicks[self.account], self.password,
                    is_continued=True)
            else:
                app.interface.join_gc_minimal(
                    self.account, self.room_jid, password=self.password)

        def on_no(text):
            app.connections[account].decline_invitation(self.room_jid,
                self.contact_fjid, text)

        dlg = YesNoDialog(pritext, sectext,
            text_label=_('Reason (if you decline):'), on_response_yes=on_yes,
            on_response_no=on_no)
        dlg.set_title(_('Groupchat Invitation'))

class ProgressDialog:
    def __init__(self, title_text, during_text, messages_queue):
        """
        During text is what to show during the procedure, messages_queue has the
        message to show in the textview
        """
        self.xml = gtkgui_helpers.get_gtk_builder('progress_dialog.ui')
        self.dialog = self.xml.get_object('progress_dialog')
        self.label = self.xml.get_object('label')
        self.label.set_markup('<big>' + during_text + '</big>')
        self.progressbar = self.xml.get_object('progressbar')
        self.dialog.set_title(title_text)
        self.dialog.set_default_size(450, 250)
        self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
        self.dialog.show_all()
        self.xml.connect_signals(self)

        self.update_progressbar_timeout_id = GLib.timeout_add(100,
            self.update_progressbar)

    def update_progressbar(self):
        if self.dialog:
            self.progressbar.pulse()
            return True # loop forever
        return False

    def on_progress_dialog_delete_event(self, widget, event):
        return True # WM's X button or Escape key should not destroy the window


class ClientCertChooserDialog(FileChooserDialog):
    def __init__(self, path_to_clientcert_file='', on_response_ok=None,
    on_response_cancel=None):
        '''
        optionally accepts path_to_clientcert_file so it has that as selected
        '''
        def on_ok(widget, callback):
            '''
            check if file exists and call callback
            '''
            path_to_clientcert_file = self.get_filename()
            if os.path.exists(path_to_clientcert_file):
                callback(widget, path_to_clientcert_file)

        FileChooserDialog.__init__(self,
            title_text=_('Choose Client Cert #PCKS12'),
            transient_for=app.interface.instances['accounts'],
            action=Gtk.FileChooserAction.OPEN,
            buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
            Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
            current_folder='',
            default_response=Gtk.ResponseType.OK,
            on_response_ok=(on_ok, on_response_ok),
            on_response_cancel=on_response_cancel)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('All files'))
        filter_.add_pattern('*')
        self.add_filter(filter_)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('PKCS12 Files'))
        filter_.add_pattern('*.p12')
        self.add_filter(filter_)
        self.set_filter(filter_)

        if path_to_clientcert_file:
            # set_filename accept only absolute path
            path_to_clientcert_file = os.path.abspath(path_to_clientcert_file)
            self.set_filename(path_to_clientcert_file)


class SoundChooserDialog(FileChooserDialog):
    def __init__(self, path_to_snd_file='', on_response_ok=None,
                    on_response_cancel=None, transient_for=None):
        """
        Optionally accepts path_to_snd_file so it has that as selected
        """
        def on_ok(widget, callback):
            """
            Check if file exists and call callback
            """
            path_to_snd_file = self.get_filename()
            if os.path.exists(path_to_snd_file):
                callback(widget, path_to_snd_file)

        FileChooserDialog.__init__(
            self, title_text=_('Choose Sound'),
            action=Gtk.FileChooserAction.OPEN,
            buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                     Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
            default_response=Gtk.ResponseType.OK,
            current_folder=app.config.get('last_sounds_dir'),
            on_response_ok=(on_ok, on_response_ok),
            on_response_cancel=on_response_cancel,
            transient_for=transient_for)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('All files'))
        filter_.add_pattern('*')
        self.add_filter(filter_)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('Wav Sounds'))
        filter_.add_pattern('*.wav')
        self.add_filter(filter_)
        self.set_filter(filter_)

        path_to_snd_file = helpers.check_soundfile_path(path_to_snd_file)
        if path_to_snd_file:
            # set_filename accept only absolute path
            path_to_snd_file = os.path.abspath(path_to_snd_file)
            self.set_filename(path_to_snd_file)

class ImageChooserDialog(FileChooserDialog):
    def __init__(self, path_to_file='', on_response_ok=None,
                    on_response_cancel=None):
        """
        Optionally accepts path_to_snd_file so it has that as selected
        """
        def on_ok(widget, callback):
            '''check if file exists and call callback'''
            path_to_file = self.get_filename()
            if not path_to_file:
                return
            if os.path.exists(path_to_file):
                if isinstance(callback, tuple):
                    callback[0](widget, path_to_file, *callback[1:])
                else:
                    callback(widget, path_to_file)

        path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES)
        FileChooserDialog.__init__(self,
           title_text = _('Choose Image'),
           action = Gtk.FileChooserAction.OPEN,
           buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                  Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
           default_response = Gtk.ResponseType.OK,
           current_folder = path,
           on_response_ok = (on_ok, on_response_ok),
           on_response_cancel = on_response_cancel)

        if on_response_cancel:
            self.connect('destroy', on_response_cancel)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('All files'))
        filter_.add_pattern('*')
        self.add_filter(filter_)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('Images'))
        filter_.add_mime_type('image/png')
        filter_.add_mime_type('image/jpeg')
        filter_.add_mime_type('image/gif')
        filter_.add_mime_type('image/tiff')
        filter_.add_mime_type('image/svg+xml')
        filter_.add_mime_type('image/x-xpixmap') # xpm
        self.add_filter(filter_)
        self.set_filter(filter_)

        if path_to_file:
            self.set_filename(path_to_file)

        self.set_use_preview_label(False)
        self.set_preview_widget(Gtk.Image())
        self.connect('selection-changed', self.update_preview)

    def update_preview(self, widget):
        path_to_file = widget.get_preview_filename()
        if path_to_file is None or os.path.isdir(path_to_file):
            # nothing to preview or directory
            # make sure you clean image do show nothing
            preview = widget.get_preview_widget()
            preview.clear()
            return
        try:
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path_to_file, 100, 100)
        except GObject.GError:
            return
        widget.get_preview_widget().set_from_pixbuf(pixbuf)

class AvatarChooserDialog(ImageChooserDialog):
    def __init__(self, path_to_file='', on_response_ok=None,
            on_response_cancel=None, on_response_clear=None):
        ImageChooserDialog.__init__(self, path_to_file, on_response_ok,
            on_response_cancel)
        button = Gtk.Button(None, Gtk.STOCK_CLEAR)
        self.response_clear = on_response_clear
        if on_response_clear:
            button.connect('clicked', self.on_clear)
        button.show_all()
        action_area = self.get_action_area()
        action_area.pack_start(button, True, True, 0)
        action_area.reorder_child(button, 0)

    def on_clear(self, widget):
        if isinstance(self.response_clear, tuple):
            self.response_clear[0](widget, *self.response_clear[1:])
        else:
            self.response_clear(widget)


class ArchiveChooserDialog(FileChooserDialog):
    def __init__(self, on_response_ok=None, on_response_cancel=None,
                 transient_for=None):

        def on_ok(widget, callback):
            '''check if file exists and call callback'''
            path_to_file = self.get_filename()
            if not path_to_file:
                return
            if os.path.exists(path_to_file):
                if isinstance(callback, tuple):
                    callback[0](path_to_file, *callback[1:])
                else:
                    callback(path_to_file)
            self.destroy()

        path = os.path.expanduser('~')

        FileChooserDialog.__init__(self,
            title_text=_('Choose Archive'),
            action=Gtk.FileChooserAction.OPEN,
            buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
            default_response=Gtk.ResponseType.OK,
            current_folder=path,
            on_response_ok=(on_ok, on_response_ok),
            on_response_cancel=on_response_cancel,
            transient_for=transient_for)

        if on_response_cancel:
            self.connect('destroy', on_response_cancel)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('All files'))
        filter_.add_pattern('*')
        self.add_filter(filter_)

        filter_ = Gtk.FileFilter()
        filter_.set_name(_('Zip files'))
        filter_.add_pattern('*.zip')

        self.add_filter(filter_)
        self.set_filter(filter_)


class AddSpecialNotificationDialog:
    def __init__(self, jid):
        """
        jid is the jid for which we want to add special notification (sound and
        notification popups)
        """
        self.xml = gtkgui_helpers.get_gtk_builder(
            'add_special_notification_window.ui')
        self.window = self.xml.get_object('add_special_notification_window')
        self.condition_combobox = self.xml.get_object('condition_combobox')
        self.condition_combobox.set_active(0)
        self.notification_popup_yes_no_combobox = self.xml.get_object(
                'notification_popup_yes_no_combobox')
        self.notification_popup_yes_no_combobox.set_active(0)
        self.listen_sound_combobox = self.xml.get_object('listen_sound_combobox')
        self.listen_sound_combobox.set_active(0)

        self.jid = jid
        self.xml.get_object('when_foo_becomes_label').set_text(
                _('When %s becomes:') % self.jid)

        self.window.set_title(_('Adding Special Notification for %s') % jid)
        self.window.show_all()
        self.xml.connect_signals(self)

    def on_cancel_button_clicked(self, widget):
        self.window.destroy()

    def on_add_special_notification_window_delete_event(self, widget, event):
        self.window.destroy()

    def on_listen_sound_combobox_changed(self, widget):
        active = widget.get_active()
        if active == 1: # user selected 'choose sound'
            def on_ok(widget, path_to_snd_file):
                pass

            def on_cancel(widget):
                widget.set_active(0) # go back to No Sound

            self.dialog = SoundChooserDialog(on_response_ok=on_ok,
                on_response_cancel=on_cancel)

    def on_ok_button_clicked(self, widget):
        conditions = ('online', 'chat', 'online_and_chat',
            'away', 'xa', 'away_and_xa', 'dnd', 'xa_and_dnd', 'offline')
        active = self.condition_combobox.get_active()

        active_iter = self.listen_sound_combobox.get_active_iter()
        listen_sound_model = self.listen_sound_combobox.get_model()

class TransformChatToMUC:
    # Keep a reference on windows so garbage collector don't restroy them
    instances = []
    def __init__(self, account, jids, preselected=None):
        """
        This window is used to trasform a one-to-one chat to a MUC. We do 2
        things: first select the server and then make a guests list
        """

        self.instances.append(self)
        self.account = account
        self.auto_jids = jids
        self.preselected_jids = preselected

        self.xml = gtkgui_helpers.get_gtk_builder('chat_to_muc_window.ui')
        self.window = self.xml.get_object('chat_to_muc_window')

        for widget_to_add in ('invite_button', 'cancel_button',
            'server_list_comboboxentry', 'guests_treeview', 'guests_store',
            'server_and_guests_hseparator', 'server_select_label'):
            self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)

        server_list = []
        self.servers = Gtk.ListStore(str)
        self.server_list_comboboxentry.set_model(self.servers)
        cell = Gtk.CellRendererText()
        self.server_list_comboboxentry.pack_start(cell, True)
        self.server_list_comboboxentry.add_attribute(cell, 'text', 0)

        # get the muc server of our server
        if 'jabber' in app.connections[account].muc_jid:
            server_list.append(app.connections[account].muc_jid['jabber'])
        # add servers or recently joined groupchats
        recently_groupchat = app.config.get_per('accounts', account, 'recent_groupchats').split()
        for g in recently_groupchat:
            server = app.get_server_from_jid(g)
            if server not in server_list and not server.startswith('irc'):
                server_list.append(server)
        # add a default server
        if not server_list:
            server_list.append('conference.jabber.org')

        for s in server_list:
            self.servers.append([s])

        self.server_list_comboboxentry.set_active(0)

        # set treeview
        # name, jid

        self.guests_store.set_sort_column_id(1, Gtk.SortType.ASCENDING)
        self.guests_treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)

        # All contacts beside the following can be invited:
        #       transports, zeroconf contacts, minimized groupchats
        def invitable(contact, contact_transport=None):
            return (contact.jid not in self.auto_jids and
                contact.jid != app.get_jid_from_account(self.account) and
                contact.jid not in app.interface.minimized_controls[account] and
                not contact.is_transport() and
                contact_transport in ('jabber', None))

        # set jabber id and pseudos
        for account in app.contacts.get_accounts():
            if app.connections[account].is_zeroconf:
                continue
            for jid in app.contacts.get_jid_list(account):
                contact = app.contacts.get_contact_with_highest_priority(
                    account, jid)
                contact_transport = app.get_transport_name_from_jid(jid)
                # Add contact if it can be invited
                if invitable(contact, contact_transport) and \
                contact.show not in ('offline', 'error'):
                    icon_name = gtkgui_helpers.get_iconset_name_for(contact.show)
                    name = contact.name
                    if name == '':
                        name = jid.split('@')[0]
                    iter_ = self.guests_store.append([icon_name, name, jid])
                    # preselect treeview rows
                    if self.preselected_jids and jid in self.preselected_jids:
                        path = self.guests_store.get_path(iter_)
                        self.guests_treeview.get_selection().select_path(path)

        app.ged.register_event_handler('unique-room-id-supported', ged.GUI1,
            self._nec_unique_room_id_supported)
        app.ged.register_event_handler('unique-room-id-not-supported',
            ged.GUI1, self._nec_unique_room_id_not_supported)

        # show all
        self.window.show_all()

        self.xml.connect_signals(self)

    def on_chat_to_muc_window_destroy(self, widget):
        app.ged.remove_event_handler('unique-room-id-supported', ged.GUI1,
            self._nec_unique_room_id_supported)
        app.ged.remove_event_handler('unique-room-id-not-supported', ged.GUI1,
            self._nec_unique_room_id_not_supported)
        self.instances.remove(self)

    def on_chat_to_muc_window_key_press_event(self, widget, event):
        if event.keyval == Gdk.KEY_Escape: # ESCAPE
            self.window.destroy()

    def on_invite_button_clicked(self, widget):
        row = self.server_list_comboboxentry.get_child().get_displayed_row()
        model = self.server_list_comboboxentry.get_model()
        server = model[row][0].strip()
        if server == '':
            return
        app.connections[self.account].check_unique_room_id_support(server, self)

    def _nec_unique_room_id_supported(self, obj):
        if obj.instance != self:
            return
        guest_list = []
        guests = self.guests_treeview.get_selection().get_selected_rows()
        for guest in guests[1]:
            iter_ = self.guests_store.get_iter(guest)
            guest_list.append(self.guests_store[iter_][2])
        for guest in self.auto_jids:
            guest_list.append(guest)
        room_jid = obj.room_id + '@' + obj.server
        app.automatic_rooms[self.account][room_jid] = {}
        app.automatic_rooms[self.account][room_jid]['invities'] = guest_list
        app.automatic_rooms[self.account][room_jid]['continue_tag'] = True
        app.interface.join_gc_room(self.account, room_jid,
            app.nicks[self.account], None, is_continued=True)
        self.window.destroy()

    def on_cancel_button_clicked(self, widget):
        self.window.destroy()

    def _nec_unique_room_id_not_supported(self, obj):
        if obj.instance != self:
            return
        obj.room_id = app.nicks[self.account].lower().replace(' ', '') + \
            str(randrange(9999999))
        self._nec_unique_room_id_supported(obj)

class DataFormWindow(Dialog):
    def __init__(self, form, on_response_ok):
        self.df_response_ok = on_response_ok
        Dialog.__init__(self, None, 'test', [(Gtk.STOCK_CANCEL,
            Gtk.ResponseType.CANCEL), (Gtk.STOCK_OK, Gtk.ResponseType.OK)],
            on_response_ok=self.on_ok)
        self.set_resizable(True)
        gtkgui_helpers.resize_window(self, 600, 400)
        self.dataform_widget = dataforms_widget.DataFormWidget()
        self.dataform = dataforms.ExtendForm(node=form)
        self.dataform_widget.set_sensitive(True)
        self.dataform_widget.data_form = self.dataform
        self.dataform_widget.show_all()
        self.get_content_area().pack_start(self.dataform_widget, True, True, 0)

    def on_ok(self):
        form = self.dataform_widget.data_form
        if isinstance(self.df_response_ok, tuple):
            self.df_response_ok[0](form, *self.df_response_ok[1:])
        else:
            self.df_response_ok(form)
        self.destroy()


class ResourceConflictDialog(TimeoutDialog, InputDialog):
    def __init__(self, title, text, resource, ok_handler):
        TimeoutDialog.__init__(self, 15)
        InputDialog.__init__(self, title, text, input_str=resource,
                is_modal=False, ok_handler=ok_handler)
        self.title_text = title
        self.run_timeout()

    def on_timeout(self):
        self.on_okbutton_clicked(None)



class VoIPCallReceivedDialog(object):
    instances = {}
    def __init__(self, account, contact_jid, sid, content_types):
        self.instances[(contact_jid, sid)] = self
        self.account = account
        self.fjid = contact_jid
        self.sid = sid
        self.content_types = content_types

        xml = gtkgui_helpers.get_gtk_builder('voip_call_received_dialog.ui')
        xml.connect_signals(self)

        jid = app.get_jid_without_resource(self.fjid)
        contact = app.contacts.get_first_contact_from_jid(account, jid)
        if contact and contact.name:
            self.contact_text = '%s (%s)' % (contact.name, jid)
        else:
            self.contact_text = contact_jid

        self.dialog = xml.get_object('voip_call_received_messagedialog')
        self.set_secondary_text()

        self.dialog.show_all()

    @classmethod
    def get_dialog(cls, jid, sid):
        if (jid, sid) in cls.instances:
            return cls.instances[(jid, sid)]
        else:
            return None

    def set_secondary_text(self):
        if 'audio' in self.content_types and 'video' in self.content_types:
            types_text = _('an audio and video')
        elif 'audio' in self.content_types:
            types_text = _('an audio')
        elif 'video' in self.content_types:
            types_text = _('a video')

        # do the substitution
        self.dialog.set_property('secondary-text',
            _('%(contact)s wants to start %(type)s session with you. Do you want '
            'to answer the call?') % {'contact': self.contact_text,
            'type': types_text})

    def add_contents(self, content_types):
        for type_ in content_types:
            if type_ not in self.content_types:
                self.content_types.add(type_)
        self.set_secondary_text()

    def remove_contents(self, content_types):
        for type_ in content_types:
            if type_ in self.content_types:
                self.content_types.remove(type_)
        if not self.content_types:
            self.dialog.destroy()
        else:
            self.set_secondary_text()

    def on_voip_call_received_messagedialog_destroy(self, dialog):
        if (self.fjid, self.sid) in self.instances:
            del self.instances[(self.fjid, self.sid)]

    def on_voip_call_received_messagedialog_close(self, dialog):
        return self.on_voip_call_received_messagedialog_response(dialog,
                Gtk.ResponseType.NO)

    def on_voip_call_received_messagedialog_response(self, dialog, response):
        # we've got response from user, either stop connecting or accept the call
        session = app.connections[self.account].get_jingle_session(self.fjid,
            self.sid)
        if not session:
            dialog.destroy()
            return
        if response == Gtk.ResponseType.YES:
            #TODO: Ensure that ctrl.contact.resource == resource
            jid = app.get_jid_without_resource(self.fjid)
            resource = app.get_resource_from_jid(self.fjid)
            ctrl = (app.interface.msg_win_mgr.get_control(self.fjid, self.account)
                or app.interface.msg_win_mgr.get_control(jid, self.account)
                or app.interface.new_chat_from_jid(self.account, jid))

            # Chat control opened, update content's status
            audio = session.get_content('audio')
            video = session.get_content('video')
            if audio and not audio.negotiated:
                ctrl.set_audio_state('connecting', self.sid)
            if video and not video.negotiated:
                video_hbox = ctrl.xml.get_object('video_hbox')
                video_hbox.set_no_show_all(False)
                if app.config.get('video_see_self'):
                    fixed = ctrl.xml.get_object('outgoing_fixed')
                    fixed.set_no_show_all(False)
                video_hbox.show_all()
                ctrl.xml.get_object('incoming_drawingarea').realize()
                if os.name == 'nt':
                    in_xid = ctrl.xml.get_object('incoming_drawingarea').\
                        get_window().handle
                else:
                    in_xid = ctrl.xml.get_object('incoming_drawingarea').\
                        get_property('window').get_xid()
                content = session.get_content('video')
                # move outgoing stream to chat window
                if app.config.get('video_see_self'):
                    ctrl.xml.get_object('outgoing_drawingarea').realize()
                    if os.name == 'nt':
                        out_xid = ctrl.xml.get_object('outgoing_drawingarea').\
                            get_window().handle
                    else:
                        out_xid = ctrl.xml.get_object('outgoing_drawingarea').\
                            get_property('window').get_xid()
                    b = content.src_bin
                    found = False
                    for e in b.children:
                        if e.get_name().startswith('autovideosink'):
                            found = True
                            break
                    if found:
                        for f in e.children:
                            if f.get_name().startswith('autovideosink'):
                                f.set_window_handle(out_xid)
                                content.out_xid = out_xid
                                break
                content.in_xid = in_xid
                ctrl.set_video_state('connecting', self.sid)
            # Now, accept the content/sessions.
            # This should be done after the chat control is running
            if not session.accepted:
                session.approve_session()
            for content in self.content_types:
                session.approve_content(content)
        else: # response==Gtk.ResponseType.NO
            if not session.accepted:
                session.decline_session()
            else:
                for content in self.content_types:
                    session.reject_content(content)

        dialog.destroy()

class CertificatDialog(InformationDialog):
    def __init__(self, parent, account, cert):
        issuer = cert.get_issuer()
        subject = cert.get_subject()
        InformationDialog.__init__(self,
            _('Certificate for account %s') % account, _('''<b>Issued to:</b>
Common Name (CN): %(scn)s
Organization (O): %(sorg)s
Organizationl Unit (OU): %(sou)s
Serial Number: %(sn)s

<b>Issued by:</b>
Common Name (CN): %(icn)s
Organization (O): %(iorg)s
Organizationl Unit (OU): %(iou)s

<b>Validity:</b>
Issued on: %(io)s
Expires on: %(eo)s

<b>Fingerprint</b>
SHA-1 Fingerprint: %(sha1)s

SHA256 Fingerprint: %(sha256)s
''') % {
            'scn': subject.commonName, 'sorg': subject.organizationName,
            'sou': subject.organizationalUnitName,
            'sn': cert.get_serial_number(), 'icn': issuer.commonName,
            'iorg': issuer.organizationName,
            'iou': issuer.organizationalUnitName,
            'io': cert.get_notBefore().decode('utf-8'),
            'eo': cert.get_notAfter().decode('utf-8'),
            'sha1': cert.digest('sha1').decode('utf-8'),
            'sha256': cert.digest('sha256').decode('utf-8')})
        pix = gtkgui_helpers.get_icon_pixmap('application-certificate', size=32,
            quiet=True)
        if pix:
            img =  Gtk.Image.new_from_pixbuf(pix)
            img.show_all()
            self.set_image(img)
        self.set_transient_for(parent)
        self.set_title(_('Certificate for account %s') % account)


class CheckFingerprintDialog(YesNoDialog):
    def __init__(self, pritext='', sectext='', checktext='',
    on_response_yes=None, on_response_no=None, account=None, certificate=None):
        self.account = account
        self.cert = certificate
        YesNoDialog.__init__(self, pritext, sectext=sectext,
            checktext=checktext, on_response_yes=on_response_yes,
            on_response_no=on_response_no)
        self.set_title(_('SSL Certificate Verification for %s') % account)
        b = Gtk.Button(label=_('View cert…'))
        b.connect('clicked', self.on_cert_clicked)
        b.show_all()
        area = self.get_action_area()
        area.pack_start(b, True, True, 0)

    def on_cert_clicked(self, button):
        CertificatDialog(self, self.account, self.cert)

class SSLErrorDialog(ConfirmationDialogDoubleCheck):
    def __init__(self, account, certificate, pritext, sectext, checktext1,
    checktext2, on_response_ok=None, on_response_cancel=None):
        self.account = account
        self.cert = certificate
        ConfirmationDialogDoubleCheck.__init__(self, pritext, sectext,
            checktext1, checktext2, on_response_ok=on_response_ok,
            on_response_cancel=on_response_cancel, is_modal=False)
        b = Gtk.Button(_('View cert…'))
        b.connect('clicked', self.on_cert_clicked)
        b.show_all()
        area = self.get_action_area()
        area.pack_start(b, True, True, 0)

    def on_cert_clicked(self, button):
        d = CertificatDialog(self, self.account, self.cert)

class ProgressWindow(Gtk.ApplicationWindow):
    def __init__(self, file):
        Gtk.ApplicationWindow.__init__(self)
        self.set_name('HTTPUploadProgressWindow')
        self.set_application(app.app)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_show_menubar(False)
        self.set_title(_('File Transfer'))
        self.set_default_size(250, -1)

        self.event = file.event
        self.file = file
        self.xml = gtkgui_helpers.get_gtk_builder(
            'httpupload_progress_dialog.ui')

        self.label = self.xml.get_object('label')
        self.progressbar = self.xml.get_object('progressbar')

        self.add(self.xml.get_object('box'))

        self.pulse = GLib.timeout_add(100, self._pulse_progressbar)
        self.show_all()

        self.connect('destroy', self._on_destroy)
        app.ged.register_event_handler('httpupload-progress', ged.CORE,
                                       self._on_httpupload_progress)

    def _on_httpupload_progress(self, obj):
        if self.file != obj.file:
            return
        if obj.status == 'request':
            self.label.set_text(_('Requesting HTTP Upload Slot...'))
        elif obj.status == 'close':
            self.destroy()
        elif obj.status == 'upload':
            self.label.set_text(_('Uploading file via HTTP File Upload...'))
        elif obj.status == 'update':
            self.update_progress(obj.seen, obj.total)
        elif obj.status == 'encrypt':
            self.label.set_text(_('Encrypting file...'))

    def _pulse_progressbar(self):
        self.progressbar.pulse()
        return True

    def _on_destroy(self, *args):
        self.event.set()
        if self.pulse:
            GLib.source_remove(self.pulse)
        app.ged.remove_event_handler('httpupload-progress', ged.CORE,
                                     self._on_httpupload_progress)

    def update_progress(self, seen, total):
        if self.event.isSet():
            return
        if self.pulse:
            GLib.source_remove(self.pulse)
            self.pulse = None
        pct = (float(seen) / total) * 100.0
        self.progressbar.set_fraction(float(seen) / total)
        self.progressbar.set_text(str(int(pct)) + "%")
