gajim3/gajim/gtk/groupchat_info.py

287 lines
9.6 KiB
Python

# 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/>.
import time
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Gtk
from nbxmpp.namespaces import Namespace
from gajim.common import app
from gajim.common.i18n import _
from gajim.common.i18n import Q_
from gajim.common.helpers import open_uri
from gajim.common.helpers import get_groupchat_name
from gajim.common.const import RFC5646_LANGUAGE_TAGS
from gajim.common.const import AvatarSize
from .util import get_builder
from .util import make_href_markup
MUC_FEATURES = {
'muc_open': (
'feather-globe-symbolic',
Q_('?Group chat feature:Open'),
_('Anyone can join this group chat')),
'muc_membersonly': (
'feather-user-check-symbolic',
Q_('?Group chat feature:Members Only'),
_('This group chat is restricted '
'to members only')),
'muc_nonanonymous': (
'feather-shield-off-symbolic',
Q_('?Group chat feature:Not Anonymous'),
_('All other group chat participants '
'can see your XMPP address')),
'muc_semianonymous': (
'feather-shield-symbolic',
Q_('?Group chat feature:Semi-Anonymous'),
_('Only moderators can see your XMPP address')),
'muc_moderated': (
'feather-mic-off-symbolic',
Q_('?Group chat feature:Moderated'),
_('Participants entering this group chat need '
'to request permission to send messages')),
'muc_unmoderated': (
'feather-mic-symbolic',
Q_('?Group chat feature:Not Moderated'),
_('Participants entering this group chat are '
'allowed to send messages')),
'muc_public': (
'feather-eye-symbolic',
Q_('?Group chat feature:Public'),
_('Group chat can be found via search')),
'muc_hidden': (
'feather-eye-off-symbolic',
Q_('?Group chat feature:Hidden'),
_('This group chat can not be found via search')),
'muc_passwordprotected': (
'feather-lock-symbolic',
Q_('?Group chat feature:Password Required'),
_('This group chat '
'does require a password upon entry')),
'muc_unsecured': (
'feather-unlock-symbolic',
Q_('?Group chat feature:No Password Required'),
_('This group chat does not require '
'a password upon entry')),
'muc_persistent': (
'feather-hard-drive-symbolic',
Q_('?Group chat feature:Persistent'),
_('This group chat persists '
'even if there are no participants')),
'muc_temporary': (
'feather-clock-symbolic',
Q_('?Group chat feature:Temporary'),
_('This group chat will be destroyed '
'once the last participant left')),
'mam': (
'feather-server-symbolic',
Q_('?Group chat feature:Archiving'),
_('Messages are archived on the server')),
}
class GroupChatInfoScrolled(Gtk.ScrolledWindow):
def __init__(self, account=None, options=None):
Gtk.ScrolledWindow.__init__(self)
if options is None:
options = {}
self._minimal = options.get('minimal', False)
self.set_size_request(options.get('width', 400), -1)
self.set_halign(Gtk.Align.CENTER)
if self._minimal:
self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
else:
self.set_vexpand(True)
self.set_min_content_height(400)
self.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC)
self._account = account
self._info = None
self._ui = get_builder('groupchat_info_scrolled.ui')
self.add(self._ui.info_grid)
self._ui.connect_signals(self)
self.show_all()
def get_account(self):
return self._account
def set_account(self, account):
self._account = account
def get_jid(self):
return self._info.jid
def set_author(self, author, epoch_timestamp=None):
has_author = bool(author)
if has_author and epoch_timestamp is not None:
time_ = time.strftime('%c', time.localtime(epoch_timestamp))
author = f'{author} - {time_}'
self._ui.author.set_text(author or '')
self._ui.author.set_visible(has_author)
self._ui.author_label.set_visible(has_author)
def set_subject(self, subject):
has_subject = bool(subject)
subject = GLib.markup_escape_text(subject or '')
self._ui.subject.set_markup(make_href_markup(subject))
self._ui.subject.set_visible(has_subject)
self._ui.subject_label.set_visible(has_subject)
def set_from_disco_info(self, info):
self._info = info
# Set name
if self._account is None:
name = info.muc_name
else:
con = app.connections[self._account]
name = get_groupchat_name(con, info.jid)
self._ui.name.set_text(name)
self._ui.name.set_visible(True)
# Set avatar
surface = app.interface.avatar_storage.get_muc_surface(
self._account,
str(info.jid),
AvatarSize.GROUP_INFO,
self.get_scale_factor())
self._ui.avatar_image.set_from_surface(surface)
# Set description
has_desc = bool(info.muc_description)
self._ui.description.set_text(info.muc_description or '')
self._ui.description.set_visible(has_desc)
self._ui.description_label.set_visible(has_desc)
# Set address
self._ui.address.set_text(str(info.jid))
if self._minimal:
return
# Set subject
self.set_subject(info.muc_subject)
# Set user
has_users = info.muc_users is not None
self._ui.users.set_text(info.muc_users or '')
self._ui.users.set_visible(has_users)
self._ui.users_image.set_visible(has_users)
# Set contacts
self._ui.contact_box.foreach(self._ui.contact_box.remove)
has_contacts = bool(info.muc_contacts)
if has_contacts:
for contact in info.muc_contacts:
self._ui.contact_box.add(self._get_contact_button(contact))
self._ui.contact_box.set_visible(has_contacts)
self._ui.contact_label.set_visible(has_contacts)
# Set discussion logs
has_log_uri = bool(info.muc_log_uri)
self._ui.logs.set_uri(info.muc_log_uri or '')
self._ui.logs.set_label(_('Website'))
self._ui.logs.set_visible(has_log_uri)
self._ui.logs_label.set_visible(has_log_uri)
# Set room language
has_lang = bool(info.muc_lang)
lang = ''
if has_lang:
lang = RFC5646_LANGUAGE_TAGS.get(info.muc_lang, info.muc_lang)
self._ui.lang.set_text(lang)
self._ui.lang.set_visible(has_lang)
self._ui.lang_image.set_visible(has_lang)
self._add_features(info.features)
def _add_features(self, features):
grid = self._ui.info_grid
for row in range(30, 9, -1):
# Remove everything from row 30 to 10
# We probably will never have 30 rows and
# there is no method to count grid rows
grid.remove_row(row)
features = list(features)
if Namespace.MAM_2 in features:
features.append('mam')
row = 10
for feature in MUC_FEATURES:
if feature in features:
icon, name, tooltip = MUC_FEATURES.get(feature,
(None, None, None))
if icon is None:
continue
grid.attach(self._get_feature_icon(icon, tooltip), 0, row, 1, 1)
grid.attach(self._get_feature_label(name), 1, row, 1, 1)
row += 1
grid.show_all()
def _on_copy_address(self, _button):
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text(f'xmpp:{self._info.jid}?join', -1)
@staticmethod
def _on_activate_log_link(button):
open_uri(button.get_uri())
return Gdk.EVENT_STOP
def _on_activate_contact_link(self, button):
open_uri(f'xmpp:{button.get_uri()}?message', account=self._account)
return Gdk.EVENT_STOP
@staticmethod
def _on_activate_subject_link(_label, uri):
# We have to use this, because the default GTK handler
# is not cross-platform compatible
open_uri(uri)
return Gdk.EVENT_STOP
@staticmethod
def _get_feature_icon(icon, tooltip):
image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
image.set_valign(Gtk.Align.CENTER)
image.set_halign(Gtk.Align.END)
image.set_tooltip_text(tooltip)
return image
@staticmethod
def _get_feature_label(text):
label = Gtk.Label(label=text, use_markup=True)
label.set_halign(Gtk.Align.START)
label.set_valign(Gtk.Align.START)
return label
def _get_contact_button(self, contact):
button = Gtk.LinkButton.new(contact)
button.set_halign(Gtk.Align.START)
button.get_style_context().add_class('link-button')
button.connect('activate-link', self._on_activate_contact_link)
button.show()
return button