Commit 0ad369dc authored by Philipp Hörist's avatar Philipp Hörist

Refactor MUC Presence

- Use nbxmpp properties
- Split into multiple events
- Add new options in the group chat menu for print settings
parent ed4a81fe
Pipeline #2892 failed with stages
in 3 minutes and 24 seconds
......@@ -1182,12 +1182,8 @@ class ChatControl(ChatControlBase):
return
scale = self.parent_win.window.get_scale_factor()
if self.TYPE_ID == message_control.TYPE_CHAT:
surface = app.contacts.get_avatar(
self.account, self.contact.jid, AvatarSize.CHAT, scale)
else:
surface = app.interface.get_avatar(
self.gc_contact.avatar_sha, AvatarSize.CHAT, scale)
surface = app.contacts.get_avatar(
self.account, self.contact.jid, AvatarSize.CHAT, scale)
image = self.xml.get_object('avatar_image')
if surface is None:
......
......@@ -512,10 +512,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
super(ChatControlBase, self).shutdown()
# PluginSystem: removing GUI extension points connected with ChatControlBase
# instance object
app.plugin_manager.remove_gui_extension_point('chat_control_base',
self)
app.plugin_manager.remove_gui_extension_point('chat_control_base', self)
app.plugin_manager.remove_gui_extension_point(
'chat_control_base_draw_banner', self)
app.plugin_manager.remove_gui_extension_point(
'chat_control_base_update_toolbar', self)
app.ged.remove_event_handler('our-show', ged.GUI1,
self._nec_our_status)
app.ged.remove_event_handler('sec-catalog-received', ged.GUI1,
......@@ -1049,8 +1050,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
gc_contact.room_jid, self.account)
self_contact = app.contacts.get_gc_contact(self.account,
gc_control.room_jid, gc_control.nick)
if gc_control.is_anonymous and gc_contact.affiliation not in ['admin',
'owner'] and self_contact.affiliation in ['admin', 'owner']:
if gc_control.is_anonymous and gc_contact.affiliation.value not in ['admin',
'owner'] and self_contact.affiliation.value in ['admin', 'owner']:
contact = app.contacts.get_contact(self.account, gc_contact.jid)
if not contact or contact.sub not in ('both', 'to'):
prim_text = _('Really send file?')
......
......@@ -379,8 +379,8 @@ class StandardGroupChatCommands(CommandContainer):
for nick in nicks:
contact = get_contact(nick)
role = helpers.get_uf_role(contact.role)
affiliation = helpers.get_uf_affiliation(contact.affiliation)
role = helpers.get_uf_role(contact.role.value)
affiliation = helpers.get_uf_affiliation(contact.affiliation.value)
self.echo("%s - %s - %s" % (nick, role, affiliation))
@command('ignore', raw=True)
......
......@@ -35,6 +35,7 @@ import logging
log = logging.getLogger('gajim.c.caps_cache')
import nbxmpp
from nbxmpp.const import Affiliation
from nbxmpp import (NS_XHTML_IM, NS_ESESSION, NS_CHATSTATES,
NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO,
NS_JINGLE_FILE_TRANSFER_5)
......@@ -485,7 +486,7 @@ class MucCapsCache:
def is_subject_change_allowed(self, jid, affiliation):
allowed = True
if affiliation in ('owner', 'admin'):
if affiliation in (Affiliation.OWNER, Affiliation.ADMIN):
return allowed
if jid in self.cache:
......
......@@ -235,7 +235,6 @@ class Config:
'show_location_in_roster': [opt_bool, True, '', True],
'avatar_position_in_roster': [opt_str, 'right', _('Define the position of the avatar in roster. Can be left or right'), True],
'print_status_in_chats': [opt_bool, False, _('If False, Gajim will no longer print status line in chats when a contact changes their status and/or their status message.')],
'print_status_in_muc': [opt_str, 'none', _('Can be "none", "all" or "in_and_out". If "none", Gajim will no longer print status line in groupchats when a member changes their status and/or their status message. If "all" Gajim will print all status messages. If "in_and_out", Gajim will only print FOO enters/leaves group chat.')],
'log_contact_status_changes': [opt_bool, False],
'log_xhtml_messages': [opt_bool, False, _('Log XHTML messages instead of plain text messages.')],
'restored_messages_small': [opt_bool, True, _('If true, restored messages will use a smaller font than the default one.')],
......@@ -440,6 +439,8 @@ class Config:
'muc_restore_lines': [opt_int, -2, _('How many lines to request from server when entering a groupchat. -1 means no limit, -2 means global value')],
'muc_restore_timeout': [opt_int, -2, _('Minutes of backlog to request when entering a groupchat. -1 means no limit, -2 means global value')],
'notify_on_all_messages': [opt_bool, False, _('State whether a notification is created for every message in this room')],
'print_status': [opt_bool, False, _('Show a status message for all status (away, dnd, etc.) changes of users in a group chat')],
'print_join_left': [opt_bool, False, _('Show a status message for every join or leave in a group chat')],
}, {}),
'plugins': ({
'active': [opt_bool, False, _('State whether plugins should be activated on startup (this is saved on Gajim exit). This option SHOULD NOT be used to (de)activate plug-ins. Use GUI instead.')],
......
......@@ -209,8 +209,6 @@ PresenceHelperEvent):
jid_list = app.contacts.get_jid_list(self.conn.name)
self.timestamp = None
self.get_id()
self.is_gc = False # is it a GC presence ?
sig_tag = None
self.avatar_sha = None
# XEP-0172 User Nickname
self.user_nick = self.stanza.getTagData('nick') or ''
......@@ -225,13 +223,7 @@ PresenceHelperEvent):
# XEP-0319
self.idle_time = parse_idle(self.stanza)
xtags = self.stanza.getTags('x')
for x in xtags:
namespace = x.getNamespace()
if namespace in (nbxmpp.NS_MUC_USER, nbxmpp.NS_MUC):
self.is_gc = True
elif namespace == nbxmpp.NS_SIGNED:
sig_tag = x
sig_tag = self.stanza.getTag('x', namespace=nbxmpp.NS_SIGNED)
self.status = self.stanza.getStatus() or ''
self._generate_show()
......@@ -241,13 +233,6 @@ PresenceHelperEvent):
self.errcode = self.stanza.getErrorCode()
self.errmsg = self.stanza.getErrorMsg()
if self.is_gc:
app.nec.push_incoming_event(
GcPresenceReceivedEvent(
None, conn=self.conn, stanza=self.stanza,
presence_obj=self))
return
if self.ptype == 'error':
return
......@@ -278,7 +263,6 @@ class ZeroconfPresenceReceivedEvent(nec.NetworkIncomingEvent):
self.ptype = 'unavailable'
else:
self.ptype = None
self.is_gc = False
self.user_nick = ''
self.transport_auto_auth = False
self.errcode = None
......@@ -286,86 +270,6 @@ class ZeroconfPresenceReceivedEvent(nec.NetworkIncomingEvent):
self.popup = False # Do we want to open chat window ?
return True
class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'gc-presence-received'
def generate(self):
self.ptype = self.presence_obj.ptype
self.fjid = self.presence_obj.fjid
self.jid = self.presence_obj.jid
self.room_jid = self.presence_obj.jid
self.nick = self.presence_obj.resource
self.show = self.presence_obj.show
self.status = self.presence_obj.status
self.avatar_sha = self.presence_obj.avatar_sha
self.errcode = self.presence_obj.errcode
self.errmsg = self.presence_obj.errmsg
self.errcon = self.stanza.getError()
self.get_gc_control()
self.gc_contact = app.contacts.get_gc_contact(self.conn.name,
self.room_jid, self.nick)
if self.ptype == 'error':
return True
if self.ptype and self.ptype != 'unavailable':
return
if app.config.get('log_contact_status_changes') and \
app.config.should_log(self.conn.name, self.room_jid):
if self.gc_contact:
jid = self.gc_contact.jid
else:
jid = self.stanza.getJid()
st = self.status
if jid:
# we know real jid, save it in db
st += ' (%s)' % jid
show = app.logger.convert_show_values_to_db_api_values(self.show)
if show is not None:
fjid = nbxmpp.JID(self.fjid)
app.logger.insert_into_logs(self.conn.name,
fjid.getStripped(),
time_time(),
KindConstant.GCSTATUS,
contact_name=fjid.getResource(),
message=st,
show=show)
# NOTE: if it's a gc presence, don't ask vcard here.
# We may ask it to real jid in gui part.
self.status_code = []
ns_muc_user_x = self.stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER)
if ns_muc_user_x:
destroy = ns_muc_user_x.getTag('destroy')
else:
destroy = None
if ns_muc_user_x and destroy:
# Room has been destroyed. see
# http://www.xmpp.org/extensions/xep-0045.html#destroyroom
self.reason = _('Room has been destroyed')
r = destroy.getTagData('reason')
if r:
self.reason += ' (%s)' % r
if destroy.getAttr('jid'):
try:
jid = helpers.parse_jid(destroy.getAttr('jid'))
self.reason += '\n' + \
_('You can join this room instead: %s') % jid
except helpers.InvalidFormat:
pass
self.status_code = ['destroyed']
else:
self.reason = self.stanza.getReason()
self.status_code = self.stanza.getStatusCode()
self.role = self.stanza.getRole()
self.affiliation = self.stanza.getAffiliation()
self.real_jid = self.stanza.getJid()
self.actor = self.stanza.getActor()
self.new_nick = self.stanza.getNewNick()
return True
class OurShowEvent(nec.NetworkIncomingEvent):
name = 'our-show'
......
......@@ -46,12 +46,13 @@ class XMPPEntity:
class CommonContact(XMPPEntity):
def __init__(self, jid, account, resource, show, status, name,
def __init__(self, jid, account, resource, show, presence, status, name,
chatstate, client_caps=None):
XMPPEntity.__init__(self, jid, account, resource)
self.show = show
self._show = show
self._presence = presence
self.status = status
self.name = name
......@@ -68,10 +69,22 @@ class CommonContact(XMPPEntity):
@show.setter
def show(self, value):
if not isinstance(value, str):
raise TypeError('show must be a string')
if isinstance(value, str) and isinstance(self, GC_Contact):
breakpoint()
self._show = value
@property
def presence(self):
return self._presence
@presence.setter
def presence(self, value):
self._presence = value
@property
def is_available(self):
return self._presence.is_available
@property
def chatstate_enum(self):
return self._chatstate
......@@ -130,7 +143,8 @@ class Contact(CommonContact):
if groups is None:
groups = []
CommonContact.__init__(self, jid, account, resource, show, status, name,
CommonContact.__init__(self, jid, account, resource, show,
None, status, name,
chatstate, client_caps=client_caps)
self.contact_name = '' # nick choosen by contact
......@@ -199,6 +213,14 @@ class Contact(CommonContact):
def is_groupchat(self):
return self._is_groupchat
@property
def is_connected(self):
from gajim.common import app
try:
return app.gc_connected[self.account.name][self.jid]
except Exception as error:
return False
def is_transport(self):
# if not '@' or '@' starts the jid then contact is transport
return self.jid.find('@') <= 0
......@@ -209,11 +231,12 @@ class GC_Contact(CommonContact):
Information concerning each groupchat contact
"""
def __init__(self, room_jid, account, name='', show='', status='', role='',
affiliation='', jid='', resource='', chatstate=None, avatar_sha=None):
def __init__(self, room_jid, account, name='', show='', presence=None,
status='', role='', affiliation='', jid='', resource='',
chatstate=None, avatar_sha=None):
CommonContact.__init__(self, jid, account, resource, show, status, name,
chatstate)
CommonContact.__init__(self, jid, account, resource, show,
presence, status, name, chatstate)
self.room_jid = room_jid
self.role = role
......@@ -443,11 +466,12 @@ class LegacyContactsAPI:
return getattr(self._metacontact_manager, attr_name)
raise AttributeError(attr_name)
def create_gc_contact(self, room_jid, account, name='', show='', status='',
role='', affiliation='', jid='', resource='', avatar_sha=None):
def create_gc_contact(self, room_jid, account, name='', show='',
presence=None, status='', role='',
affiliation='', jid='', resource='', avatar_sha=None):
account = self._accounts.get(account, account) # Use Account object if available
return GC_Contact(room_jid, account, name, show, status, role, affiliation, jid,
resource, avatar_sha=avatar_sha)
return GC_Contact(room_jid, account, name, show, presence, status,
role, affiliation, jid, resource, avatar_sha=avatar_sha)
def add_gc_contact(self, account, gc_contact):
return self._accounts[account].gc_contacts.add_gc_contact(gc_contact)
......@@ -464,6 +488,9 @@ class LegacyContactsAPI:
def get_nick_list(self, account, room_jid):
return self._accounts[account].gc_contacts.get_nick_list(room_jid)
def get_gc_contact_list(self, account, room_jid):
return self._accounts[account].gc_contacts.get_gc_contact_list(room_jid)
def get_gc_contact(self, account, room_jid, nick):
return self._accounts[account].gc_contacts.get_gc_contact(room_jid, nick)
......@@ -682,6 +709,12 @@ class GC_Contacts():
return []
return list(self._rooms[room_jid].keys())
def get_gc_contact_list(self, room_jid):
try:
return list(self._rooms[room_jid].values())
except Exception:
return []
def get_gc_contact(self, room_jid, nick):
try:
return self._rooms[room_jid][nick]
......
......@@ -220,8 +220,9 @@ class Chatstate:
'chatstate': str(State.ACTIVE)}
if contact.is_groupchat():
app.nec.push_outgoing_event(
GcMessageOutgoingEvent(None, **event_attrs))
if contact.is_connected:
app.nec.push_outgoing_event(
GcMessageOutgoingEvent(None, **event_attrs))
else:
app.nec.push_outgoing_event(
MessageOutgoingEvent(None, **event_attrs))
......@@ -265,8 +266,9 @@ class Chatstate:
'chatstate': str(state)}
if contact.is_groupchat():
app.nec.push_outgoing_event(
GcMessageOutgoingEvent(None, **event_attrs))
if contact.is_connected:
app.nec.push_outgoing_event(
GcMessageOutgoingEvent(None, **event_attrs))
else:
app.nec.push_outgoing_event(
MessageOutgoingEvent(None, **event_attrs))
......
......@@ -20,11 +20,14 @@ import logging
import nbxmpp
from nbxmpp.const import InviteType
from nbxmpp.const import PresenceType
from nbxmpp.structs import StanzaHandler
from gajim.common import i18n
from gajim.common import app
from gajim.common import helpers
from gajim.common.const import KindConstant
from gajim.common.helpers import AdditionalDataDict
from gajim.common.caps_cache import muc_caps_cache
from gajim.common.nec import NetworkEvent
from gajim.common.modules.bits_of_binary import store_bob_data
......@@ -38,6 +41,14 @@ class MUC:
self._account = con.name
self.handlers = [
StanzaHandler(name='presence',
callback=self._on_muc_user_presence,
ns=nbxmpp.NS_MUC_USER,
priority=49),
StanzaHandler(name='presence',
callback=self._on_muc_presence,
ns=nbxmpp.NS_MUC,
priority=49),
StanzaHandler(name='message',
callback=self._on_subject_change,
typ='groupchat',
......@@ -79,13 +90,14 @@ class MUC:
]
def __getattr__(self, key):
if key in self._nbmxpp_methods:
if not app.account_is_connected(self._account):
log.warning('Account %s not connected, cant use %s',
self._account, key)
return
module = self._con.connection.get_module('MUC')
return getattr(module, key)
if key not in self._nbmxpp_methods:
raise AttributeError
if not app.account_is_connected(self._account):
log.warning('Account %s not connected, cant use %s',
self._account, key)
return
module = self._con.connection.get_module('MUC')
return getattr(module, key)
def pass_disco(self, from_, identities, features, _data, _node):
for identity in identities:
......@@ -149,6 +161,163 @@ class MUC:
if tags:
muc_x.setTag('history', tags)
def _on_muc_presence(self, _con, _stanza, properties):
if properties.type == PresenceType.ERROR:
self._raise_muc_event('muc-presence-error', properties)
raise nbxmpp.NodeProcessed
def _on_muc_user_presence(self, _con, _stanza, properties):
if properties.type == PresenceType.ERROR:
return
if properties.is_muc_destroyed:
for contact in app.contacts.get_gc_contact_list(
self._account, properties.jid.getBare()):
contact.presence = PresenceType.UNAVAILABLE
log.info('MUC destroyed: %s', properties.jid.getBare())
self._raise_muc_event('muc-destroyed', properties)
raise nbxmpp.NodeProcessed
contact = app.contacts.get_gc_contact(self._account,
properties.jid.getBare(),
properties.muc_nickname)
if properties.is_nickname_changed:
app.contacts.remove_gc_contact(self._account, contact)
contact.name = properties.muc_user.nick
app.contacts.add_gc_contact(self._account, contact)
log.info('Nickname changed: %s to %s',
properties.jid,
properties.muc_user.nick)
self._raise_muc_event('muc-nickname-changed', properties)
raise nbxmpp.NodeProcessed
if contact is None and properties.type.is_available:
self._add_new_muc_contact(properties)
if properties.is_muc_self_presence:
log.info('Self presence: %s', properties.jid)
self._raise_muc_event('muc-self-presence', properties)
else:
log.info('User joined: %s', properties.jid)
self._raise_muc_event('muc-user-joined', properties)
raise nbxmpp.NodeProcessed
if properties.is_muc_self_presence and properties.is_kicked:
self._raise_muc_event('muc-self-kicked', properties)
raise nbxmpp.NodeProcessed
if properties.is_muc_self_presence and properties.type.is_unavailable:
# Its not a kick, so this is the reflection of our own
# unavailable presence, because we left the MUC
raise nbxmpp.NodeProcessed
if properties.type.is_unavailable:
for _event in app.events.get_events(self._account,
jid=str(properties.jid),
types=['pm']):
contact.show = properties.show
contact.presence = properties.type
contact.status = properties.status
contact.affiliation = properties.affiliation
app.interface.handle_event(self._account,
str(properties.jid),
'pm')
# Handle only the first pm event, the rest will be
# handled by the opened ChatControl
break
# We remove the contact from the MUC, but there could be
# a PrivateChatControl open, so we update the contacts presence
contact.presence = properties.type
app.contacts.remove_gc_contact(self._account, contact)
log.info('User %s left', properties.jid)
self._raise_muc_event('muc-user-left', properties)
raise nbxmpp.NodeProcessed
if contact.affiliation != properties.affiliation:
contact.affiliation = properties.affiliation
log.info('Affiliation changed: %s %s',
properties.jid,
properties.affiliation)
self._raise_muc_event('muc-user-affiliation-changed', properties)
if contact.role != properties.role:
contact.role = properties.role
log.info('Role changed: %s %s',
properties.jid,
properties.role)
self._raise_muc_event('muc-user-role-changed', properties)
if (contact.status != properties.status or
contact.show != properties.show):
contact.status = properties.status
contact.show = properties.show
log.info('Show/Status changed: %s %s %s',
properties.jid,
properties.status,
properties.show)
self._raise_muc_event('muc-user-status-show-changed', properties)
raise nbxmpp.NodeProcessed
def _raise_muc_event(self, event_name, properties):
app.nec.push_incoming_event(
NetworkEvent(event_name,
account=self._account,
room_jid=properties.jid.getBare(),
properties=properties))
self._log_muc_event(event_name, properties)
def _log_muc_event(self, event_name, properties):
if event_name not in ['muc-user-joined',
'muc-user-left',
'muc-user-status-show-changed']:
return
if (not app.config.get('log_contact_status_changes') or
not app.config.should_log(self._account, properties.jid)):
return
additional_data = AdditionalDataDict()
if properties.muc_user is not None:
if properties.muc_user.jid is not None:
additional_data.set_value(
'gajim', 'real_jid', str(properties.muc_user.jid))
# TODO: Refactor
if properties.type == PresenceType.UNAVAILABLE:
show = 'offline'
else:
show = properties.show.value
show = app.logger.convert_show_values_to_db_api_values(show)
app.logger.insert_into_logs(
self._account,
properties.jid.getBare(),
properties.timestamp,
KindConstant.GCSTATUS,
contact_name=properties.muc_nickname,
message=properties.status or None,
show=show,
additional_data=additional_data)
def _add_new_muc_contact(self, properties):
real_jid = None
if properties.muc_user.jid is not None:
real_jid = str(properties.muc_user.jid)
contact = app.contacts.create_gc_contact(
room_jid=properties.jid.getBare(),
account=self._account,
name=properties.muc_nickname,
show=properties.show,
status=properties.status,
presence=properties.type,
role=properties.role,
affiliation=properties.affiliation,
jid=real_jid,
avatar_sha=properties.avatar_sha)
app.contacts.add_gc_contact(self._account, contact)
def _on_subject_change(self, _con, _stanza, properties):
if not properties.is_muc_subject:
return
......
......@@ -702,42 +702,10 @@
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">These status messages are displayed when a room occupant changes status (this includes entering/leaving the room)</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Displa_y status messages in group chats</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">print_status_in_muc_combobox</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
<placeholder/>
</child>
<child>
<object class="GtkComboBox" id="print_status_in_muc_combobox">
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="model">print_status_in_muc_liststore</property>
<signal name="changed" handler="print_status_in_muc_combobox_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
<placeholder/>
</child>
</object>
<packing>
......
This diff is collapsed.
......@@ -147,15 +147,6 @@ class Preferences(Gtk.ApplicationWindow):
st = app.config.get('show_subject_on_join')
self._ui.subject_on_join_checkbutton.set_active(st)
# Print status in MUC
st = app.config.get('print_status_in_muc')
if st == 'none':
self._ui.print_status_in_muc_combobox.set_active(0)
elif st == 'all':
self._ui.print_status_in_muc_combobox.set_active(1)
else: # in_and_out
self._ui.print_status_in_muc_combobox.set_active(2)
# Displayed chat state notifications
st = app.config.get('show_chatstate_in_tabs')
self._ui.show_chatstate_in_tabs.set_active(st)
......@@ -604,15 +595,6 @@ class Preferences(Gtk.ApplicationWindow):
def on_subject_on_join_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'show_subject_on_join')
def print_status_in_muc_combobox_changed(self, widget):
active = widget.get_active()
if active == 0: # none
app.config.set('print_status_in_muc', 'none')
elif active == 1: # all
app.config.set('print_status_in_muc', 'all')
else: # in_and_out
app.config.set('print_status_in_muc', 'in_and_out')
def on_show_chatstate_in_tabs_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'show_chatstate_in_tabs')
......
......@@ -203,18 +203,18 @@ class GCTooltip():
self._ui.status.show()
# Status
show = helpers.get_uf_show(contact.show)
show = helpers.get_uf_show(contact.show.value)
self._ui.user_show.set_markup(colorize_status(show))
self._ui.user_show.show()
# JID
if contact.jid.strip():
self._ui.jid.set_text(contact.jid)
if contact.jid is not None:
self._ui.jid.set_text(str(contact.jid))
self._ui.jid.show()
# Affiliation
if contact.affiliation != 'none':
uf_affiliation = helpers.get_uf_affiliation(contact.affiliation)
if not contact.affiliation.is_none:
uf_affiliation = helpers.get_uf_affiliation(contact.affiliation.value)
uf_affiliation = \
_('%(owner_or_admin_or_member)s of this group chat') \
% {'owner_or_admin_or_member': uf_affiliation}
......
......@@ -329,57 +329,6 @@ class Interface:
cancel_handler=on_cancel)
gc_control.error_dialog.input_entry.set_visibility(False)
def handle_event_gc_presence(self, obj):
gc_control = obj.gc_control
parent_win = None
if gc_control and gc_control.parent_win: