# -*- coding: utf-8 -*- ## src/dialogs.py ## ## Copyright (C) 2003-2005 Vincent Hanquez ## Copyright (C) 2003-2014 Yann Leboulanger ## Copyright (C) 2005 Alex Mauer ## Copyright (C) 2005-2006 Dimitur Kirov ## Travis Shirk ## Copyright (C) 2005-2008 Nikos Kouremenos ## Copyright (C) 2006-2008 Jean-Marie Traissard ## Copyright (C) 2007 Lukas Petrovicky ## Copyright (C) 2007-2008 Brendan Taylor ## Julien Pivotto ## Stephan Erb ## Copyright (C) 2008 Jonathan Schleifer ## ## 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 . ## from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GLib import os import logging from gajim import gtkgui_helpers from gajim import vcard from gajim import dataforms_widget from random import randrange from gajim.common import ged from gajim.common.const import ACTIVITIES from gajim.common.const import MOODS from gajim.common import app from gajim.common import helpers from gajim.common import i18n from gajim.common.modules import dataforms from gajim.common.exceptions import GajimGeneralException # Compat with Gajim 1.0.3 for plugins from gajim.gtk import * 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: %s') % contact.get_shown_name()) self.xml.get_object('jid_label').set_markup( _('JID: %s') % 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 ACTIVITIES: item = self.xml.get_object(category + '_image') item.set_from_pixbuf( gtkgui_helpers.load_activity_icon(category).get_pixbuf()) item.set_tooltip_text(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='' + ACTIVITIES[category]['category'] \ + '') 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 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=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 ACTIVITIES: if not self.subactivity in 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 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(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 MOODS: self.mood_buttons[self.mood].set_active(True) self.label.set_text(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(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 \ ACTIVITIES: if 'subactivity' in self.pep_dict and self.pep_dict['subactivity'] \ in 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 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 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].get_module('Presence').subscribed(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].get_module('Presence').unsubscribed(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 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() #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( _('%(jid)s would like you to %(action)s 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): con = app.connections[self.account] con.get_module('Presence').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].get_module('Presence').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 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].get_module('MUC').decline( 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('' + during_text + '') 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 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 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 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 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)) + "%")