Refactor UserNickname and UserMood
- Use IconTheme for mood icons - Simplify modules because nbxmpp handles more stuff
|
@ -47,6 +47,7 @@ from gajim.common.contacts import GC_Contact
|
||||||
from gajim.common.const import AvatarSize
|
from gajim.common.const import AvatarSize
|
||||||
from gajim.common.const import KindConstant
|
from gajim.common.const import KindConstant
|
||||||
from gajim.common.const import Chatstate
|
from gajim.common.const import Chatstate
|
||||||
|
from gajim.common.const import PEPEventType
|
||||||
|
|
||||||
from gajim import gtkgui_helpers
|
from gajim import gtkgui_helpers
|
||||||
from gajim import gui_menu_builder
|
from gajim import gui_menu_builder
|
||||||
|
@ -57,6 +58,8 @@ from gajim.gtk.dialogs import ConfirmationDialog
|
||||||
from gajim.gtk.add_contact import AddNewContactWindow
|
from gajim.gtk.add_contact import AddNewContactWindow
|
||||||
from gajim.gtk.util import get_icon_name
|
from gajim.gtk.util import get_icon_name
|
||||||
from gajim.gtk.util import get_cursor
|
from gajim.gtk.util import get_cursor
|
||||||
|
from gajim.gtk.util import ensure_proper_control
|
||||||
|
from gajim.gtk.util import format_mood
|
||||||
|
|
||||||
from gajim.command_system.implementation.hosts import ChatCommands
|
from gajim.command_system.implementation.hosts import ChatCommands
|
||||||
from gajim.command_system.framework import CommandHost # pylint: disable=unused-import
|
from gajim.command_system.framework import CommandHost # pylint: disable=unused-import
|
||||||
|
@ -128,7 +131,6 @@ class ChatControl(ChatControlBase):
|
||||||
self.update_toolbar()
|
self.update_toolbar()
|
||||||
|
|
||||||
self._pep_images = {}
|
self._pep_images = {}
|
||||||
self._pep_images['mood'] = self.xml.get_object('mood_image')
|
|
||||||
self._pep_images['activity'] = self.xml.get_object('activity_image')
|
self._pep_images['activity'] = self.xml.get_object('activity_image')
|
||||||
self._pep_images['tune'] = self.xml.get_object('tune_image')
|
self._pep_images['tune'] = self.xml.get_object('tune_image')
|
||||||
self._pep_images['geoloc'] = self.xml.get_object('location_image')
|
self._pep_images['geoloc'] = self.xml.get_object('location_image')
|
||||||
|
@ -227,6 +229,10 @@ class ChatControl(ChatControlBase):
|
||||||
|
|
||||||
app.ged.register_event_handler('pep-received', ged.GUI1,
|
app.ged.register_event_handler('pep-received', ged.GUI1,
|
||||||
self._nec_pep_received)
|
self._nec_pep_received)
|
||||||
|
app.ged.register_event_handler('nickname-received', ged.GUI1,
|
||||||
|
self._on_nickname_received)
|
||||||
|
app.ged.register_event_handler('mood-received', ged.GUI1,
|
||||||
|
self._on_mood_received)
|
||||||
if self.TYPE_ID == message_control.TYPE_CHAT:
|
if self.TYPE_ID == message_control.TYPE_CHAT:
|
||||||
# Dont connect this when PrivateChatControl is used
|
# Dont connect this when PrivateChatControl is used
|
||||||
app.ged.register_event_handler('update-roster-avatar', ged.GUI1,
|
app.ged.register_event_handler('update-roster-avatar', ged.GUI1,
|
||||||
|
@ -409,6 +415,7 @@ class ChatControl(ChatControlBase):
|
||||||
def update_all_pep_types(self):
|
def update_all_pep_types(self):
|
||||||
for pep_type in self._pep_images:
|
for pep_type in self._pep_images:
|
||||||
self.update_pep(pep_type)
|
self.update_pep(pep_type)
|
||||||
|
self._update_pep(PEPEventType.MOOD)
|
||||||
|
|
||||||
def update_pep(self, pep_type):
|
def update_pep(self, pep_type):
|
||||||
if isinstance(self.contact, GC_Contact):
|
if isinstance(self.contact, GC_Contact):
|
||||||
|
@ -434,12 +441,36 @@ class ChatControl(ChatControlBase):
|
||||||
if obj.jid != self.contact.jid:
|
if obj.jid != self.contact.jid:
|
||||||
return
|
return
|
||||||
|
|
||||||
if obj.pep_type == 'nick':
|
self.update_pep(obj.pep_type)
|
||||||
self.update_ui()
|
|
||||||
self.parent_win.redraw_tab(self)
|
def _update_pep(self, type_):
|
||||||
self.parent_win.show_title()
|
image = self._get_pep_widget(type_)
|
||||||
else:
|
data = self.contact.pep.get(type_)
|
||||||
self.update_pep(obj.pep_type)
|
if data is None:
|
||||||
|
image.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
if type_ == PEPEventType.MOOD:
|
||||||
|
icon = 'mood-%s' % data.mood
|
||||||
|
formated_text = format_mood(*data)
|
||||||
|
|
||||||
|
image.set_from_icon_name(icon, Gtk.IconSize.MENU)
|
||||||
|
image.set_tooltip_markup(formated_text)
|
||||||
|
image.show()
|
||||||
|
|
||||||
|
def _get_pep_widget(self, type_):
|
||||||
|
if type_ == PEPEventType.MOOD:
|
||||||
|
return self.xml.get_object('mood_image')
|
||||||
|
|
||||||
|
@ensure_proper_control
|
||||||
|
def _on_mood_received(self, _event):
|
||||||
|
self._update_pep(PEPEventType.MOOD)
|
||||||
|
|
||||||
|
@ensure_proper_control
|
||||||
|
def _on_nickname_received(self, _event):
|
||||||
|
self.update_ui()
|
||||||
|
self.parent_win.redraw_tab(self)
|
||||||
|
self.parent_win.show_title()
|
||||||
|
|
||||||
def _update_jingle(self, jingle_type):
|
def _update_jingle(self, jingle_type):
|
||||||
if jingle_type not in ('audio', 'video'):
|
if jingle_type not in ('audio', 'video'):
|
||||||
|
@ -1051,6 +1082,10 @@ class ChatControl(ChatControlBase):
|
||||||
|
|
||||||
app.ged.remove_event_handler('pep-received', ged.GUI1,
|
app.ged.remove_event_handler('pep-received', ged.GUI1,
|
||||||
self._nec_pep_received)
|
self._nec_pep_received)
|
||||||
|
app.ged.remove_event_handler('nickname-received', ged.GUI1,
|
||||||
|
self._on_nickname_received)
|
||||||
|
app.ged.remove_event_handler('mood-received', ged.GUI1,
|
||||||
|
self._on_mood_received)
|
||||||
if self.TYPE_ID == message_control.TYPE_CHAT:
|
if self.TYPE_ID == message_control.TYPE_CHAT:
|
||||||
app.ged.remove_event_handler('update-roster-avatar', ged.GUI1,
|
app.ged.remove_event_handler('update-roster-avatar', ged.GUI1,
|
||||||
self._nec_update_avatar)
|
self._nec_update_avatar)
|
||||||
|
|
|
@ -221,8 +221,6 @@ class ConfigPaths:
|
||||||
'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
|
'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
|
||||||
('MY_ICONSETS',
|
('MY_ICONSETS',
|
||||||
'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
|
'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
|
||||||
('MY_MOOD_ICONSETS',
|
|
||||||
'moods', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
|
|
||||||
('MY_ACTIVITY_ICONSETS',
|
('MY_ACTIVITY_ICONSETS',
|
||||||
'activities', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
|
'activities', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
|
||||||
|
|
||||||
|
|
|
@ -1631,6 +1631,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
|
|
||||||
# Inform GUI we just signed in
|
# Inform GUI we just signed in
|
||||||
app.nec.push_incoming_event(NetworkEvent('signed-in', conn=self))
|
app.nec.push_incoming_event(NetworkEvent('signed-in', conn=self))
|
||||||
|
modules.send_stored_publish(self.name)
|
||||||
self.get_module('PEP').send_stored_publish()
|
self.get_module('PEP').send_stored_publish()
|
||||||
self.continue_connect_info = None
|
self.continue_connect_info = None
|
||||||
|
|
||||||
|
|
|
@ -1098,13 +1098,6 @@ def get_current_show(account):
|
||||||
status = app.connections[account].connected
|
status = app.connections[account].connected
|
||||||
return app.SHOW_LIST[status]
|
return app.SHOW_LIST[status]
|
||||||
|
|
||||||
def get_mood_iconset_path(iconset):
|
|
||||||
if os.path.isdir(os.path.join(configpaths.get('DATA'), 'moods', iconset)):
|
|
||||||
return os.path.join(configpaths.get('DATA'), 'moods', iconset)
|
|
||||||
if os.path.isdir(
|
|
||||||
os.path.join(configpaths.get('MY_MOOD_ICONSETS'), iconset)):
|
|
||||||
return os.path.join(configpaths.get('MY_MOOD_ICONSETS'), iconset)
|
|
||||||
|
|
||||||
def get_activity_iconset_path(iconset):
|
def get_activity_iconset_path(iconset):
|
||||||
if os.path.isdir(os.path.join(configpaths.get('DATA'), 'activities', iconset)):
|
if os.path.isdir(os.path.join(configpaths.get('DATA'), 'activities', iconset)):
|
||||||
return os.path.join(configpaths.get('DATA'), 'activities', iconset)
|
return os.path.join(configpaths.get('DATA'), 'activities', iconset)
|
||||||
|
|
|
@ -34,6 +34,8 @@ ZEROCONF_MODULES = ['iq',
|
||||||
|
|
||||||
_imported_modules = [] # type: List[tuple]
|
_imported_modules = [] # type: List[tuple]
|
||||||
_modules = {} # type: Dict[str, Dict[str, Any]]
|
_modules = {} # type: Dict[str, Dict[str, Any]]
|
||||||
|
_store_publish_modules = [
|
||||||
|
'UserMood'] # type: List[str]
|
||||||
|
|
||||||
for file in Path(__file__).parent.iterdir():
|
for file in Path(__file__).parent.iterdir():
|
||||||
if file.stem == '__init__':
|
if file.stem == '__init__':
|
||||||
|
@ -112,6 +114,11 @@ def unregister_single(con: ConnectionT, name: str) -> None:
|
||||||
del _modules[con.name][name]
|
del _modules[con.name][name]
|
||||||
|
|
||||||
|
|
||||||
|
def send_stored_publish(account: str) -> None:
|
||||||
|
for name in _store_publish_modules:
|
||||||
|
_modules[account][name].send_stored_publish()
|
||||||
|
|
||||||
|
|
||||||
def get(account: str, name: str) -> Any:
|
def get(account: str, name: str) -> Any:
|
||||||
try:
|
try:
|
||||||
return _modules[account][name]
|
return _modules[account][name]
|
||||||
|
|
|
@ -20,6 +20,9 @@ import logging
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
from nbxmpp.structs import StanzaHandler
|
||||||
|
|
||||||
from gajim.common import app
|
from gajim.common import app
|
||||||
|
|
||||||
log = logging.getLogger('gajim.c.m.base')
|
log = logging.getLogger('gajim.c.m.base')
|
||||||
|
@ -34,6 +37,7 @@ class BaseModule:
|
||||||
self._con = con
|
self._con = con
|
||||||
self._account = con.name
|
self._account = con.name
|
||||||
self._nbxmpp_callbacks = {} # type: Dict[str, Any]
|
self._nbxmpp_callbacks = {} # type: Dict[str, Any]
|
||||||
|
self._stored_publish = None # type: Callable
|
||||||
self.handlers = [] # type: List[str]
|
self.handlers = [] # type: List[str]
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
|
@ -46,8 +50,10 @@ class BaseModule:
|
||||||
|
|
||||||
module = self._con.connection.get_module(self._nbxmpp_extends)
|
module = self._con.connection.get_module(self._nbxmpp_extends)
|
||||||
|
|
||||||
return partial(getattr(module, key),
|
callback = self._nbxmpp_callbacks.get(key)
|
||||||
callback=self._nbxmpp_callbacks.get(key))
|
if callback is None:
|
||||||
|
return getattr(module, key)
|
||||||
|
return partial(getattr(module, key), callback=callback)
|
||||||
|
|
||||||
def _nbxmpp(self, module_name=None):
|
def _nbxmpp(self, module_name=None):
|
||||||
if not app.account_is_connected(self._account):
|
if not app.account_is_connected(self._account):
|
||||||
|
@ -61,3 +67,16 @@ class BaseModule:
|
||||||
|
|
||||||
def _register_callback(self, method, callback):
|
def _register_callback(self, method, callback):
|
||||||
self._nbxmpp_callbacks[method] = callback
|
self._nbxmpp_callbacks[method] = callback
|
||||||
|
|
||||||
|
def _register_pubsub_handler(self, callback):
|
||||||
|
handler = StanzaHandler(name='message',
|
||||||
|
callback=callback,
|
||||||
|
ns=nbxmpp.NS_PUBSUB_EVENT,
|
||||||
|
priority=49)
|
||||||
|
self.handlers.append(handler)
|
||||||
|
|
||||||
|
def send_stored_publish(self):
|
||||||
|
if self._stored_publish is None:
|
||||||
|
return
|
||||||
|
log.info('Send stored publish')
|
||||||
|
self._stored_publish()
|
||||||
|
|
|
@ -55,12 +55,11 @@ class Message:
|
||||||
]
|
]
|
||||||
|
|
||||||
# XEPs for which this message module should not be executed
|
# XEPs for which this message module should not be executed
|
||||||
self._message_namespaces = set([nbxmpp.NS_PUBSUB_EVENT,
|
self._message_namespaces = set([nbxmpp.NS_ROSTERX,
|
||||||
nbxmpp.NS_ROSTERX,
|
|
||||||
nbxmpp.NS_IBB])
|
nbxmpp.NS_IBB])
|
||||||
|
|
||||||
def _message_received(self, _con, stanza, properties):
|
def _message_received(self, _con, stanza, properties):
|
||||||
if properties.is_mam_message:
|
if properties.is_mam_message or properties.is_pubsub_event:
|
||||||
return
|
return
|
||||||
# Check if a child of the message contains any
|
# Check if a child of the message contains any
|
||||||
# namespaces that we handle in other modules.
|
# namespaces that we handle in other modules.
|
||||||
|
|
|
@ -6,94 +6,69 @@
|
||||||
#
|
#
|
||||||
# Gajim is distributed in the hope that it will be useful,
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# XEP-0107: User Mood
|
# XEP-0107: User Mood
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
|
||||||
from typing import List # pylint: disable=unused-import
|
|
||||||
from typing import Optional
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
from gi.repository import GLib
|
|
||||||
|
|
||||||
from gajim.common.const import PEPEventType, MOODS
|
from gajim.common import app
|
||||||
from gajim.common.exceptions import StanzaMalformed
|
from gajim.common.nec import NetworkEvent
|
||||||
from gajim.common.modules.pep import AbstractPEPModule, AbstractPEPData
|
from gajim.common.modules.base import BaseModule
|
||||||
|
from gajim.common.modules.util import event_node
|
||||||
|
from gajim.common.modules.util import store_publish
|
||||||
|
from gajim.common.const import PEPEventType
|
||||||
|
|
||||||
log = logging.getLogger('gajim.c.m.user_mood')
|
log = logging.getLogger('gajim.c.m.user_mood')
|
||||||
|
|
||||||
|
|
||||||
class UserMoodData(AbstractPEPData):
|
class UserMood(BaseModule):
|
||||||
|
|
||||||
type_ = PEPEventType.MOOD
|
_nbxmpp_extends = 'Mood'
|
||||||
|
_nbxmpp_methods = [
|
||||||
|
'set_mood',
|
||||||
|
]
|
||||||
|
|
||||||
def as_markup_text(self) -> str:
|
def __init__(self, con):
|
||||||
if self.data is None:
|
BaseModule.__init__(self, con)
|
||||||
return ''
|
self._register_pubsub_handler(self._mood_received)
|
||||||
mood = self._translate_mood(self.data['mood'])
|
|
||||||
markuptext = '<b>%s</b>' % GLib.markup_escape_text(mood)
|
|
||||||
if 'text' in self.data:
|
|
||||||
text = self.data['text']
|
|
||||||
markuptext += ' (%s)' % GLib.markup_escape_text(text)
|
|
||||||
return markuptext
|
|
||||||
|
|
||||||
@staticmethod
|
@event_node(nbxmpp.NS_MOOD)
|
||||||
def _translate_mood(mood: str) -> str:
|
def _mood_received(self, _con, _stanza, properties):
|
||||||
if mood in MOODS:
|
data = properties.pubsub_event.data
|
||||||
return MOODS[mood]
|
for contact in app.contacts.get_contacts(self._account,
|
||||||
return mood
|
str(properties.jid)):
|
||||||
|
if data.mood is not None:
|
||||||
|
contact.pep[PEPEventType.MOOD] = data
|
||||||
class UserMood(AbstractPEPModule):
|
|
||||||
|
|
||||||
name = 'mood'
|
|
||||||
namespace = nbxmpp.NS_MOOD
|
|
||||||
pep_class = UserMoodData
|
|
||||||
store_publish = True
|
|
||||||
_log = log
|
|
||||||
|
|
||||||
def _extract_info(self, item: nbxmpp.Node) -> Optional[Dict[str, str]]:
|
|
||||||
mood_dict = {}
|
|
||||||
mood_tag = item.getTag('mood', namespace=nbxmpp.NS_MOOD)
|
|
||||||
if mood_tag is None:
|
|
||||||
raise StanzaMalformed('No mood node')
|
|
||||||
|
|
||||||
if not mood_tag.getChildren():
|
|
||||||
return None
|
|
||||||
|
|
||||||
for child in mood_tag.getChildren():
|
|
||||||
name = child.getName().strip()
|
|
||||||
if name == 'text':
|
|
||||||
mood_dict['text'] = child.getData()
|
|
||||||
else:
|
else:
|
||||||
mood_dict['mood'] = name
|
contact.pep.pop(PEPEventType.MOOD, None)
|
||||||
|
|
||||||
if 'mood' not in mood_dict:
|
if properties.is_self_message:
|
||||||
raise StanzaMalformed('No mood value found')
|
if data.mood is not None:
|
||||||
return mood_dict
|
self._con.pep[PEPEventType.MOOD] = data
|
||||||
|
else:
|
||||||
|
self._con.pep.pop(PEPEventType.MOOD, None)
|
||||||
|
|
||||||
def _build_node(self, data: Optional[Tuple[str, str]]) -> nbxmpp.Node:
|
app.nec.push_incoming_event(
|
||||||
item = nbxmpp.Node('mood', {'xmlns': nbxmpp.NS_MOOD})
|
NetworkEvent('mood-received',
|
||||||
if data is None:
|
account=self._account,
|
||||||
return item
|
jid=properties.jid.getBare(),
|
||||||
|
mood=data,
|
||||||
|
is_self_message=properties.is_self_message))
|
||||||
|
|
||||||
mood, text = data
|
@store_publish
|
||||||
if not mood:
|
def set_mood(self, mood):
|
||||||
return item
|
log.info('Send %s', mood)
|
||||||
item.addChild(mood)
|
self._nbxmpp('Mood').set_mood(mood)
|
||||||
|
|
||||||
if text:
|
|
||||||
item.addChild('text', payload=text)
|
|
||||||
return item
|
|
||||||
|
|
||||||
|
|
||||||
def get_instance(*args: Any, **kwargs: Any) -> Tuple[UserMood, str]:
|
def get_instance(*args: Any, **kwargs: Any) -> Tuple[UserMood, str]:
|
||||||
|
|
|
@ -6,17 +6,15 @@
|
||||||
#
|
#
|
||||||
# Gajim is distributed in the hope that it will be useful,
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# XEP-0172: User Nickname
|
# XEP-0172: User Nickname
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import List # pylint: disable=unused-import
|
|
||||||
from typing import Optional
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -24,56 +22,41 @@ import logging
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
|
|
||||||
from gajim.common import app
|
from gajim.common import app
|
||||||
from gajim.common.const import PEPEventType
|
from gajim.common.nec import NetworkEvent
|
||||||
from gajim.common.exceptions import StanzaMalformed
|
from gajim.common.modules.base import BaseModule
|
||||||
from gajim.common.modules.pep import AbstractPEPModule, AbstractPEPData
|
from gajim.common.modules.util import event_node
|
||||||
|
|
||||||
log = logging.getLogger('gajim.c.m.user_nickname')
|
log = logging.getLogger('gajim.c.m.user_nickname')
|
||||||
|
|
||||||
|
|
||||||
class UserNicknameData(AbstractPEPData):
|
class UserNickname(BaseModule):
|
||||||
|
|
||||||
type_ = PEPEventType.NICKNAME
|
_nbxmpp_extends = 'Nickname'
|
||||||
|
_nbxmpp_methods = [
|
||||||
|
'set_nickname',
|
||||||
|
]
|
||||||
|
|
||||||
def get_nick(self) -> str:
|
def __init__(self, con):
|
||||||
return self.data or ''
|
BaseModule.__init__(self, con)
|
||||||
|
self._register_pubsub_handler(self._nickname_received)
|
||||||
|
|
||||||
|
@event_node(nbxmpp.NS_NICK)
|
||||||
|
def _nickname_received(self, _con, _stanza, properties):
|
||||||
|
nick = properties.pubsub_event.data
|
||||||
|
if properties.self_message:
|
||||||
|
if nick is None:
|
||||||
|
nick = app.config.get_per('accounts', self._account, 'name')
|
||||||
|
app.nicks[self._account] = nick
|
||||||
|
|
||||||
class UserNickname(AbstractPEPModule):
|
for contact in app.contacts.get_contacts(self._account,
|
||||||
|
str(properties.jid)):
|
||||||
|
contact.contact_name = nick
|
||||||
|
|
||||||
name = 'nick'
|
app.nec.push_incoming_event(
|
||||||
namespace = nbxmpp.NS_NICK
|
NetworkEvent('nickname-received',
|
||||||
pep_class = UserNicknameData
|
account=self._account,
|
||||||
store_publish = True
|
jid=properties.jid.getBare(),
|
||||||
_log = log
|
nickname=nick))
|
||||||
|
|
||||||
def _extract_info(self, item: nbxmpp.Node) -> Optional[str]:
|
|
||||||
nick = ''
|
|
||||||
child = item.getTag('nick', namespace=nbxmpp.NS_NICK)
|
|
||||||
if child is None:
|
|
||||||
raise StanzaMalformed('No nick node')
|
|
||||||
nick = child.getData()
|
|
||||||
|
|
||||||
return nick or None
|
|
||||||
|
|
||||||
def _build_node(self, data: Optional[str]) -> Optional[nbxmpp.Node]:
|
|
||||||
item = nbxmpp.Node('nick', {'xmlns': nbxmpp.NS_NICK})
|
|
||||||
if data is not None:
|
|
||||||
item.addData(data)
|
|
||||||
return item
|
|
||||||
|
|
||||||
def _notification_received(self,
|
|
||||||
jid: nbxmpp.JID,
|
|
||||||
user_pep: UserNicknameData) -> None:
|
|
||||||
for contact in app.contacts.get_contacts(self._account, str(jid)):
|
|
||||||
contact.contact_name = user_pep.get_nick()
|
|
||||||
|
|
||||||
if jid == self._con.get_own_jid().getStripped():
|
|
||||||
if user_pep:
|
|
||||||
app.nicks[self._account] = user_pep.get_nick()
|
|
||||||
else:
|
|
||||||
app.nicks[self._account] = app.config.get_per(
|
|
||||||
'accounts', self._account, 'name')
|
|
||||||
|
|
||||||
|
|
||||||
def parse_nickname(stanza: nbxmpp.Node) -> str:
|
def parse_nickname(stanza: nbxmpp.Node) -> str:
|
||||||
|
|
|
@ -16,6 +16,11 @@
|
||||||
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
|
|
||||||
|
|
||||||
def from_xs_boolean(value: Union[str, bool]) -> bool:
|
def from_xs_boolean(value: Union[str, bool]) -> bool:
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
|
@ -44,3 +49,25 @@ def to_xs_boolean(value: Union[bool, None]) -> str:
|
||||||
|
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Cant convert %s to xs:boolean' % value)
|
'Cant convert %s to xs:boolean' % value)
|
||||||
|
|
||||||
|
|
||||||
|
def event_node(node):
|
||||||
|
def event_node_decorator(func):
|
||||||
|
@wraps(func)
|
||||||
|
def func_wrapper(self, _con, _stanza, properties):
|
||||||
|
if properties.pubsub_event.node != node:
|
||||||
|
return
|
||||||
|
func(self, _con, _stanza, properties)
|
||||||
|
|
||||||
|
return func_wrapper
|
||||||
|
return event_node_decorator
|
||||||
|
|
||||||
|
|
||||||
|
def store_publish(func):
|
||||||
|
@wraps(func)
|
||||||
|
def func_wrapper(self, *args, **kwargs):
|
||||||
|
if not app.account_is_connected(self._account):
|
||||||
|
self._stored_publish = partial(func, self, *args, **kwargs)
|
||||||
|
return
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
return func_wrapper
|
||||||
|
|
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 984 B |
Before Width: | Height: | Size: 966 B After Width: | Height: | Size: 966 B |
Before Width: | Height: | Size: 989 B After Width: | Height: | Size: 989 B |
Before Width: | Height: | Size: 943 B After Width: | Height: | Size: 943 B |
Before Width: | Height: | Size: 924 B After Width: | Height: | Size: 924 B |
Before Width: | Height: | Size: 949 B After Width: | Height: | Size: 949 B |
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 942 B After Width: | Height: | Size: 942 B |
Before Width: | Height: | Size: 934 B After Width: | Height: | Size: 934 B |
Before Width: | Height: | Size: 931 B After Width: | Height: | Size: 931 B |
Before Width: | Height: | Size: 918 B After Width: | Height: | Size: 918 B |
Before Width: | Height: | Size: 922 B After Width: | Height: | Size: 922 B |
Before Width: | Height: | Size: 996 B After Width: | Height: | Size: 996 B |
Before Width: | Height: | Size: 913 B After Width: | Height: | Size: 913 B |
Before Width: | Height: | Size: 951 B After Width: | Height: | Size: 951 B |
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 907 B |
Before Width: | Height: | Size: 886 B After Width: | Height: | Size: 886 B |
Before Width: | Height: | Size: 939 B After Width: | Height: | Size: 939 B |
Before Width: | Height: | Size: 981 B After Width: | Height: | Size: 981 B |
Before Width: | Height: | Size: 916 B After Width: | Height: | Size: 916 B |
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 944 B |
Before Width: | Height: | Size: 886 B After Width: | Height: | Size: 886 B |
Before Width: | Height: | Size: 1,001 B After Width: | Height: | Size: 1,001 B |
Before Width: | Height: | Size: 918 B After Width: | Height: | Size: 918 B |
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 964 B After Width: | Height: | Size: 964 B |
Before Width: | Height: | Size: 871 B After Width: | Height: | Size: 871 B |
Before Width: | Height: | Size: 902 B After Width: | Height: | Size: 902 B |
Before Width: | Height: | Size: 960 B After Width: | Height: | Size: 960 B |
Before Width: | Height: | Size: 949 B After Width: | Height: | Size: 949 B |
Before Width: | Height: | Size: 906 B After Width: | Height: | Size: 906 B |
Before Width: | Height: | Size: 975 B After Width: | Height: | Size: 975 B |
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 923 B |
Before Width: | Height: | Size: 928 B After Width: | Height: | Size: 928 B |
Before Width: | Height: | Size: 897 B After Width: | Height: | Size: 897 B |
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 923 B |
Before Width: | Height: | Size: 880 B After Width: | Height: | Size: 880 B |
Before Width: | Height: | Size: 883 B After Width: | Height: | Size: 883 B |
Before Width: | Height: | Size: 982 B After Width: | Height: | Size: 982 B |
Before Width: | Height: | Size: 925 B After Width: | Height: | Size: 925 B |
Before Width: | Height: | Size: 924 B After Width: | Height: | Size: 924 B |
Before Width: | Height: | Size: 855 B After Width: | Height: | Size: 855 B |
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 954 B |
Before Width: | Height: | Size: 972 B After Width: | Height: | Size: 972 B |
Before Width: | Height: | Size: 979 B After Width: | Height: | Size: 979 B |
Before Width: | Height: | Size: 979 B After Width: | Height: | Size: 979 B |
Before Width: | Height: | Size: 922 B After Width: | Height: | Size: 922 B |
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 986 B |
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 923 B |
Before Width: | Height: | Size: 914 B After Width: | Height: | Size: 914 B |
Before Width: | Height: | Size: 940 B After Width: | Height: | Size: 940 B |
Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 888 B |
Before Width: | Height: | Size: 889 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 892 B |
Before Width: | Height: | Size: 963 B After Width: | Height: | Size: 963 B |
Before Width: | Height: | Size: 903 B After Width: | Height: | Size: 903 B |
Before Width: | Height: | Size: 914 B After Width: | Height: | Size: 914 B |
Before Width: | Height: | Size: 987 B After Width: | Height: | Size: 987 B |
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 947 B |
Before Width: | Height: | Size: 914 B After Width: | Height: | Size: 914 B |
Before Width: | Height: | Size: 949 B After Width: | Height: | Size: 949 B |
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 969 B |
Before Width: | Height: | Size: 935 B After Width: | Height: | Size: 935 B |
Before Width: | Height: | Size: 966 B After Width: | Height: | Size: 966 B |
Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 978 B |
Before Width: | Height: | Size: 963 B After Width: | Height: | Size: 963 B |
Before Width: | Height: | Size: 931 B After Width: | Height: | Size: 931 B |
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
Before Width: | Height: | Size: 1,002 B After Width: | Height: | Size: 1,002 B |
Before Width: | Height: | Size: 966 B After Width: | Height: | Size: 966 B |
Before Width: | Height: | Size: 1,023 B After Width: | Height: | Size: 1,023 B |
Before Width: | Height: | Size: 910 B After Width: | Height: | Size: 910 B |
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 947 B |
Before Width: | Height: | Size: 998 B After Width: | Height: | Size: 998 B |
Before Width: | Height: | Size: 961 B After Width: | Height: | Size: 961 B |
Before Width: | Height: | Size: 975 B After Width: | Height: | Size: 975 B |
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 892 B |
Before Width: | Height: | Size: 967 B After Width: | Height: | Size: 967 B |
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 959 B |
Before Width: | Height: | Size: 899 B After Width: | Height: | Size: 899 B |
Before Width: | Height: | Size: 720 B After Width: | Height: | Size: 720 B |
Before Width: | Height: | Size: 851 B After Width: | Height: | Size: 851 B |
Before Width: | Height: | Size: 992 B After Width: | Height: | Size: 992 B |
|
@ -504,10 +504,12 @@ class ChangeMoodDialog:
|
||||||
self.MOODS.sort()
|
self.MOODS.sort()
|
||||||
|
|
||||||
for mood in self.MOODS:
|
for mood in self.MOODS:
|
||||||
|
image = Gtk.Image.new_from_icon_name(
|
||||||
|
'mood-%s' % mood, Gtk.IconSize.MENU)
|
||||||
self.mood_buttons[mood] = Gtk.RadioButton()
|
self.mood_buttons[mood] = Gtk.RadioButton()
|
||||||
self.mood_buttons[mood].join_group(no_mood_button)
|
self.mood_buttons[mood].join_group(no_mood_button)
|
||||||
self.mood_buttons[mood].set_mode(False)
|
self.mood_buttons[mood].set_mode(False)
|
||||||
self.mood_buttons[mood].add(gtkgui_helpers.load_mood_icon(mood))
|
self.mood_buttons[mood].add(image)
|
||||||
self.mood_buttons[mood].set_relief(Gtk.ReliefStyle.NONE)
|
self.mood_buttons[mood].set_relief(Gtk.ReliefStyle.NONE)
|
||||||
self.mood_buttons[mood].set_tooltip_text(MOODS[mood])
|
self.mood_buttons[mood].set_tooltip_text(MOODS[mood])
|
||||||
self.mood_buttons[mood].connect('clicked',
|
self.mood_buttons[mood].connect('clicked',
|
||||||
|
@ -700,8 +702,8 @@ class ChangeStatusMessageDialog(TimeoutDialog):
|
||||||
img = self.xml.get_object('mood_image')
|
img = self.xml.get_object('mood_image')
|
||||||
label = self.xml.get_object('mood_button_label')
|
label = self.xml.get_object('mood_button_label')
|
||||||
if 'mood' in self.pep_dict and self.pep_dict['mood'] in MOODS:
|
if 'mood' in self.pep_dict and self.pep_dict['mood'] in MOODS:
|
||||||
img.set_from_pixbuf(gtkgui_helpers.load_mood_icon(
|
img.set_from_icon_name('mood-%s' % self.pep_dict['mood'],
|
||||||
self.pep_dict['mood']).get_pixbuf())
|
Gtk.IconSize.MENU)
|
||||||
if self.pep_dict['mood_text']:
|
if self.pep_dict['mood_text']:
|
||||||
label.set_text(self.pep_dict['mood_text'])
|
label.set_text(self.pep_dict['mood_text'])
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -325,7 +325,7 @@ class ProfileWindow(Gtk.ApplicationWindow):
|
||||||
return
|
return
|
||||||
vcard_, sha = self.make_vcard()
|
vcard_, sha = self.make_vcard()
|
||||||
nick = vcard_.get('NICKNAME') or None
|
nick = vcard_.get('NICKNAME') or None
|
||||||
app.connections[self.account].get_module('UserNickname').send(nick)
|
app.connections[self.account].get_module('UserNickname').set_nickname(nick)
|
||||||
if not nick:
|
if not nick:
|
||||||
nick = app.config.get_per('accounts', self.account, 'name')
|
nick = app.config.get_per('accounts', self.account, 'name')
|
||||||
app.nicks[self.account] = nick
|
app.nicks[self.account] = nick
|
||||||
|
|
|
@ -36,11 +36,13 @@ from gi.repository import Pango
|
||||||
from gajim.common import app
|
from gajim.common import app
|
||||||
from gajim.common import helpers
|
from gajim.common import helpers
|
||||||
from gajim.common.const import AvatarSize
|
from gajim.common.const import AvatarSize
|
||||||
|
from gajim.common.const import PEPEventType
|
||||||
from gajim.common.i18n import Q_
|
from gajim.common.i18n import Q_
|
||||||
from gajim.common.i18n import _
|
from gajim.common.i18n import _
|
||||||
|
|
||||||
from gajim.gtk.util import get_builder
|
from gajim.gtk.util import get_builder
|
||||||
from gajim.gtk.util import get_icon_name
|
from gajim.gtk.util import get_icon_name
|
||||||
|
from gajim.gtk.util import format_mood
|
||||||
|
|
||||||
log = logging.getLogger('gajim.gtk.tooltips')
|
log = logging.getLogger('gajim.gtk.tooltips')
|
||||||
|
|
||||||
|
@ -471,8 +473,8 @@ class RosterTooltip(StatusTable):
|
||||||
Append Tune, Mood, Activity, Location information of the specified contact
|
Append Tune, Mood, Activity, Location information of the specified contact
|
||||||
to the given property list.
|
to the given property list.
|
||||||
"""
|
"""
|
||||||
if 'mood' in contact.pep:
|
if PEPEventType.MOOD in contact.pep:
|
||||||
mood = contact.pep['mood'].as_markup_text()
|
mood = format_mood(*contact.pep[PEPEventType.MOOD])
|
||||||
self._ui.mood.set_markup(mood)
|
self._ui.mood.set_markup(mood)
|
||||||
self._ui.mood.show()
|
self._ui.mood.show()
|
||||||
self._ui.mood_label.show()
|
self._ui.mood_label.show()
|
||||||
|
|
|
@ -36,6 +36,7 @@ from gajim.common import app
|
||||||
from gajim.common import configpaths
|
from gajim.common import configpaths
|
||||||
from gajim.common import i18n
|
from gajim.common import i18n
|
||||||
from gajim.common.i18n import _
|
from gajim.common.i18n import _
|
||||||
|
from gajim.common.const import MOODS
|
||||||
|
|
||||||
from gajim.gtk.const import GajimIconSet
|
from gajim.gtk.const import GajimIconSet
|
||||||
|
|
||||||
|
@ -499,3 +500,24 @@ def ensure_not_destroyed(func):
|
||||||
return
|
return
|
||||||
return func(self, *args, **kwargs)
|
return func(self, *args, **kwargs)
|
||||||
return func_wrapper
|
return func_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_proper_control(func):
|
||||||
|
@wraps(func)
|
||||||
|
def func_wrapper(self, event):
|
||||||
|
if event.account != self.account:
|
||||||
|
return
|
||||||
|
if event.jid != self.contact.jid:
|
||||||
|
return
|
||||||
|
return func(self, event)
|
||||||
|
return func_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def format_mood(mood, text):
|
||||||
|
if mood is None:
|
||||||
|
return ''
|
||||||
|
mood = MOODS[mood]
|
||||||
|
markuptext = '<b>%s</b>' % GLib.markup_escape_text(mood)
|
||||||
|
if text is not None:
|
||||||
|
markuptext += ' (%s)' % GLib.markup_escape_text(text)
|
||||||
|
return markuptext
|
||||||
|
|
|
@ -267,15 +267,6 @@ def create_list_multi(value_list, selected_values=None):
|
||||||
treeview.show_all()
|
treeview.show_all()
|
||||||
return treeview
|
return treeview
|
||||||
|
|
||||||
def load_mood_icon(icon_name):
|
|
||||||
"""
|
|
||||||
Load an icon from the mood iconset in 16x16
|
|
||||||
"""
|
|
||||||
iconset = app.config.get('mood_iconset')
|
|
||||||
path = os.path.join(helpers.get_mood_iconset_path(iconset), '')
|
|
||||||
icon_list = _load_icon_list([icon_name], path)
|
|
||||||
return icon_list[icon_name]
|
|
||||||
|
|
||||||
def load_activity_icon(category, activity=None):
|
def load_activity_icon(category, activity=None):
|
||||||
"""
|
"""
|
||||||
Load an icon from the activity iconset in 16x16
|
Load an icon from the activity iconset in 16x16
|
||||||
|
@ -289,12 +280,6 @@ def load_activity_icon(category, activity=None):
|
||||||
return icon_list[activity]
|
return icon_list[activity]
|
||||||
|
|
||||||
def get_pep_icon(pep_class):
|
def get_pep_icon(pep_class):
|
||||||
if pep_class == PEPEventType.MOOD:
|
|
||||||
received_mood = pep_class.data['mood']
|
|
||||||
mood = received_mood if received_mood in MOODS else 'unknown'
|
|
||||||
pixbuf = load_mood_icon(mood).get_pixbuf()
|
|
||||||
return pixbuf
|
|
||||||
|
|
||||||
if pep_class == PEPEventType.TUNE:
|
if pep_class == PEPEventType.TUNE:
|
||||||
return 'audio-x-generic'
|
return 'audio-x-generic'
|
||||||
|
|
||||||
|
|