2018-09-05 02:59:34 +02:00
|
|
|
# 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/>.
|
2004-05-15 18:50:38 +02:00
|
|
|
|
2018-09-21 23:55:57 +02:00
|
|
|
from typing import Dict # pylint: disable=unused-import
|
|
|
|
from typing import List # pylint: disable=unused-import
|
|
|
|
from typing import Tuple # pylint: disable=unused-import
|
2017-11-25 17:37:46 +01:00
|
|
|
|
2005-05-17 19:37:50 +02:00
|
|
|
import os
|
2018-07-16 23:22:33 +02:00
|
|
|
import logging
|
2018-09-21 23:55:57 +02:00
|
|
|
from random import randrange
|
|
|
|
|
|
|
|
from gi.repository import Gtk
|
|
|
|
from gi.repository import Gdk
|
|
|
|
from gi.repository import GLib
|
2005-05-20 17:58:23 +02:00
|
|
|
|
2017-06-13 23:58:06 +02:00
|
|
|
from gajim import gtkgui_helpers
|
|
|
|
from gajim import vcard
|
|
|
|
from gajim import dataforms_widget
|
2007-12-12 09:44:46 +01:00
|
|
|
|
2017-06-13 23:58:06 +02:00
|
|
|
from gajim.common import ged
|
2018-09-13 23:56:12 +02:00
|
|
|
from gajim.common.i18n import _
|
2018-07-16 23:22:33 +02:00
|
|
|
from gajim.common.const import ACTIVITIES
|
2018-07-05 18:52:43 +02:00
|
|
|
from gajim.common.const import MOODS
|
2005-07-21 23:39:47 +02:00
|
|
|
|
2017-08-13 13:18:56 +02:00
|
|
|
from gajim.common import app
|
2017-06-13 23:58:06 +02:00
|
|
|
from gajim.common import helpers
|
|
|
|
from gajim.common import i18n
|
2018-07-21 14:35:28 +02:00
|
|
|
from gajim.common.modules import dataforms
|
2017-06-13 23:58:06 +02:00
|
|
|
from gajim.common.exceptions import GajimGeneralException
|
2004-05-15 18:50:38 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
# Compat with Gajim 1.0.3 for plugins
|
|
|
|
from gajim.gtk import *
|
|
|
|
|
2017-11-25 17:37:46 +01:00
|
|
|
|
2016-11-05 16:22:03 +01:00
|
|
|
log = logging.getLogger('gajim.dialogs')
|
|
|
|
|
2016-10-31 02:11:46 +01:00
|
|
|
|
2005-06-10 23:14:16 +02:00
|
|
|
class EditGroupsDialog:
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
|
|
|
Class for the edit group dialog window
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, list_):
|
|
|
|
"""
|
|
|
|
list_ is a list of (contact, account) tuples
|
|
|
|
"""
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder('edit_groups_dialog.ui')
|
|
|
|
self.dialog = self.xml.get_object('edit_groups_dialog')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.dialog.set_transient_for(app.interface.roster.window)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.list_ = list_
|
|
|
|
self.changes_made = False
|
2010-02-08 15:08:40 +01:00
|
|
|
self.treeview = self.xml.get_object('groups_treeview')
|
2010-02-10 17:59:17 +01:00
|
|
|
if len(list_) == 1:
|
|
|
|
contact = list_[0][0]
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.get_object('nickname_label').set_markup(
|
2010-02-10 17:59:17 +01:00
|
|
|
_('Contact name: <i>%s</i>') % contact.get_shown_name())
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.get_object('jid_label').set_markup(
|
2017-02-04 23:29:45 +01:00
|
|
|
_('JID: <i>%s</i>') % contact.jid)
|
2010-02-10 17:59:17 +01:00
|
|
|
else:
|
2010-02-08 15:08:40 +01:00
|
|
|
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()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.connect_signals(self)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.init_list()
|
|
|
|
|
|
|
|
self.dialog.show_all()
|
|
|
|
if self.changes_made:
|
|
|
|
for (contact, account) in self.list_:
|
2017-08-13 13:18:56 +02:00
|
|
|
app.connections[account].update_contact(contact.jid,
|
2010-12-03 13:38:18 +01:00
|
|
|
contact.name, contact.groups)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
def on_edit_groups_dialog_response(self, widget, response_id):
|
2012-12-23 16:23:43 +01:00
|
|
|
if response_id == Gtk.ResponseType.CLOSE:
|
2010-02-10 17:59:17 +01:00
|
|
|
self.dialog.destroy()
|
|
|
|
|
|
|
|
def remove_group(self, group):
|
|
|
|
"""
|
|
|
|
Remove group group from all contacts and all their brothers
|
|
|
|
"""
|
|
|
|
for (contact, account) in self.list_:
|
2017-08-13 13:18:56 +02:00
|
|
|
app.interface.roster.remove_contact_from_groups(contact.jid,
|
2010-12-03 13:38:18 +01:00
|
|
|
account, [group])
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# FIXME: Ugly workaround.
|
2018-09-18 17:58:39 +02:00
|
|
|
# pylint: disable=undefined-loop-variable
|
2017-08-13 13:18:56 +02:00
|
|
|
app.interface.roster.draw_group(_('General'), account)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
def add_group(self, group):
|
|
|
|
"""
|
|
|
|
Add group group to all contacts and all their brothers
|
|
|
|
"""
|
|
|
|
for (contact, account) in self.list_:
|
2017-08-13 13:18:56 +02:00
|
|
|
app.interface.roster.add_contact_to_groups(contact.jid, account,
|
2010-12-03 13:38:18 +01:00
|
|
|
[group])
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2010-12-03 13:38:18 +01:00
|
|
|
# FIXME: Ugly workaround.
|
|
|
|
# Maybe we haven't been in any group (defaults to General)
|
2018-09-18 17:58:39 +02:00
|
|
|
# pylint: disable=undefined-loop-variable
|
2017-08-13 13:18:56 +02:00
|
|
|
app.interface.roster.draw_group(_('General'), account)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
def on_add_button_clicked(self, widget):
|
2013-01-01 19:44:25 +01:00
|
|
|
group = self.xml.get_object('group_entry').get_text()
|
2010-02-10 17:59:17 +01:00
|
|
|
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()
|
2012-12-23 16:23:43 +01:00
|
|
|
iter_ = model.get_iter_first()
|
2010-02-10 17:59:17 +01:00
|
|
|
while iter_:
|
2013-01-01 19:44:25 +01:00
|
|
|
if model.get_value(iter_, 0) == group:
|
2010-02-10 17:59:17 +01:00
|
|
|
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]
|
2013-01-01 19:44:25 +01:00
|
|
|
group = model[path][0]
|
2010-02-10 17:59:17 +01:00
|
|
|
if model[path][1]:
|
|
|
|
self.add_group(group)
|
|
|
|
else:
|
|
|
|
self.remove_group(group)
|
|
|
|
|
|
|
|
def init_list(self):
|
2012-12-23 16:23:43 +01:00
|
|
|
store = Gtk.ListStore(str, bool, bool)
|
2010-02-10 17:59:17 +01:00
|
|
|
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)
|
2017-08-13 13:18:56 +02:00
|
|
|
for g in app.groups[account].keys():
|
2010-02-10 17:59:17 +01:00
|
|
|
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)
|
2012-12-23 16:23:43 +01:00
|
|
|
column = Gtk.TreeViewColumn(_('Group'))
|
2010-02-10 17:59:17 +01:00
|
|
|
column.set_expand(True)
|
|
|
|
self.treeview.append_column(column)
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer = Gtk.CellRendererText()
|
2012-12-30 23:29:51 +01:00
|
|
|
column.pack_start(renderer, True)
|
|
|
|
column.add_attribute(renderer, 'text', 0)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
column = Gtk.TreeViewColumn(_('In the group'))
|
2010-02-10 17:59:17 +01:00
|
|
|
column.set_expand(False)
|
|
|
|
self.treeview.append_column(column)
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer = Gtk.CellRendererToggle()
|
2012-12-30 23:29:51 +01:00
|
|
|
column.pack_start(renderer, True)
|
2010-02-10 17:59:17 +01:00
|
|
|
renderer.set_property('activatable', True)
|
|
|
|
renderer.connect('toggled', self.group_toggled_cb)
|
2012-12-30 23:29:51 +01:00
|
|
|
column.add_attribute(renderer, 'active', 1)
|
|
|
|
column.add_attribute(renderer, 'inconsistent', 2)
|
2005-03-17 18:41:09 +01:00
|
|
|
|
2005-06-10 23:14:16 +02:00
|
|
|
class PassphraseDialog:
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
|
|
|
Class for Passphrase dialog
|
|
|
|
"""
|
|
|
|
def __init__(self, titletext, labeltext, checkbuttontext=None,
|
2018-03-07 19:39:33 +01:00
|
|
|
ok_handler=None, cancel_handler=None, transient_for=None):
|
2010-02-08 15:08:40 +01:00
|
|
|
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')
|
2010-02-10 17:59:17 +01:00
|
|
|
self.passphrase = -1
|
|
|
|
self.window.set_title(titletext)
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.get_object('message_label').set_text(labeltext)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
self.ok = False
|
|
|
|
|
|
|
|
self.cancel_handler = cancel_handler
|
|
|
|
self.ok_handler = ok_handler
|
2010-02-08 15:08:40 +01:00
|
|
|
okbutton = self.xml.get_object('ok_button')
|
2010-02-10 17:59:17 +01:00
|
|
|
okbutton.connect('clicked', self.on_okbutton_clicked)
|
2010-02-08 15:08:40 +01:00
|
|
|
cancelbutton = self.xml.get_object('cancel_button')
|
2010-02-10 17:59:17 +01:00
|
|
|
cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.connect_signals(self)
|
2018-03-07 19:39:33 +01:00
|
|
|
if transient_for is None:
|
|
|
|
transient_for = app.app.get_active_window()
|
|
|
|
self.window.set_transient_for(transient_for)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.show_all()
|
|
|
|
|
|
|
|
self.check = bool(checkbuttontext)
|
2018-09-18 12:06:01 +02:00
|
|
|
checkbutton = self.xml.get_object('save_passphrase_checkbutton')
|
2010-02-10 17:59:17 +01:00
|
|
|
if self.check:
|
|
|
|
checkbutton.set_label(checkbuttontext)
|
|
|
|
else:
|
|
|
|
checkbutton.hide()
|
|
|
|
|
|
|
|
def on_okbutton_clicked(self, widget):
|
|
|
|
if not self.ok_handler:
|
|
|
|
return
|
|
|
|
|
2013-01-01 19:44:25 +01:00
|
|
|
passph = self.passphrase_entry.get_text()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
if self.check:
|
2010-02-08 15:08:40 +01:00
|
|
|
checked = self.xml.get_object('save_passphrase_checkbutton').\
|
2010-02-10 17:59:17 +01:00
|
|
|
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()
|
2004-05-15 18:50:38 +02:00
|
|
|
|
2005-06-08 15:45:30 +02:00
|
|
|
class ChooseGPGKeyDialog:
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
|
|
|
Class for GPG key dialog
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, title_text, prompt_text, secret_keys, on_response,
|
2012-07-05 21:23:56 +02:00
|
|
|
selected=None, transient_for=None):
|
2010-02-10 17:59:17 +01:00
|
|
|
'''secret_keys : {keyID: userName, ...}'''
|
|
|
|
self.on_response = on_response
|
2010-02-08 15:08:40 +01:00
|
|
|
xml = gtkgui_helpers.get_gtk_builder('choose_gpg_key_dialog.ui')
|
|
|
|
self.window = xml.get_object('choose_gpg_key_dialog')
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.set_title(title_text)
|
2013-01-01 11:46:13 +01:00
|
|
|
self.window.set_transient_for(transient_for)
|
2010-02-08 15:08:40 +01:00
|
|
|
self.keys_treeview = xml.get_object('keys_treeview')
|
|
|
|
prompt_label = xml.get_object('prompt_label')
|
2010-02-10 17:59:17 +01:00
|
|
|
prompt_label.set_text(prompt_text)
|
2012-12-23 16:23:43 +01:00
|
|
|
model = Gtk.ListStore(str, str)
|
2010-02-10 17:59:17 +01:00
|
|
|
model.set_sort_func(1, self.sort_keys)
|
2012-12-23 16:23:43 +01:00
|
|
|
model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.keys_treeview.set_model(model)
|
|
|
|
#columns
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer = Gtk.CellRendererText()
|
2016-02-21 14:04:31 +01:00
|
|
|
self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
|
|
|
|
renderer, text=0)
|
|
|
|
col = self.keys_treeview.get_column(0)
|
2010-02-10 17:59:17 +01:00
|
|
|
col.set_sort_column_id(0)
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer = Gtk.CellRendererText()
|
2016-02-21 14:04:31 +01:00
|
|
|
self.keys_treeview.insert_column_with_attributes(-1, _('Contact name'),
|
|
|
|
renderer, text=1)
|
|
|
|
col = self.keys_treeview.get_column(1)
|
2010-02-10 17:59:17 +01:00
|
|
|
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)
|
2012-12-23 16:23:43 +01:00
|
|
|
self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.show_all()
|
|
|
|
|
2013-01-05 15:32:53 +01:00
|
|
|
def sort_keys(self, model, iter1, iter2, data=None):
|
2010-02-10 17:59:17 +01:00
|
|
|
value1 = model[iter1][1]
|
|
|
|
value2 = model[iter2][1]
|
|
|
|
if value1 == _('None'):
|
|
|
|
return -1
|
2018-09-18 10:14:04 +02:00
|
|
|
if value2 == _('None'):
|
2010-02-10 17:59:17 +01:00
|
|
|
return 1
|
2018-09-18 10:14:04 +02:00
|
|
|
if value1 < value2:
|
2010-02-10 17:59:17 +01:00
|
|
|
return -1
|
|
|
|
return 1
|
|
|
|
|
|
|
|
def on_dialog_response(self, dialog, response):
|
|
|
|
selection = self.keys_treeview.get_selection()
|
|
|
|
(model, iter_) = selection.get_selected()
|
2012-12-23 16:23:43 +01:00
|
|
|
if iter_ and response == Gtk.ResponseType.OK:
|
2018-09-18 12:06:01 +02:00
|
|
|
keyID = [model[iter_][0], model[iter_][1]]
|
2010-02-10 17:59:17 +01:00
|
|
|
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)
|
2005-07-23 00:49:03 +02:00
|
|
|
|
|
|
|
|
2007-03-31 11:09:16 +02:00
|
|
|
class ChangeActivityDialog:
|
2018-09-18 08:37:18 +02:00
|
|
|
PAGELIST = [
|
|
|
|
'doing_chores', 'drinking', 'eating', 'exercising', 'grooming',
|
|
|
|
'having_appointment', 'inactive', 'relaxing', 'talking', 'traveling',
|
|
|
|
'working']
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-09-18 08:37:18 +02:00
|
|
|
def __init__(self, on_response, activity_=None, subactivity_=None, text=''):
|
2010-02-10 17:59:17 +01:00
|
|
|
self.on_response = on_response
|
2018-09-18 08:37:18 +02:00
|
|
|
self.activity = activity_
|
|
|
|
self.subactivity = subactivity_
|
2010-02-10 17:59:17 +01:00
|
|
|
self.text = text
|
2010-10-03 20:09:42 +02:00
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder('change_activity_dialog.ui')
|
2010-02-08 15:08:40 +01:00
|
|
|
self.window = self.xml.get_object('change_activity_dialog')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.window.set_transient_for(app.interface.roster.window)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.checkbutton = self.xml.get_object('enable_checkbutton')
|
|
|
|
self.notebook = self.xml.get_object('notebook')
|
|
|
|
self.entry = self.xml.get_object('description_entry')
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
rbtns = {}
|
|
|
|
group = None
|
|
|
|
|
2018-07-03 00:10:55 +02:00
|
|
|
for category in ACTIVITIES:
|
2010-02-08 15:08:40 +01:00
|
|
|
item = self.xml.get_object(category + '_image')
|
2010-02-10 17:59:17 +01:00
|
|
|
item.set_from_pixbuf(
|
2018-09-18 08:37:18 +02:00
|
|
|
gtkgui_helpers.load_activity_icon(category).get_pixbuf())
|
2018-07-03 00:10:55 +02:00
|
|
|
item.set_tooltip_text(ACTIVITIES[category]['category'])
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
vbox = self.xml.get_object(category + '_vbox')
|
2010-02-10 17:59:17 +01:00
|
|
|
vbox.set_border_width(5)
|
|
|
|
|
|
|
|
# Other
|
|
|
|
act = category + '_other'
|
|
|
|
|
|
|
|
if group:
|
2013-01-03 20:28:04 +01:00
|
|
|
rbtns[act] = Gtk.RadioButton()
|
|
|
|
rbtns[act].join_group(group)
|
2010-02-10 17:59:17 +01:00
|
|
|
else:
|
2012-12-23 16:23:43 +01:00
|
|
|
rbtns[act] = group = Gtk.RadioButton()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-09-18 08:37:18 +02:00
|
|
|
icon = gtkgui_helpers.load_activity_icon(category, self.activity)
|
2015-07-18 17:27:50 +02:00
|
|
|
hbox = Gtk.HBox(homogeneous=False, spacing=5)
|
2018-09-18 08:37:18 +02:00
|
|
|
hbox.pack_start(icon, False, False, 0)
|
|
|
|
lbl = Gtk.Label(
|
|
|
|
label='<b>%s</b>' % ACTIVITIES[category]['category'])
|
2010-02-10 17:59:17 +01:00
|
|
|
lbl.set_use_markup(True)
|
|
|
|
hbox.pack_start(lbl, False, False, 0)
|
|
|
|
rbtns[act].add(hbox)
|
2018-09-18 08:37:18 +02:00
|
|
|
rbtns[act].connect(
|
|
|
|
'toggled', self.on_rbtn_toggled, [category, 'other'])
|
2010-02-10 17:59:17 +01:00
|
|
|
vbox.pack_start(rbtns[act], False, False, 0)
|
|
|
|
|
2018-09-18 08:37:18 +02:00
|
|
|
activities = list(ACTIVITIES[category].keys())
|
2010-02-10 17:59:17 +01:00
|
|
|
activities.sort()
|
|
|
|
for activity in activities:
|
|
|
|
if activity == 'category':
|
|
|
|
continue
|
|
|
|
|
|
|
|
act = category + '_' + activity
|
|
|
|
|
|
|
|
if group:
|
2013-01-03 20:28:04 +01:00
|
|
|
rbtns[act] = Gtk.RadioButton()
|
|
|
|
rbtns[act].join_group(group)
|
2010-02-10 17:59:17 +01:00
|
|
|
else:
|
2012-12-23 16:23:43 +01:00
|
|
|
rbtns[act] = group = Gtk.RadioButton()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-09-18 08:37:18 +02:00
|
|
|
icon = gtkgui_helpers.load_activity_icon(category, activity)
|
|
|
|
label = Gtk.Label(label=ACTIVITIES[category][activity])
|
2015-07-18 17:27:50 +02:00
|
|
|
hbox = Gtk.HBox(homogeneous=False, spacing=5)
|
2018-09-18 08:37:18 +02:00
|
|
|
hbox.pack_start(icon, False, False, 0)
|
|
|
|
hbox.pack_start(label, False, False, 0)
|
|
|
|
rbtns[act].connect(
|
|
|
|
'toggled', self.on_rbtn_toggled, [category, activity])
|
2010-02-10 17:59:17 +01:00
|
|
|
rbtns[act].add(hbox)
|
|
|
|
vbox.pack_start(rbtns[act], False, False, 0)
|
|
|
|
|
|
|
|
|
2010-10-03 20:09:42 +02:00
|
|
|
self.default_radio = rbtns['doing_chores_other']
|
|
|
|
|
2018-07-03 00:10:55 +02:00
|
|
|
if self.activity in ACTIVITIES:
|
|
|
|
if not self.subactivity in ACTIVITIES[self.activity]:
|
2010-02-10 17:59:17 +01:00
|
|
|
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(
|
2018-09-18 08:37:18 +02:00
|
|
|
self.PAGELIST.index(self.activity))
|
2010-02-10 17:59:17 +01:00
|
|
|
self.entry.set_text(text)
|
|
|
|
|
|
|
|
else:
|
|
|
|
self.checkbutton.set_active(False)
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.connect_signals(self)
|
2012-12-23 16:23:43 +01:00
|
|
|
self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
2010-02-10 17:59:17 +01:00
|
|
|
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())
|
2010-10-03 20:09:42 +02:00
|
|
|
if not self.activity:
|
|
|
|
self.default_radio.set_active(True)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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():
|
2018-09-18 08:37:18 +02:00
|
|
|
self.on_response(
|
|
|
|
self.activity, self.subactivity, self.entry.get_text())
|
2010-02-10 17:59:17 +01:00
|
|
|
else:
|
|
|
|
self.on_response(None, None, '')
|
|
|
|
self.window.destroy()
|
|
|
|
|
|
|
|
def on_cancel_button_clicked(self, widget):
|
|
|
|
self.window.destroy()
|
2007-03-31 11:09:16 +02:00
|
|
|
|
2007-03-31 00:52:11 +02:00
|
|
|
class ChangeMoodDialog:
|
2010-02-10 17:59:17 +01:00
|
|
|
COLS = 11
|
|
|
|
|
2018-09-18 08:37:18 +02:00
|
|
|
def __init__(self, on_response, mood_=None, text=''):
|
2010-02-10 17:59:17 +01:00
|
|
|
self.on_response = on_response
|
2018-09-18 08:37:18 +02:00
|
|
|
self.mood = mood_
|
2010-02-10 17:59:17 +01:00
|
|
|
self.text = text
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder('change_mood_dialog.ui')
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.window = self.xml.get_object('change_mood_dialog')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.window.set_transient_for(app.interface.roster.window)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.set_title(_('Set Mood'))
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
table = self.xml.get_object('mood_icons_table')
|
|
|
|
self.label = self.xml.get_object('mood_label')
|
|
|
|
self.entry = self.xml.get_object('description_entry')
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
no_mood_button = self.xml.get_object('no_mood_button')
|
2010-02-10 17:59:17 +01:00
|
|
|
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 = []
|
2018-07-05 18:52:43 +02:00
|
|
|
for mood in MOODS:
|
2010-02-10 17:59:17 +01:00
|
|
|
self.MOODS.append(mood)
|
|
|
|
self.MOODS.sort()
|
|
|
|
|
|
|
|
for mood in self.MOODS:
|
2013-01-03 20:28:04 +01:00
|
|
|
self.mood_buttons[mood] = Gtk.RadioButton()
|
|
|
|
self.mood_buttons[mood].join_group(no_mood_button)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.mood_buttons[mood].set_mode(False)
|
|
|
|
self.mood_buttons[mood].add(gtkgui_helpers.load_mood_icon(mood))
|
2012-12-23 16:23:43 +01:00
|
|
|
self.mood_buttons[mood].set_relief(Gtk.ReliefStyle.NONE)
|
2018-07-05 18:52:43 +02:00
|
|
|
self.mood_buttons[mood].set_tooltip_text(MOODS[mood])
|
2010-02-10 17:59:17 +01:00
|
|
|
self.mood_buttons[mood].connect('clicked',
|
2015-08-21 22:03:24 +02:00
|
|
|
self.on_mood_button_clicked, mood)
|
|
|
|
table.attach(self.mood_buttons[mood], x, y, 1, 1)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# Calculate the next position
|
|
|
|
x += 1
|
|
|
|
if x >= self.COLS:
|
|
|
|
x = 0
|
|
|
|
y += 1
|
|
|
|
|
2018-07-05 18:52:43 +02:00
|
|
|
if self.mood in MOODS:
|
2010-02-10 17:59:17 +01:00
|
|
|
self.mood_buttons[self.mood].set_active(True)
|
2018-07-05 18:52:43 +02:00
|
|
|
self.label.set_text(MOODS[self.mood])
|
2010-02-10 17:59:17 +01:00
|
|
|
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)
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.connect_signals(self)
|
2012-12-23 16:23:43 +01:00
|
|
|
self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.show_all()
|
|
|
|
|
|
|
|
def on_mood_button_clicked(self, widget, data):
|
|
|
|
if data:
|
2018-07-05 18:52:43 +02:00
|
|
|
self.label.set_text(MOODS[data])
|
2010-02-10 17:59:17 +01:00
|
|
|
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)'''
|
2013-01-01 19:44:25 +01:00
|
|
|
message = self.entry.get_text()
|
2010-02-10 17:59:17 +01:00
|
|
|
self.on_response(self.mood, message)
|
|
|
|
self.window.destroy()
|
|
|
|
|
|
|
|
def on_cancel_button_clicked(self, widget):
|
|
|
|
self.window.destroy()
|
2007-03-31 00:52:11 +02:00
|
|
|
|
2009-10-20 11:20:11 +02:00
|
|
|
class TimeoutDialog:
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
|
|
|
Class designed to be derivated to create timeout'd dialogs (dialogs that
|
|
|
|
closes automatically after a timeout)
|
|
|
|
"""
|
2017-08-23 18:57:38 +02:00
|
|
|
def __init__(self, timeout):
|
2010-02-10 17:59:17 +01:00
|
|
|
self.countdown_left = timeout
|
|
|
|
self.countdown_enabled = True
|
|
|
|
self.title_text = ''
|
|
|
|
|
|
|
|
def run_timeout(self):
|
|
|
|
if self.countdown_left > 0:
|
|
|
|
self.countdown()
|
2013-07-28 20:50:30 +02:00
|
|
|
GLib.timeout_add_seconds(1, self.countdown)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2017-08-23 18:57:38 +02:00
|
|
|
def on_timeout(self):
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
|
|
|
To be implemented in derivated classes
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
def countdown(self):
|
|
|
|
if self.countdown_enabled:
|
|
|
|
if self.countdown_left <= 0:
|
|
|
|
self.on_timeout()
|
|
|
|
return False
|
2018-09-18 10:14:04 +02:00
|
|
|
self.dialog.set_title('%s [%s]' % (
|
|
|
|
self.title_text, str(self.countdown_left)))
|
2010-02-10 17:59:17 +01:00
|
|
|
self.countdown_left -= 1
|
|
|
|
return True
|
2018-09-18 10:14:04 +02:00
|
|
|
|
|
|
|
self.dialog.set_title(self.title_text)
|
|
|
|
return False
|
2009-10-20 11:20:11 +02:00
|
|
|
|
|
|
|
class ChangeStatusMessageDialog(TimeoutDialog):
|
2010-02-10 17:59:17 +01:00
|
|
|
def __init__(self, on_response, show=None, show_pep=True):
|
2017-08-13 13:18:56 +02:00
|
|
|
countdown_time = app.config.get('change_status_window_timeout')
|
2017-08-23 18:57:38 +02:00
|
|
|
TimeoutDialog.__init__(self, countdown_time)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.show = show
|
|
|
|
self.pep_dict = {}
|
|
|
|
self.show_pep = show_pep
|
|
|
|
self.on_response = on_response
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder('change_status_message_dialog.ui')
|
|
|
|
self.dialog = self.xml.get_object('change_status_message_dialog')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.dialog.set_transient_for(app.interface.roster.window)
|
2010-02-10 17:59:17 +01:00
|
|
|
msg = None
|
|
|
|
if show:
|
|
|
|
uf_show = helpers.get_uf_show(show)
|
|
|
|
self.title_text = _('%s Status Message') % uf_show
|
2017-08-13 13:18:56 +02:00
|
|
|
msg = app.config.get_per('statusmsg', '_last_' + self.show,
|
2010-02-10 17:59:17 +01:00
|
|
|
'message')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.pep_dict['activity'] = app.config.get_per('statusmsg',
|
2010-11-06 14:01:28 +01:00
|
|
|
'_last_' + self.show, 'activity')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.pep_dict['subactivity'] = app.config.get_per('statusmsg',
|
2010-11-06 14:01:28 +01:00
|
|
|
'_last_' + self.show, 'subactivity')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.pep_dict['activity_text'] = app.config.get_per('statusmsg',
|
2010-11-06 14:01:28 +01:00
|
|
|
'_last_' + self.show, 'activity_text')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.pep_dict['mood'] = app.config.get_per('statusmsg',
|
2010-11-06 14:01:28 +01:00
|
|
|
'_last_' + self.show, 'mood')
|
2017-08-13 13:18:56 +02:00
|
|
|
self.pep_dict['mood_text'] = app.config.get_per('statusmsg',
|
2010-11-06 14:01:28 +01:00
|
|
|
'_last_' + self.show, 'mood_text')
|
2010-02-10 17:59:17 +01:00
|
|
|
else:
|
|
|
|
self.title_text = _('Status Message')
|
|
|
|
self.dialog.set_title(self.title_text)
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
message_textview = self.xml.get_object('message_textview')
|
2010-02-10 17:59:17 +01:00
|
|
|
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 = {'': ['', '', '', '', '', '']}
|
2017-08-13 13:18:56 +02:00
|
|
|
for msg_name in app.config.get_per('statusmsg'):
|
2010-02-10 17:59:17 +01:00
|
|
|
if msg_name.startswith('_last_'):
|
|
|
|
continue
|
|
|
|
opts = []
|
|
|
|
for opt in ['message', 'activity', 'subactivity', 'activity_text',
|
|
|
|
'mood', 'mood_text']:
|
2017-08-13 13:18:56 +02:00
|
|
|
opts.append(app.config.get_per('statusmsg', msg_name, opt))
|
2010-02-10 17:59:17 +01:00
|
|
|
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)
|
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
self.message_liststore = Gtk.ListStore(str) # msg_name
|
2010-02-08 15:08:40 +01:00
|
|
|
self.message_combobox = self.xml.get_object('message_combobox')
|
2010-02-10 17:59:17 +01:00
|
|
|
self.message_combobox.set_model(self.message_liststore)
|
2012-12-23 16:23:43 +01:00
|
|
|
cellrenderertext = Gtk.CellRendererText()
|
2013-01-03 20:01:24 +01:00
|
|
|
self.message_combobox.pack_start(cellrenderertext, True)
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
2010-02-08 15:08:40 +01:00
|
|
|
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)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.run_timeout()
|
|
|
|
self.dialog.connect('response', self.on_dialog_response)
|
2012-12-23 16:23:43 +01:00
|
|
|
self.dialog.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.dialog.show_all()
|
|
|
|
|
|
|
|
def draw_activity(self):
|
|
|
|
"""
|
|
|
|
Set activity button
|
|
|
|
"""
|
2010-02-08 15:08:40 +01:00
|
|
|
img = self.xml.get_object('activity_image')
|
|
|
|
label = self.xml.get_object('activity_button_label')
|
2010-02-10 17:59:17 +01:00
|
|
|
if 'activity' in self.pep_dict and self.pep_dict['activity'] in \
|
2018-07-03 00:10:55 +02:00
|
|
|
ACTIVITIES:
|
2010-10-19 21:43:09 +02:00
|
|
|
if 'subactivity' in self.pep_dict and self.pep_dict['subactivity'] \
|
2018-07-03 00:10:55 +02:00
|
|
|
in ACTIVITIES[self.pep_dict['activity']]:
|
2010-02-10 17:59:17 +01:00
|
|
|
img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
|
2010-10-19 21:43:09 +02:00
|
|
|
self.pep_dict['activity'], self.pep_dict['subactivity']).\
|
|
|
|
get_pixbuf())
|
2010-02-10 17:59:17 +01:00
|
|
|
else:
|
|
|
|
img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
|
2010-10-19 21:43:09 +02:00
|
|
|
self.pep_dict['activity']).get_pixbuf())
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
|
|
|
"""
|
2010-02-08 15:08:40 +01:00
|
|
|
img = self.xml.get_object('mood_image')
|
|
|
|
label = self.xml.get_object('mood_button_label')
|
2018-07-05 18:52:43 +02:00
|
|
|
if 'mood' in self.pep_dict and self.pep_dict['mood'] in MOODS:
|
2010-02-10 17:59:17 +01:00
|
|
|
img.set_from_pixbuf(gtkgui_helpers.load_mood_icon(
|
2010-10-19 21:43:09 +02:00
|
|
|
self.pep_dict['mood']).get_pixbuf())
|
2010-02-10 17:59:17 +01:00
|
|
|
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()
|
2012-12-23 16:23:43 +01:00
|
|
|
self.dialog.response(Gtk.ResponseType.OK)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
def on_dialog_response(self, dialog, response):
|
2012-12-23 16:23:43 +01:00
|
|
|
if response == Gtk.ResponseType.OK:
|
2010-02-10 17:59:17 +01:00
|
|
|
beg, end = self.message_buffer.get_bounds()
|
2013-01-01 19:44:25 +01:00
|
|
|
message = self.message_buffer.get_text(beg, end, True).strip()
|
2010-02-10 17:59:17 +01:00
|
|
|
message = helpers.remove_invalid_xml_chars(message)
|
|
|
|
msg = helpers.to_one_line(message)
|
|
|
|
if self.show:
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', '_last_' + self.show,
|
2010-11-06 14:01:28 +01:00
|
|
|
'message', msg)
|
2010-02-10 17:59:17 +01:00
|
|
|
if self.show_pep:
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', '_last_' + self.show,
|
2010-11-06 14:01:28 +01:00
|
|
|
'activity', self.pep_dict['activity'])
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', '_last_' + self.show,
|
2010-11-06 14:01:28 +01:00
|
|
|
'subactivity', self.pep_dict['subactivity'])
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', '_last_' + self.show,
|
2010-11-06 14:01:28 +01:00
|
|
|
'activity_text', self.pep_dict['activity_text'])
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', '_last_' + self.show,
|
2010-11-06 14:01:28 +01:00
|
|
|
'mood', self.pep_dict['mood'])
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', '_last_' + self.show,
|
2010-11-06 14:01:28 +01:00
|
|
|
'mood_text', self.pep_dict['mood_text'])
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
2013-01-01 19:44:25 +01:00
|
|
|
name = model[active][0]
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
2012-12-23 16:23:43 +01:00
|
|
|
if event.keyval == Gdk.KEY_Return or \
|
2018-09-16 14:42:05 +02:00
|
|
|
event.keyval == Gdk.KEY_KP_Enter: # catch CTRL+ENTER
|
|
|
|
if event.get_state() & Gdk.ModifierType.CONTROL_MASK:
|
2012-12-23 16:23:43 +01:00
|
|
|
self.dialog.response(Gtk.ResponseType.OK)
|
2010-02-10 17:59:17 +01:00
|
|
|
# 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):
|
2010-02-08 15:08:40 +01:00
|
|
|
btn = self.xml.get_object('save_as_preset_button')
|
2010-02-10 17:59:17 +01:00
|
|
|
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(
|
2012-12-23 16:23:43 +01:00
|
|
|
start_iter, finish_iter, True)
|
2010-02-10 17:59:17 +01:00
|
|
|
def on_ok(msg_name):
|
2013-01-01 19:44:25 +01:00
|
|
|
msg_text = status_message_to_save_as_preset
|
2010-02-10 17:59:17 +01:00
|
|
|
msg_text_1l = helpers.to_one_line(msg_text)
|
|
|
|
if not msg_name: # msg_name was ''
|
2013-01-01 19:44:25 +01:00
|
|
|
msg_name = msg_text_1l
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
def on_ok2():
|
2010-11-06 14:01:28 +01:00
|
|
|
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')]
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', msg_name, 'message',
|
2010-11-06 14:01:28 +01:00
|
|
|
msg_text_1l)
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', msg_name, 'activity',
|
2010-11-06 14:01:28 +01:00
|
|
|
self.pep_dict.get('activity'))
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', msg_name, 'subactivity',
|
2010-11-06 14:01:28 +01:00
|
|
|
self.pep_dict.get('subactivity'))
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', msg_name, 'activity_text',
|
2010-11-06 14:01:28 +01:00
|
|
|
self.pep_dict.get('activity_text'))
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', msg_name, 'mood',
|
2010-11-06 14:01:28 +01:00
|
|
|
self.pep_dict.get('mood'))
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.set_per('statusmsg', msg_name, 'mood_text',
|
2010-11-06 14:01:28 +01:00
|
|
|
self.pep_dict.get('mood_text'))
|
2010-02-10 17:59:17 +01:00
|
|
|
if msg_name in self.preset_messages_dict:
|
|
|
|
ConfirmationDialog(_('Overwrite Status Message?'),
|
2010-11-06 14:01:28 +01:00
|
|
|
_('This name is already used. Do you want to overwrite this '
|
2016-10-05 20:26:25 +02:00
|
|
|
'status message?'), on_response_ok=on_ok2, transient_for=self.dialog)
|
2010-02-10 17:59:17 +01:00
|
|
|
return
|
2017-08-13 13:18:56 +02:00
|
|
|
app.config.add_per('statusmsg', msg_name)
|
2010-02-10 17:59:17 +01:00
|
|
|
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'),
|
2010-11-06 14:01:28 +01:00
|
|
|
_('Please type a name for this status message'), is_modal=False,
|
|
|
|
ok_handler=on_ok)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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'],
|
2010-11-06 14:01:28 +01:00
|
|
|
self.pep_dict['subactivity'], self.pep_dict['activity_text'])
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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'])
|
2009-04-12 20:13:37 +02:00
|
|
|
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
class SubscriptionRequestWindow(Gtk.ApplicationWindow):
|
|
|
|
def __init__(self, jid, text, account, user_nick=None):
|
2018-07-07 00:46:23 +02:00
|
|
|
Gtk.ApplicationWindow.__init__(self)
|
2018-07-16 23:22:33 +02:00
|
|
|
self.set_name('SubscriptionRequest')
|
2018-07-07 00:46:23 +02:00
|
|
|
self.set_application(app.app)
|
|
|
|
self.set_show_menubar(False)
|
|
|
|
self.set_resizable(False)
|
2018-07-16 23:22:33 +02:00
|
|
|
self.set_position(Gtk.WindowPosition.CENTER)
|
|
|
|
self.set_title(_('Subscription Request'))
|
2018-07-07 00:46:23 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
xml = gtkgui_helpers.get_gtk_builder('subscription_request_window.ui')
|
|
|
|
self.add(xml.get_object('subscription_box'))
|
|
|
|
self.jid = jid
|
2010-02-10 17:59:17 +01:00
|
|
|
self.account = account
|
2018-07-16 23:22:33 +02:00
|
|
|
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
|
2018-07-07 00:46:23 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
from_label = xml.get_object('from_label')
|
|
|
|
from_label.set_text(prompt_text)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
textview = xml.get_object('message_textview')
|
|
|
|
textview.get_buffer().set_text(text)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
self.set_default(xml.get_object('authorize_button'))
|
|
|
|
xml.connect_signals(self)
|
2018-07-07 00:46:23 +02:00
|
|
|
self.show_all()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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]
|
2018-07-07 00:46:23 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_close_button_clicked(self, widget):
|
|
|
|
self.destroy()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_authorize_button_clicked(self, widget):
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
2018-07-16 23:22:33 +02:00
|
|
|
Accept the request
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
2018-07-23 23:01:39 +02:00
|
|
|
app.connections[self.account].get_module('Presence').subscribed(self.jid)
|
2018-07-07 00:46:23 +02:00
|
|
|
self.destroy()
|
2018-07-16 23:22:33 +02:00
|
|
|
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)
|
2018-07-07 00:46:23 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_contact_info_activate(self, widget):
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
2018-07-16 23:22:33 +02:00
|
|
|
Ask vcard
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
2018-07-16 23:22:33 +02:00
|
|
|
if self.jid in app.interface.instances[self.account]['infos']:
|
|
|
|
app.interface.instances[self.account]['infos'][self.jid].window.present()
|
2010-12-15 22:08:42 +01:00
|
|
|
else:
|
2018-07-16 23:22:33 +02:00
|
|
|
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)
|
2018-07-07 00:46:23 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_start_chat_activate(self, widget):
|
|
|
|
"""
|
|
|
|
Open chat
|
|
|
|
"""
|
|
|
|
app.interface.new_chat_from_jid(self.account, self.jid)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_deny_button_clicked(self, widget):
|
|
|
|
"""
|
|
|
|
Refuse the request
|
|
|
|
"""
|
2018-07-23 23:01:39 +02:00
|
|
|
app.connections[self.account].get_module('Presence').unsubscribed(self.jid)
|
2018-07-16 23:22:33 +02:00
|
|
|
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)
|
2018-07-07 00:46:23 +02:00
|
|
|
self.destroy()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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')
|
2018-07-07 00:46:23 +02:00
|
|
|
self.account = account
|
2018-07-16 23:22:33 +02:00
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder('synchronise_select_account_dialog.ui')
|
|
|
|
self.dialog = self.xml.get_object('synchronise_select_account_dialog')
|
2018-08-16 20:47:54 +02:00
|
|
|
self.dialog.set_transient_for(app.get_app_window('AccountsWindow'))
|
2018-07-16 23:22:33 +02:00
|
|
|
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)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
self.xml.connect_signals(self)
|
|
|
|
self.init_accounts()
|
|
|
|
self.dialog.show_all()
|
2010-12-15 21:15:00 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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()
|
2010-02-10 17:59:17 +01:00
|
|
|
model.clear()
|
2018-07-16 23:22:33 +02:00
|
|
|
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))
|
2018-07-07 00:46:23 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_cancel_button_clicked(self, widget):
|
|
|
|
self.dialog.destroy()
|
2010-12-15 21:15:00 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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
|
2018-09-18 10:14:04 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
SynchroniseSelectContactsDialog(self.account, remote_account)
|
|
|
|
except GajimGeneralException:
|
|
|
|
# if we showed ErrorDialog, there will not be dialog instance
|
|
|
|
return
|
2018-07-16 23:22:33 +02:00
|
|
|
self.dialog.destroy()
|
2008-04-20 22:51:05 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
@staticmethod
|
|
|
|
def on_destroy(widget):
|
|
|
|
del app.interface.instances['import_contacts']
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
self.xml.connect_signals(self)
|
|
|
|
self.init_contacts()
|
|
|
|
self.dialog.show_all()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def toggled_callback(self, cell, path):
|
|
|
|
model = self.contacts_treeview.get_model()
|
|
|
|
iter_ = model.get_iter(path)
|
|
|
|
model[iter_][0] = not cell.get_active()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_contacts_window_key_press_event(self, widget, event):
|
|
|
|
if event.keyval == Gdk.KEY_Escape:
|
|
|
|
self.window.destroy()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def init_contacts(self):
|
|
|
|
"""
|
|
|
|
Initialize listStore with existing accounts
|
|
|
|
"""
|
|
|
|
model = self.contacts_treeview.get_model()
|
|
|
|
model.clear()
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
# recover local contacts
|
|
|
|
local_jid_list = app.contacts.get_contacts_jid_list(self.local_account)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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)
|
2005-06-07 03:10:24 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
def on_cancel_button_clicked(self, widget):
|
|
|
|
self.dialog.destroy()
|
2006-04-07 16:23:21 +02:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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,
|
2018-09-18 12:06:01 +02:00
|
|
|
self.local_account, groups=remote_contact.groups,
|
|
|
|
nickname=remote_contact.name, auto_auth=True)
|
2018-07-16 23:22:33 +02:00
|
|
|
iter_ = model.iter_next(iter_)
|
|
|
|
self.dialog.destroy()
|
2005-03-02 00:48:05 +01:00
|
|
|
|
2009-11-16 20:56:51 +01:00
|
|
|
#Action that can be done with an incoming list of contacts
|
|
|
|
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
|
2010-04-01 11:40:27 +02:00
|
|
|
'remove': _('remove')}
|
2009-06-21 03:43:57 +02:00
|
|
|
class RosterItemExchangeWindow:
|
2010-02-10 17:59:17 +01:00
|
|
|
"""
|
|
|
|
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
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
# Connect to gtk builder
|
2010-11-06 14:01:28 +01:00
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder(
|
|
|
|
'roster_item_exchange_window.ui')
|
2010-02-08 15:08:40 +01:00
|
|
|
self.window = self.xml.get_object('roster_item_exchange_window')
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# Add Widgets.
|
|
|
|
for widget_to_add in ['accept_button_label', 'type_label',
|
|
|
|
'body_scrolledwindow', 'body_textview', 'items_list_treeview']:
|
2010-02-08 15:08:40 +01:00
|
|
|
self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# Set labels
|
|
|
|
# self.action can be 'add', 'modify' or 'remove'
|
|
|
|
self.type_label.set_label(
|
2010-11-06 14:01:28 +01:00
|
|
|
_('<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]})
|
2010-02-10 17:59:17 +01:00
|
|
|
if message_body:
|
|
|
|
buffer_ = self.body_textview.get_buffer()
|
|
|
|
buffer_.set_text(self.message_body)
|
|
|
|
else:
|
|
|
|
self.body_scrolledwindow.hide()
|
|
|
|
# Treeview
|
2012-12-23 16:23:43 +01:00
|
|
|
model = Gtk.ListStore(bool, str, str, str, str)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.items_list_treeview.set_model(model)
|
|
|
|
# columns
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer1 = Gtk.CellRendererToggle()
|
2010-02-10 17:59:17 +01:00
|
|
|
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,
|
2010-11-06 14:01:28 +01:00
|
|
|
renderer1, active=0)
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer2 = Gtk.CellRendererText()
|
2017-02-04 23:29:45 +01:00
|
|
|
self.items_list_treeview.insert_column_with_attributes(-1, _('JID'),
|
2010-11-06 14:01:28 +01:00
|
|
|
renderer2, text=1)
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer3 = Gtk.CellRendererText()
|
2010-02-10 17:59:17 +01:00
|
|
|
self.items_list_treeview.insert_column_with_attributes(-1, _('Name'),
|
2010-11-06 14:01:28 +01:00
|
|
|
renderer3, text=2)
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer4 = Gtk.CellRendererText()
|
2010-02-10 17:59:17 +01:00
|
|
|
self.items_list_treeview.insert_column_with_attributes(-1, _('Groups'),
|
2010-11-06 14:01:28 +01:00
|
|
|
renderer4, text=3)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# 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
|
2017-08-13 13:18:56 +02:00
|
|
|
contact = app.contacts.get_contact_with_highest_priority(
|
2010-11-06 14:01:28 +01:00
|
|
|
self.account, jid)
|
2013-02-12 19:31:35 +01:00
|
|
|
if not contact or _('Not in Roster') in contact.groups:
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
2017-08-13 13:18:56 +02:00
|
|
|
contact = app.contacts.get_contact_with_highest_priority(
|
2010-11-06 14:01:28 +01:00
|
|
|
self.account, jid)
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
2017-08-13 13:18:56 +02:00
|
|
|
contact = app.contacts.get_contact_with_highest_priority(
|
2010-02-10 17:59:17 +01:00
|
|
|
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()
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.connect_signals(self)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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()
|
2012-12-23 16:23:43 +01:00
|
|
|
iter_ = model.get_iter_first()
|
2010-02-10 17:59:17 +01:00
|
|
|
if self.action == 'add':
|
|
|
|
a = 0
|
|
|
|
while iter_:
|
|
|
|
if model[iter_][0]:
|
2018-09-18 12:06:01 +02:00
|
|
|
a += 1
|
2010-02-10 17:59:17 +01:00
|
|
|
# it is selected
|
2013-01-01 19:44:25 +01:00
|
|
|
#remote_jid = model[iter_][1]
|
2010-02-10 17:59:17 +01:00
|
|
|
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 = []
|
2013-01-01 19:44:25 +01:00
|
|
|
jid = model[iter_][1]
|
2017-08-13 13:18:56 +02:00
|
|
|
if app.jid_is_transport(self.jid_from):
|
2018-07-23 23:01:39 +02:00
|
|
|
con = app.connections[self.account]
|
|
|
|
con.get_module('Presence').automatically_added.append(jid)
|
2017-08-13 13:18:56 +02:00
|
|
|
app.interface.roster.req_sub(self, jid, message,
|
2010-02-10 17:59:17 +01:00
|
|
|
self.account, groups=groups, nickname=model[iter_][2],
|
|
|
|
auto_auth=True)
|
|
|
|
iter_ = model.iter_next(iter_)
|
2012-11-14 17:13:06 +01:00
|
|
|
InformationDialog(i18n.ngettext('Added %d contact',
|
|
|
|
'Added %d contacts', a, a, a))
|
2010-02-10 17:59:17 +01:00
|
|
|
elif self.action == 'modify':
|
|
|
|
a = 0
|
|
|
|
while iter_:
|
|
|
|
if model[iter_][0]:
|
2018-09-18 12:06:01 +02:00
|
|
|
a += 1
|
2010-02-10 17:59:17 +01:00
|
|
|
# it is selected
|
2013-01-01 19:44:25 +01:00
|
|
|
jid = model[iter_][1]
|
2010-02-10 17:59:17 +01:00
|
|
|
# keep same groups and same nickname
|
|
|
|
groups = model[iter_][3].split(', ')
|
|
|
|
if groups == ['']:
|
|
|
|
groups = []
|
2017-08-13 13:18:56 +02:00
|
|
|
for u in app.contacts.get_contact(self.account, jid):
|
2010-02-10 17:59:17 +01:00
|
|
|
u.name = model[iter_][2]
|
2017-08-13 13:18:56 +02:00
|
|
|
app.connections[self.account].update_contact(jid,
|
2010-02-10 17:59:17 +01:00
|
|
|
model[iter_][2], groups)
|
2010-12-03 13:38:18 +01:00
|
|
|
self.draw_contact(jid, self.account)
|
2010-02-10 17:59:17 +01:00
|
|
|
# Update opened chat
|
2017-08-13 13:18:56 +02:00
|
|
|
ctrl = app.interface.msg_win_mgr.get_control(jid, self.account)
|
2010-02-10 17:59:17 +01:00
|
|
|
if ctrl:
|
|
|
|
ctrl.update_ui()
|
2017-08-13 13:18:56 +02:00
|
|
|
win = app.interface.msg_win_mgr.get_window(jid,
|
2010-02-10 17:59:17 +01:00
|
|
|
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]:
|
2018-09-18 12:06:01 +02:00
|
|
|
a += 1
|
2010-02-10 17:59:17 +01:00
|
|
|
# it is selected
|
2013-01-01 19:44:25 +01:00
|
|
|
jid = model[iter_][1]
|
2018-07-23 23:01:39 +02:00
|
|
|
app.connections[self.account].get_module('Presence').unsubscribe(jid)
|
2017-08-13 13:18:56 +02:00
|
|
|
app.interface.roster.remove_contact(jid, self.account)
|
|
|
|
app.contacts.remove_jid(self.account, jid)
|
2010-02-10 17:59:17 +01:00
|
|
|
iter_ = model.iter_next(iter_)
|
2012-11-14 17:13:06 +01:00
|
|
|
InformationDialog(i18n.ngettext('Removed %d contact',
|
|
|
|
'Removed %d contacts', a, a, a))
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.destroy()
|
|
|
|
|
|
|
|
def on_cancel_button_clicked(self, widget):
|
|
|
|
self.window.destroy()
|
2009-07-22 10:58:49 +02:00
|
|
|
|
2009-06-21 03:43:57 +02:00
|
|
|
|
2005-09-11 17:02:22 +02:00
|
|
|
class InvitationReceivedDialog:
|
2012-08-24 09:37:40 +02:00
|
|
|
def __init__(self, account, room_jid, contact_fjid, password=None,
|
|
|
|
comment=None, is_continued=False):
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
self.room_jid = room_jid
|
|
|
|
self.account = account
|
|
|
|
self.password = password
|
|
|
|
self.is_continued = is_continued
|
2012-08-24 09:37:40 +02:00
|
|
|
self.contact_fjid = contact_fjid
|
|
|
|
|
2017-08-13 13:18:56 +02:00
|
|
|
jid = app.get_jid_without_resource(contact_fjid)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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')\
|
2012-08-24 09:37:40 +02:00
|
|
|
% {'room_jid': room_jid}
|
2017-08-13 13:18:56 +02:00
|
|
|
contact = app.contacts.get_first_contact_from_jid(account, jid)
|
2012-08-24 09:50:50 +02:00
|
|
|
contact_text = contact and contact.name or jid
|
2013-09-12 17:41:33 +02:00
|
|
|
sectext = i18n.direction_mark + sectext.replace('$Contact',
|
|
|
|
contact_text)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
if comment: # only if not None and not ''
|
2013-07-28 20:50:30 +02:00
|
|
|
comment = GLib.markup_escape_text(comment)
|
2010-02-10 17:59:17 +01:00
|
|
|
comment = _('Comment: %s') % comment
|
|
|
|
sectext += '\n\n%s' % comment
|
|
|
|
sectext += '\n\n' + _('Do you want to accept the invitation?')
|
|
|
|
|
2012-08-23 21:34:03 +02:00
|
|
|
def on_yes(checked, text):
|
2017-11-25 15:33:54 +01:00
|
|
|
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)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2012-08-23 21:34:03 +02:00
|
|
|
def on_no(text):
|
2018-07-16 23:22:33 +02:00
|
|
|
app.connections[account].get_module('MUC').decline(
|
|
|
|
self.room_jid, self.contact_fjid, text)
|
2012-08-23 21:34:03 +02:00
|
|
|
|
2013-08-10 21:39:35 +02:00
|
|
|
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'))
|
2008-07-31 08:23:03 +02:00
|
|
|
|
2005-12-02 17:18:04 +01:00
|
|
|
class ProgressDialog:
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
|
|
|
"""
|
2010-02-08 15:08:40 +01:00
|
|
|
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')
|
2010-02-10 17:59:17 +01:00
|
|
|
self.label.set_markup('<big>' + during_text + '</big>')
|
2010-02-08 15:08:40 +01:00
|
|
|
self.progressbar = self.xml.get_object('progressbar')
|
2010-02-10 17:59:17 +01:00
|
|
|
self.dialog.set_title(title_text)
|
|
|
|
self.dialog.set_default_size(450, 250)
|
2012-12-23 16:23:43 +01:00
|
|
|
self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.dialog.show_all()
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.connect_signals(self)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2013-07-28 20:50:30 +02:00
|
|
|
self.update_progressbar_timeout_id = GLib.timeout_add(100,
|
2010-11-06 14:01:28 +01:00
|
|
|
self.update_progressbar)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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
|
2005-12-02 17:18:04 +01:00
|
|
|
|
2007-12-12 09:44:46 +01:00
|
|
|
class TransformChatToMUC:
|
2010-02-10 17:59:17 +01:00
|
|
|
# Keep a reference on windows so garbage collector don't restroy them
|
2018-09-21 23:55:57 +02:00
|
|
|
instances = [] # type: List[TransformChatToMUC]
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder('chat_to_muc_window.ui')
|
|
|
|
self.window = self.xml.get_object('chat_to_muc_window')
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
for widget_to_add in ('invite_button', 'cancel_button',
|
2017-10-27 13:38:38 +02:00
|
|
|
'server_list_comboboxentry', 'guests_treeview', 'guests_store',
|
2010-11-06 14:01:28 +01:00
|
|
|
'server_and_guests_hseparator', 'server_select_label'):
|
2010-02-08 15:08:40 +01:00
|
|
|
self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
server_list = []
|
2012-12-23 16:23:43 +01:00
|
|
|
self.servers = Gtk.ListStore(str)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.server_list_comboboxentry.set_model(self.servers)
|
2013-01-02 17:57:20 +01:00
|
|
|
cell = Gtk.CellRendererText()
|
|
|
|
self.server_list_comboboxentry.pack_start(cell, True)
|
|
|
|
self.server_list_comboboxentry.add_attribute(cell, 'text', 0)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# get the muc server of our server
|
2017-08-13 13:18:56 +02:00
|
|
|
if 'jabber' in app.connections[account].muc_jid:
|
|
|
|
server_list.append(app.connections[account].muc_jid['jabber'])
|
2010-02-10 17:59:17 +01:00
|
|
|
# add servers or recently joined groupchats
|
2017-11-25 15:33:54 +01:00
|
|
|
recently_groupchat = app.config.get_per('accounts', account, 'recent_groupchats').split()
|
2010-02-10 17:59:17 +01:00
|
|
|
for g in recently_groupchat:
|
2017-08-13 13:18:56 +02:00
|
|
|
server = app.get_server_from_jid(g)
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
|
|
|
|
2017-10-27 13:38:38 +02:00
|
|
|
self.guests_store.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
2012-12-23 16:23:43 +01:00
|
|
|
self.guests_treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# 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
|
2018-09-18 08:37:18 +02:00
|
|
|
contact.jid != app.get_jid_from_account(account) and
|
2017-08-13 13:18:56 +02:00
|
|
|
contact.jid not in app.interface.minimized_controls[account] and
|
2010-11-06 14:01:28 +01:00
|
|
|
not contact.is_transport() and
|
2013-08-13 13:42:20 +02:00
|
|
|
contact_transport in ('jabber', None))
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
# set jabber id and pseudos
|
2018-09-18 08:37:18 +02:00
|
|
|
for account_ in app.contacts.get_accounts():
|
|
|
|
if app.connections[account_].is_zeroconf:
|
2010-02-10 17:59:17 +01:00
|
|
|
continue
|
2018-09-18 08:37:18 +02:00
|
|
|
for jid in app.contacts.get_jid_list(account_):
|
2017-08-13 13:18:56 +02:00
|
|
|
contact = app.contacts.get_contact_with_highest_priority(
|
2018-09-18 08:37:18 +02:00
|
|
|
account_, jid)
|
2017-08-13 13:18:56 +02:00
|
|
|
contact_transport = app.get_transport_name_from_jid(jid)
|
2010-02-10 17:59:17 +01:00
|
|
|
# Add contact if it can be invited
|
|
|
|
if invitable(contact, contact_transport) and \
|
2013-08-13 13:42:20 +02:00
|
|
|
contact.show not in ('offline', 'error'):
|
2017-10-27 13:38:38 +02:00
|
|
|
icon_name = gtkgui_helpers.get_iconset_name_for(contact.show)
|
2010-02-10 17:59:17 +01:00
|
|
|
name = contact.name
|
|
|
|
if name == '':
|
|
|
|
name = jid.split('@')[0]
|
2017-10-27 13:38:38 +02:00
|
|
|
iter_ = self.guests_store.append([icon_name, name, jid])
|
2010-02-10 17:59:17 +01:00
|
|
|
# preselect treeview rows
|
|
|
|
if self.preselected_jids and jid in self.preselected_jids:
|
2017-10-27 13:38:38 +02:00
|
|
|
path = self.guests_store.get_path(iter_)
|
2010-11-06 14:01:28 +01:00
|
|
|
self.guests_treeview.get_selection().select_path(path)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2017-08-13 13:18:56 +02:00
|
|
|
app.ged.register_event_handler('unique-room-id-supported', ged.GUI1,
|
2010-11-24 17:28:18 +01:00
|
|
|
self._nec_unique_room_id_supported)
|
2017-08-13 13:18:56 +02:00
|
|
|
app.ged.register_event_handler('unique-room-id-not-supported',
|
2010-11-24 17:28:18 +01:00
|
|
|
ged.GUI1, self._nec_unique_room_id_not_supported)
|
|
|
|
|
2010-02-10 17:59:17 +01:00
|
|
|
# show all
|
|
|
|
self.window.show_all()
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.xml.connect_signals(self)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
def on_chat_to_muc_window_destroy(self, widget):
|
2017-08-13 13:18:56 +02:00
|
|
|
app.ged.remove_event_handler('unique-room-id-supported', ged.GUI1,
|
2010-11-24 17:28:18 +01:00
|
|
|
self._nec_unique_room_id_supported)
|
2017-08-13 13:18:56 +02:00
|
|
|
app.ged.remove_event_handler('unique-room-id-not-supported', ged.GUI1,
|
2010-11-24 17:28:18 +01:00
|
|
|
self._nec_unique_room_id_not_supported)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.instances.remove(self)
|
|
|
|
|
|
|
|
def on_chat_to_muc_window_key_press_event(self, widget, event):
|
2012-12-23 16:23:43 +01:00
|
|
|
if event.keyval == Gdk.KEY_Escape: # ESCAPE
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.destroy()
|
|
|
|
|
|
|
|
def on_invite_button_clicked(self, widget):
|
2013-01-02 17:57:20 +01:00
|
|
|
row = self.server_list_comboboxentry.get_child().get_displayed_row()
|
|
|
|
model = self.server_list_comboboxentry.get_model()
|
2013-01-02 19:13:27 +01:00
|
|
|
server = model[row][0].strip()
|
2010-02-10 17:59:17 +01:00
|
|
|
if server == '':
|
|
|
|
return
|
2017-08-13 13:18:56 +02:00
|
|
|
app.connections[self.account].check_unique_room_id_support(server, self)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2010-11-24 17:28:18 +01:00
|
|
|
def _nec_unique_room_id_supported(self, obj):
|
|
|
|
if obj.instance != self:
|
|
|
|
return
|
2010-02-10 17:59:17 +01:00
|
|
|
guest_list = []
|
|
|
|
guests = self.guests_treeview.get_selection().get_selected_rows()
|
|
|
|
for guest in guests[1]:
|
2017-10-27 13:38:38 +02:00
|
|
|
iter_ = self.guests_store.get_iter(guest)
|
|
|
|
guest_list.append(self.guests_store[iter_][2])
|
2010-02-10 17:59:17 +01:00
|
|
|
for guest in self.auto_jids:
|
|
|
|
guest_list.append(guest)
|
2010-11-24 17:28:18 +01:00
|
|
|
room_jid = obj.room_id + '@' + obj.server
|
2017-08-13 13:18:56 +02:00
|
|
|
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)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.window.destroy()
|
|
|
|
|
|
|
|
def on_cancel_button_clicked(self, widget):
|
|
|
|
self.window.destroy()
|
|
|
|
|
2010-11-24 17:28:18 +01:00
|
|
|
def _nec_unique_room_id_not_supported(self, obj):
|
|
|
|
if obj.instance != self:
|
|
|
|
return
|
2017-08-13 13:18:56 +02:00
|
|
|
obj.room_id = app.nicks[self.account].lower().replace(' ', '') + \
|
2010-11-24 17:28:18 +01:00
|
|
|
str(randrange(9999999))
|
|
|
|
self._nec_unique_room_id_supported(obj)
|
2008-02-15 23:55:21 +01:00
|
|
|
|
2018-07-16 23:22:33 +02:00
|
|
|
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:
|
2018-09-17 21:11:45 +02:00
|
|
|
self.add_button(stock, response)
|
2018-07-16 23:22:33 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2008-02-15 23:55:21 +01:00
|
|
|
class DataFormWindow(Dialog):
|
2010-02-10 17:59:17 +01:00
|
|
|
def __init__(self, form, on_response_ok):
|
|
|
|
self.df_response_ok = on_response_ok
|
2012-12-23 16:23:43 +01:00
|
|
|
Dialog.__init__(self, None, 'test', [(Gtk.STOCK_CANCEL,
|
2017-07-16 22:49:34 +02:00
|
|
|
Gtk.ResponseType.CANCEL), (Gtk.STOCK_OK, Gtk.ResponseType.OK)],
|
2010-11-06 14:01:28 +01:00
|
|
|
on_response_ok=self.on_ok)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.set_resizable(True)
|
|
|
|
gtkgui_helpers.resize_window(self, 600, 400)
|
2017-07-16 22:49:34 +02:00
|
|
|
self.dataform_widget = dataforms_widget.DataFormWidget()
|
2018-09-12 00:01:54 +02:00
|
|
|
self.dataform = dataforms.extend_form(node=form)
|
2010-02-10 17:59:17 +01:00
|
|
|
self.dataform_widget.set_sensitive(True)
|
|
|
|
self.dataform_widget.data_form = self.dataform
|
|
|
|
self.dataform_widget.show_all()
|
2017-08-23 19:50:29 +02:00
|
|
|
self.get_content_area().pack_start(self.dataform_widget, True, True, 0)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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()
|
2008-06-29 06:39:29 +02:00
|
|
|
|
2009-10-29 10:19:58 +01:00
|
|
|
|
2009-10-20 11:20:11 +02:00
|
|
|
class ResourceConflictDialog(TimeoutDialog, InputDialog):
|
2010-02-10 17:59:17 +01:00
|
|
|
def __init__(self, title, text, resource, ok_handler):
|
2017-08-23 18:57:38 +02:00
|
|
|
TimeoutDialog.__init__(self, 15)
|
2010-02-10 17:59:17 +01:00
|
|
|
InputDialog.__init__(self, title, text, input_str=resource,
|
|
|
|
is_modal=False, ok_handler=ok_handler)
|
|
|
|
self.title_text = title
|
|
|
|
self.run_timeout()
|
2009-10-20 11:20:11 +02:00
|
|
|
|
2010-02-10 17:59:17 +01:00
|
|
|
def on_timeout(self):
|
|
|
|
self.on_okbutton_clicked(None)
|
2009-10-20 11:20:11 +02:00
|
|
|
|
2009-10-29 10:19:58 +01:00
|
|
|
|
|
|
|
|
2018-09-16 11:56:56 +02:00
|
|
|
class VoIPCallReceivedDialog:
|
2018-09-21 23:55:57 +02:00
|
|
|
instances = {} # type: Dict[Tuple[str, str], VoIPCallReceivedDialog]
|
2010-02-10 17:59:17 +01:00
|
|
|
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
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
xml = gtkgui_helpers.get_gtk_builder('voip_call_received_dialog.ui')
|
|
|
|
xml.connect_signals(self)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
2017-08-13 13:18:56 +02:00
|
|
|
jid = app.get_jid_without_resource(self.fjid)
|
|
|
|
contact = app.contacts.get_first_contact_from_jid(account, jid)
|
2010-02-10 17:59:17 +01:00
|
|
|
if contact and contact.name:
|
|
|
|
self.contact_text = '%s (%s)' % (contact.name, jid)
|
|
|
|
else:
|
|
|
|
self.contact_text = contact_jid
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
self.dialog = xml.get_object('voip_call_received_messagedialog')
|
2010-02-10 17:59:17 +01:00
|
|
|
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)]
|
2018-09-18 10:14:04 +02:00
|
|
|
return None
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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',
|
2010-11-06 14:01:28 +01:00
|
|
|
_('%(contact)s wants to start %(type)s session with you. Do you want '
|
|
|
|
'to answer the call?') % {'contact': self.contact_text,
|
|
|
|
'type': types_text})
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
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()
|
|
|
|
|
2010-03-21 21:45:45 +01:00
|
|
|
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()
|
|
|
|
|
2010-02-10 17:59:17 +01:00
|
|
|
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,
|
2012-12-23 16:23:43 +01:00
|
|
|
Gtk.ResponseType.NO)
|
2010-02-10 17:59:17 +01:00
|
|
|
|
|
|
|
def on_voip_call_received_messagedialog_response(self, dialog, response):
|
|
|
|
# we've got response from user, either stop connecting or accept the call
|
2017-08-13 13:18:56 +02:00
|
|
|
session = app.connections[self.account].get_jingle_session(self.fjid,
|
2010-11-06 14:01:28 +01:00
|
|
|
self.sid)
|
2010-02-10 17:59:17 +01:00
|
|
|
if not session:
|
2012-03-13 14:42:52 +01:00
|
|
|
dialog.destroy()
|
2010-02-10 17:59:17 +01:00
|
|
|
return
|
2012-12-23 16:23:43 +01:00
|
|
|
if response == Gtk.ResponseType.YES:
|
2010-02-10 17:59:17 +01:00
|
|
|
#TODO: Ensure that ctrl.contact.resource == resource
|
2017-08-13 13:18:56 +02:00
|
|
|
jid = app.get_jid_without_resource(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))
|
2010-03-15 21:34:28 +01:00
|
|
|
|
2010-02-10 17:59:17 +01:00
|
|
|
# Chat control opened, update content's status
|
2010-02-08 15:08:40 +01:00
|
|
|
audio = session.get_content('audio')
|
|
|
|
video = session.get_content('video')
|
|
|
|
if audio and not audio.negotiated:
|
2010-02-10 17:59:17 +01:00
|
|
|
ctrl.set_audio_state('connecting', self.sid)
|
2010-02-08 15:08:40 +01:00
|
|
|
if video and not video.negotiated:
|
2013-07-06 21:27:30 +02:00
|
|
|
video_hbox = ctrl.xml.get_object('video_hbox')
|
|
|
|
video_hbox.set_no_show_all(False)
|
2017-08-13 13:18:56 +02:00
|
|
|
if app.config.get('video_see_self'):
|
2013-07-06 21:27:30 +02:00
|
|
|
fixed = ctrl.xml.get_object('outgoing_fixed')
|
|
|
|
fixed.set_no_show_all(False)
|
|
|
|
video_hbox.show_all()
|
2015-12-23 22:40:53 +01:00
|
|
|
ctrl.xml.get_object('incoming_drawingarea').realize()
|
2014-02-28 11:02:26 +01:00
|
|
|
if os.name == 'nt':
|
|
|
|
in_xid = ctrl.xml.get_object('incoming_drawingarea').\
|
|
|
|
get_window().handle
|
|
|
|
else:
|
|
|
|
in_xid = ctrl.xml.get_object('incoming_drawingarea').\
|
2015-12-23 22:40:53 +01:00
|
|
|
get_property('window').get_xid()
|
2013-07-06 21:27:30 +02:00
|
|
|
content = session.get_content('video')
|
|
|
|
# move outgoing stream to chat window
|
2017-08-13 13:18:56 +02:00
|
|
|
if app.config.get('video_see_self'):
|
2015-12-23 22:40:53 +01:00
|
|
|
ctrl.xml.get_object('outgoing_drawingarea').realize()
|
2014-02-28 11:02:26 +01:00
|
|
|
if os.name == 'nt':
|
|
|
|
out_xid = ctrl.xml.get_object('outgoing_drawingarea').\
|
|
|
|
get_window().handle
|
|
|
|
else:
|
|
|
|
out_xid = ctrl.xml.get_object('outgoing_drawingarea').\
|
2015-12-23 22:40:53 +01:00
|
|
|
get_property('window').get_xid()
|
2013-07-31 19:15:42 +02:00
|
|
|
b = content.src_bin
|
2015-12-23 22:40:53 +01:00
|
|
|
for e in b.children:
|
2018-09-18 17:58:39 +02:00
|
|
|
if not e.get_name().startswith('autovideosink'):
|
|
|
|
continue
|
2015-12-23 22:40:53 +01:00
|
|
|
for f in e.children:
|
2013-07-31 19:15:42 +02:00
|
|
|
if f.get_name().startswith('autovideosink'):
|
2015-12-23 22:40:53 +01:00
|
|
|
f.set_window_handle(out_xid)
|
2013-07-31 19:15:42 +02:00
|
|
|
content.out_xid = out_xid
|
2015-12-23 22:40:53 +01:00
|
|
|
break
|
2018-09-18 17:58:39 +02:00
|
|
|
break
|
2013-07-06 21:27:30 +02:00
|
|
|
content.in_xid = in_xid
|
2010-02-10 17:59:17 +01:00
|
|
|
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)
|
2012-12-23 16:23:43 +01:00
|
|
|
else: # response==Gtk.ResponseType.NO
|
2010-02-10 17:59:17 +01:00
|
|
|
if not session.accepted:
|
|
|
|
session.decline_session()
|
|
|
|
else:
|
|
|
|
for content in self.content_types:
|
|
|
|
session.reject_content(content)
|
|
|
|
|
|
|
|
dialog.destroy()
|
2011-04-18 22:36:06 +02:00
|
|
|
|
2017-12-27 13:17:13 +01:00
|
|
|
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':
|
2018-03-25 16:26:29 +02:00
|
|
|
self.label.set_text(_('Requesting HTTP Upload Slot…'))
|
2017-12-27 13:17:13 +01:00
|
|
|
elif obj.status == 'close':
|
|
|
|
self.destroy()
|
|
|
|
elif obj.status == 'upload':
|
2018-03-25 16:26:29 +02:00
|
|
|
self.label.set_text(_('Uploading file via HTTP File Upload…'))
|
2017-12-27 13:17:13 +01:00
|
|
|
elif obj.status == 'update':
|
|
|
|
self.update_progress(obj.seen, obj.total)
|
|
|
|
elif obj.status == 'encrypt':
|
2018-03-25 16:26:29 +02:00
|
|
|
self.label.set_text(_('Encrypting file…'))
|
2017-12-27 13:17:13 +01:00
|
|
|
|
|
|
|
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)) + "%")
|