Daniel Brötzmann pushed to branch mainwindow at gajim / gajim
Commits: 97a5b223 by wurstsalat at 2021-11-25T21:00:17+01:00 Use Direction Enum for selection next chat
- - - - - 6282556e by wurstsalat at 2021-11-25T23:27:48+01:00 BaseControl: Typing
- - - - - cf5bf121 by wurstsalat at 2021-11-25T23:28:01+01:00 ChatControl: Typing
- - - - - d7d25700 by wurstsalat at 2021-11-25T23:28:19+01:00 PrivateChatControl: Typring
- - - - - 0615dbff by wurstsalat at 2021-11-25T23:28:45+01:00 GroupchatControl: Typing
- - - - -
7 changed files:
- gajim/common/const.py - gajim/gtk/chat_list.py - gajim/gtk/controls/base.py - gajim/gtk/controls/chat.py - gajim/gtk/controls/groupchat.py - gajim/gtk/controls/private.py - gajim/gtk/main.py
Changes:
===================================== gajim/common/const.py ===================================== @@ -186,6 +186,11 @@ class Trust(IntEnum): VERIFIED = 3
+class Direction(IntEnum): + NEXT = 0 + PREV = 1 + + class Display(Enum): X11 = 'X11Display' WAYLAND = 'GdkWaylandDisplay'
===================================== gajim/gtk/chat_list.py ===================================== @@ -30,6 +30,7 @@ from gajim.common import app from gajim.common import ged from gajim.common.const import AvatarSize +from gajim.common.const import Direction from gajim.common.const import KindConstant from gajim.common.const import RowHeaderType from gajim.common.i18n import _ @@ -224,10 +225,9 @@ def select_chat(self, account: str, jid: JID) -> None: row = self._chats[(account, jid)] self.select_row(row)
- def select_next_chat(self, forwards: bool, + def select_next_chat(self, direction: Direction, unread_first: bool = False) -> None: # Selects the next chat, but prioritizes chats with unread messages. - # Direction is forwards (True) or backwards (False) row = self.get_selected_chat() if row is None: row = self.get_row_at_index(0) @@ -243,7 +243,7 @@ def select_next_chat(self, forwards: bool,
# Loop until finding a chat with unread count or completing a cycle while True: - if forwards: + if direction == Direction.NEXT: index += 1 if index >= len(self.get_children()): index = 0 @@ -266,12 +266,12 @@ def select_next_chat(self, forwards: bool, return
index = row.get_index() - if forwards: + if direction == Direction.NEXT: next_row = self.get_row_at_index(index + 1) else: next_row = self.get_row_at_index(index - 1) if next_row is None: - if forwards: + if direction == Direction.NEXT: next_row = self.get_row_at_index(0) else: last = len(self.get_children()) - 1
===================================== gajim/gtk/controls/base.py ===================================== @@ -23,6 +23,9 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from typing import Any +from typing import Dict +from typing import List from typing import Optional
import os @@ -31,13 +34,16 @@ import uuid import tempfile
-from nbxmpp.const import Chatstate - from gi.repository import Gtk from gi.repository import Gdk +from gi.repository import GdkPixbuf from gi.repository import GLib from gi.repository import Gio
+from nbxmpp import JID +from nbxmpp.const import Chatstate +from nbxmpp.modules.security_labels import Displaymarking + from gajim.common import app from gajim.common import helpers from gajim.common import ged @@ -48,6 +54,7 @@ from gajim.common.helpers import AdditionalDataDict from gajim.common.helpers import get_retraction_text from gajim.common.const import KindConstant +from gajim.common.modules.httpupload import HTTPFileTransfer from gajim.common.preview_helpers import filename_from_uri from gajim.common.preview_helpers import guess_simple_file_type from gajim.common.structs import OutgoingMessage @@ -103,13 +110,13 @@ class BaseControl(ChatCommandProcessor, CommandTools, EventHelper):
_type = None # type: ControlType
- def __init__(self, widget_name, account, jid): + def __init__(self, widget_name: str, account: str, jid: JID) -> None: EventHelper.__init__(self) # Undo needs this variable to know if space has been pressed. # Initialize it to True so empty textview is saved in undo list - self.space_pressed = True + self.space_pressed: bool = True
- self.handlers = {} + self.handlers: Dict[str, Any] = {}
self.account = account
@@ -122,7 +129,7 @@ def __init__(self, widget_name, account, jid):
# control_id is a unique id for the control, # its used as action name for actions that belong to a control - self.control_id = str(uuid.uuid4()) + self.control_id: str = str(uuid.uuid4()) self.session = None
self.xml = get_builder(f'{widget_name}.ui') @@ -131,8 +138,8 @@ def __init__(self, widget_name, account, jid):
self._account_badge = AccountBadge(self.account) self.xml.account_badge_box.add(self._account_badge) - show = len(app.settings.get_active_accounts()) > 1 - self.xml.account_badge_box.set_visible(show) + show_account_badge = len(app.settings.get_active_accounts()) > 1 + self.xml.account_badge_box.set_visible(show_account_badge)
# Drag and drop self.xml.overlay.add_overlay(self.xml.drop_area) @@ -214,30 +221,32 @@ def __init__(self, widget_name, account, jid): self.xml.hbox.pack_start(self.msg_scrolledwindow, True, True, 0)
# the following vars are used to keep history of user's messages - self.sent_history = [] - self.sent_history_pos = 0 - self.received_history = [] - self.received_history_pos = 0 - self.orig_msg = None + self.sent_history: List[str] = [] + self.sent_history_pos: int = 0 + self.received_history: List[str] = [] + self.received_history_pos: int = 0 + self.orig_msg: Optional[str] = None
- # For XEP-0333 - self.last_msg_id = None + # XEP-0333 Chat Markers + self.last_msg_id: Optional[str] = None
- self.correcting = False - self.last_sent_msg = None + # XEP-0308 Message Correction + self.correcting: bool = False + self.last_sent_msg: Optional[str] = None
self.set_emoticon_popover()
# Attach speller self.set_speller()
- # For XEP-0172 + # XEP-0172 User Nickname + # TODO: self.user_nick = None
- self.command_hits = [] - self.last_key_tabs = False + self.command_hits: List[str] = [] + self.last_key_tabs: bool = False
- self.sendmessage = True + self.sendmessage: bool = True
self._client.get_module('Chatstate').set_active(self.contact)
@@ -263,11 +272,17 @@ def __init__(self, widget_name, account, jid): def _connect_contact_signals(self): raise NotImplementedError
- def _process_jingle_av_event(self): + def _process_jingle_av_event(self, *args): raise NotImplementedError
- def _get_action(self, name): - return app.window.lookup_action(name + self.control_id) + def get_our_nick(self): + raise NotImplementedError + + def room_name(self): + raise NotImplementedError + + def _get_action(self, name: str) -> Gio.Action: + return app.window.lookup_action(f'{name}{self.control_id}')
def process_event(self, event): if event.account != self.account: @@ -341,22 +356,22 @@ def _on_conversation_view_key_press(self, _listbox, event): return Gdk.EVENT_STOP
@property - def type(self): + def type(self) -> ControlType: return self._type
@property - def is_chat(self): + def is_chat(self) -> bool: return self._type.is_chat
@property - def is_privatechat(self): + def is_privatechat(self) -> bool: return self._type.is_privatechat
@property - def is_groupchat(self): + def is_groupchat(self) -> bool: return self._type.is_groupchat
- def safe_shutdown(self): + def safe_shutdown(self) -> bool: """ Called to check if control can be closed without losing data. returns True if control can be closed safely else False @@ -378,7 +393,7 @@ def allow_shutdown(self, method, on_response_yes, on_response_no): def focus(self): raise NotImplementedError
- def draw_banner(self): + def draw_banner(self) -> None: """ Draw the fat line at the top of the window that houses the icon, jid, etc @@ -387,7 +402,7 @@ def draw_banner(self): """ self.draw_banner_text()
- def update_toolbar(self): + def update_toolbar(self) -> None: """ update state of buttons in toolbar """ @@ -395,18 +410,18 @@ def update_toolbar(self): app.plugin_manager.gui_extension_point( 'chat_control_base_update_toolbar', self)
- def draw_banner_text(self): + def draw_banner_text(self) -> None: """ Derived types SHOULD implement this """
- def update_ui(self): + def update_ui(self) -> None: """ Derived types SHOULD implement this """ self.draw_banner()
- def repaint_themed_widgets(self): + def repaint_themed_widgets(self) -> None: """ Derived types MAY implement this """ @@ -453,7 +468,7 @@ def remove_session(self, session): def _nec_ping(self, obj): raise NotImplementedError
- def setup_seclabel(self): + def setup_seclabel(self) -> None: self.xml.label_selector.hide() self.xml.label_selector.set_no_show_all(True) lb = Gtk.ListStore(str) @@ -502,7 +517,7 @@ def delegate_action(self, action): return Gdk.EVENT_STOP
if action == 'delete-line': - self.clear(self.msg_textview) + self.msg_textview.clear() return Gdk.EVENT_STOP
if action == 'show-emoji-chooser': @@ -514,7 +529,7 @@ def delegate_action(self, action):
return Gdk.EVENT_PROPAGATE
- def add_actions(self): + def add_actions(self) -> None: action = Gio.SimpleAction.new_stateful( f'set-encryption-{self.control_id}', GLib.VariantType.new('s'), @@ -535,7 +550,7 @@ def add_actions(self): action.set_enabled(False) app.window.add_action(action)
- def remove_actions(self): + def remove_actions(self) -> None: actions = [ 'send-message-', 'set-encryption-', @@ -547,7 +562,7 @@ def remove_actions(self): for action in actions: app.window.remove_action(f'{action}{self.control_id}')
- def mark_as_read(self, send_marker=True): + def mark_as_read(self, send_marker: bool = True) -> None: self._jump_to_end_button.reset_unread_count()
if send_marker: @@ -576,7 +591,7 @@ def change_encryption(self, action, param): self.set_encryption_menu_icon() self.set_lock_image()
- def set_lock_image(self): + def set_lock_image(self) -> None: encryption_state = {'visible': self.encryption is not None, 'enc_type': self.encryption, 'authenticated': False} @@ -621,7 +636,7 @@ def get_encryption_state(self): return None return state
- def set_encryption_menu_icon(self): + def set_encryption_menu_icon(self) -> None: image = self.xml.encryption_menu.get_image() if image is None: image = Gtk.Image() @@ -633,7 +648,7 @@ def set_encryption_menu_icon(self): image.set_from_icon_name('channel-secure-symbolic', Gtk.IconSize.MENU)
- def set_speller(self): + def set_speller(self) -> None: if (not app.is_installed('GSPELL') or not app.settings.get('use_speller')): return @@ -649,7 +664,7 @@ def set_speller(self):
spell_checker.connect('notify::language', self.on_language_changed)
- def get_speller_language(self): + def get_speller_language(self) -> Optional[Gspell.Language]: lang = self.contact.settings.get('speller_language') if not lang: # use the default one @@ -665,7 +680,7 @@ def on_language_changed(self, checker, _param): gspell_lang = checker.get_language() self.contact.settings.set('speller_language', gspell_lang.get_code())
- def shutdown(self): + def shutdown(self) -> None: # remove_gui_extension_point() is called on shutdown, but also when # a plugin is getting disabled. Plugins don’t know the difference. # Plugins might want to remove their widgets on @@ -763,17 +778,24 @@ def _on_message_textview_paste_event(self, _texview): callback=self._paste_event_confirmed, args=[image])]).show()
- def _paste_event_confirmed(self, is_checked, image): + def _paste_event_confirmed(self, + is_checked: bool, + image: Optional[GdkPixbuf.Pixbuf] + ) -> None: if is_checked: app.settings.set('confirm_paste_image', False)
dir_ = tempfile.gettempdir() path = os.path.join(dir_, f'{uuid.uuid4()}.png') + if image is None: + self.add_info_message(_('Error: Could not process image')) + return + image.savev(path, 'png', [], [])
self._start_filetransfer(path)
- def _get_pref_ft_method(self): + def _get_pref_ft_method(self) -> Optional[str]: ft_pref = app.settings.get_account_setting(self.account, 'filetransfer_preference') httpupload = app.window.lookup_action( @@ -1006,12 +1028,13 @@ def _on_send_message(self, *args): self.send_message(message, xhtml=xhtml)
def send_message(self, - message, - type_='chat', - resource=None, - xhtml=None, - process_commands=True, - attention=False): + message: str, + type_: str = 'chat', + resource: Optional[str] = None, + xhtml: Optional[str] = None, + process_commands: bool = True, + attention: bool = False + ) -> None: """ Send the given message to the active tab. Doesn't return None if error """ @@ -1023,6 +1046,7 @@ def send_message(self,
label = self.get_seclabel()
+ correct_id: Optional[str] = None if self.correcting and self.last_sent_msg: correct_id = self.last_sent_msg else: @@ -1073,7 +1097,7 @@ def _on_message_tv_buffer_changed(self, textbuffer): self._client.get_module('Chatstate').set_chatstate( self.contact, Chatstate.COMPOSING)
- def save_message(self, message, msg_type): + def save_message(self, message: str, msg_type: str) -> None: # save the message, so user can scroll though the list with key up/down if msg_type == 'sent': history = self.sent_history @@ -1099,10 +1123,10 @@ def save_message(self, message, msg_type): else: self.received_history_pos = pos
- def add_info_message(self, text): + def add_info_message(self, text: str) -> None: self.conversation_view.add_info_message(text)
- def add_file_transfer(self, transfer): + def add_file_transfer(self, transfer: HTTPFileTransfer) -> None: self.conversation_view.add_file_transfer(transfer)
def add_jingle_file_transfer(self, event): @@ -1118,16 +1142,17 @@ def add_call_message(self, event): self.conversation_view.add_call_message(event=event)
def add_message(self, - text, - kind, - name, - tim, - notify, - displaymarking=None, - msg_log_id=None, - message_id=None, - stanza_id=None, - additional_data=None): + text: str, + kind: str, + name: str, + tim: float, + notify: bool, + displaymarking: Optional[Displaymarking] = None, + msg_log_id: Optional[str] = None, + message_id: Optional[str] = None, + stanza_id: Optional[str] =None, + additional_data: Optional[AdditionalDataDict] = None + ) -> None:
if additional_data is None: additional_data = AdditionalDataDict() @@ -1174,8 +1199,12 @@ def add_message(self, if chat_active and self._scrolled_view.get_autoscroll(): app.window.mark_as_read(self.account, self.contact.jid)
- def _notify(self, name: str, text: str, tim: Optional[int], - additional_data: AdditionalDataDict) -> None: + def _notify(self, + name: str, + text: str, + tim: Optional[float], + additional_data: AdditionalDataDict + ) -> None: if app.window.is_chat_active(self.account, self.contact.jid): if self._scrolled_view.get_autoscroll(): return @@ -1192,6 +1221,7 @@ def _notify(self, name: str, text: str, tim: Optional[int], _icon, file_type = guess_simple_file_type(text) text = f'{file_type} ({file_name})'
+ sound: Optional[str] = None if self.is_chat: msg_type = 'chat-message' sound = 'first_message_received' @@ -1205,6 +1235,7 @@ def _notify(self, name: str, text: str, tim: Optional[int], sound = 'muc_message_highlight' else: sound = 'muc_message_received' + if not self.contact.can_notify(): sound = None
@@ -1234,7 +1265,7 @@ def _notify(self, name: str, text: str, tim: Optional[int], text=text, sound=sound))
- def toggle_emoticons(self): + def toggle_emoticons(self) -> None: """ Hide show emoticons_button """ @@ -1245,7 +1276,7 @@ def toggle_emoticons(self): self.xml.emoticons_button.set_no_show_all(True) self.xml.emoticons_button.hide()
- def set_emoticon_popover(self): + def set_emoticon_popover(self) -> None: if not app.settings.get('emoticons_theme'): return
@@ -1295,7 +1326,7 @@ def clear(tv): start, end = buffer_.get_bounds() buffer_.delete(start, end)
- def set_control_active(self, state): + def set_control_active(self, state: bool) -> None: if state: self.set_emoticon_popover()
@@ -1309,19 +1340,19 @@ def set_control_active(self, state): self._client.get_module('Chatstate').set_chatstate( self.contact, Chatstate.INACTIVE)
- def reset_view(self): + def reset_view(self) -> None: self.conversation_view.clear()
- def get_autoscroll(self): + def get_autoscroll(self) -> bool: return self._scrolled_view.get_autoscroll()
- def scroll_to_end(self, force=False): + def scroll_to_end(self, force: bool = False) -> None: # Clear view and reload conversation self.conversation_view.clear() self._scrolled_view.reset() self.conversation_view.scroll_to_end(force)
- def scroll_to_message(self, log_line_id, timestamp): + def scroll_to_message(self, log_line_id: str, timestamp: float) -> None: row = self.conversation_view.get_row_by_log_line_id(log_line_id) if row is None: # Clear view and reload conversation around timestamp @@ -1338,7 +1369,11 @@ def scroll_to_message(self, log_line_id, timestamp): log_line_id) GLib.idle_add(self.conversation_view.unlock)
- def fetch_n_lines_history(self, _scrolled, before, n_lines): + def fetch_n_lines_history(self, + _scrolled: Gtk.ScrolledWindow, + before: bool, + n_lines: int + ) -> None: if self.conversation_view.locked: return
@@ -1436,13 +1471,17 @@ def add_messages(self, messages): marker=msg.marker, error=msg.error)
- def has_focus(self): + def has_focus(self) -> bool: if app.window.get_property('has-toplevel-focus'): if self == app.window.get_active_control(): return True return False
- def scroll_messages(self, direction, msg_buf, msg_type): + def scroll_messages(self, + direction: str, + msg_buf: Gtk.TextBuffer, + msg_type: str, + ) -> None: if msg_type == 'sent': history = self.sent_history pos = self.sent_history_pos @@ -1490,7 +1529,7 @@ def scroll_messages(self, direction, msg_buf, msg_type): message = '> %s\n' % message.replace('\n', '\n> ') msg_buf.set_text(message)
- def update_account_badge(self): + def update_account_badge(self) -> None: show = len(app.settings.get_active_accounts()) > 1 self.xml.account_badge_box.set_visible(show)
===================================== gajim/gtk/controls/chat.py ===================================== @@ -25,6 +25,7 @@
from typing import ClassVar # pylint: disable=unused-import from typing import Type # pylint: disable=unused-import +from typing import Optional
import time import logging @@ -34,8 +35,10 @@ from gi.repository import GLib from gi.repository import Gdk
-from nbxmpp.namespaces import Namespace +from nbxmpp import JID from nbxmpp.const import Chatstate +from nbxmpp.modules.security_labels import Displaymarking +from nbxmpp.namespaces import Namespace
from gajim.common import app from gajim.common import helpers @@ -80,14 +83,17 @@ class ChatControl(BaseControl): # processed with this command host. COMMAND_HOST = ChatCommands # type: ClassVar[Type[CommandHost]]
- def __init__(self, account, jid): + def __init__(self, account: str, jid: JID) -> None: BaseControl.__init__(self, 'chat_control', account, jid)
- self.last_recv_message_id = None - self.last_recv_message_marks = None + self.sendmessage: bool = True + + # XEP-0308 Message Correction + self.correcting: bool = False + self.last_sent_msg: Optional[str] = None
self.toggle_emoticons()
@@ -147,7 +153,7 @@ def __init__(self, account, jid): app.plugin_manager.gui_extension_point('chat_control', self) self.update_actions()
- def _connect_contact_signals(self): + def _connect_contact_signals(self) -> None: self.contact.multi_connect({ 'presence-update': self._on_presence_update, 'chatstate-update': self._on_chatstate_update, @@ -157,10 +163,10 @@ def _connect_contact_signals(self): })
@property - def jid(self): + def jid(self) -> JID: return self.contact.jid
- def add_actions(self): + def add_actions(self) -> None: super().add_actions() actions = [ ('invite-contacts-', self._on_invite_contacts), @@ -194,7 +200,7 @@ def add_actions(self): act.connect('change-state', self._on_send_marker) app.window.add_action(act)
- def update_actions(self): + def update_actions(self) -> None: online = app.account_is_connected(self.account)
if self.type.is_chat: @@ -254,7 +260,7 @@ def update_actions(self): # Information self._get_action('information-').set_enabled(online)
- def remove_actions(self): + def remove_actions(self) -> None: super().remove_actions() actions = [ 'invite-contacts-', @@ -268,7 +274,7 @@ def remove_actions(self): for action in actions: app.window.remove_action(f'{action}{self.control_id}')
- def focus(self): + def focus(self) -> None: self.msg_textview.grab_focus()
def delegate_action(self, action): @@ -307,7 +313,7 @@ def _on_send_marker(self, action, param): action.set_state(param) self.contact.settings.set('send_marker', param.get_boolean())
- def _update_toolbar(self): + def _update_toolbar(self) -> None: # Formatting # TODO: find out what encryption allows for xhtml and which not if self.contact.supports(Namespace.XHTML_IM): @@ -319,13 +325,13 @@ def _update_toolbar(self): self.xml.formattings_button.set_tooltip_text( _('This contact does not support HTML'))
- def update_all_pep_types(self): + def update_all_pep_types(self) -> None: self._update_pep(PEPEventType.LOCATION) self._update_pep(PEPEventType.MOOD) self._update_pep(PEPEventType.ACTIVITY) self._update_pep(PEPEventType.TUNE)
- def _update_pep(self, type_): + def _update_pep(self, type_: PEPEventType) -> None: return # TODO image = self._get_pep_widget(type_) @@ -351,7 +357,7 @@ def _update_pep(self, type_): image.set_tooltip_markup(formated_text) image.show()
- def _get_pep_widget(self, type_): + def _get_pep_widget(self, type_: PEPEventType) -> Optional[Gtk.Image]: if type_ == PEPEventType.MOOD: return self.xml.mood_image if type_ == PEPEventType.ACTIVITY: @@ -488,7 +494,7 @@ def _nec_ping(self, event): self.add_info_message(event.error)
# Jingle AV calls - def _on_start_call(self): + def _on_start_call(self) -> None: self._call_widget.start_call()
def _process_jingle_av_event(self, event): @@ -527,14 +533,14 @@ def on_location_eventbox_enter_notify_event(self, _widget, _event): cursor = get_cursor('pointer') app.window.get_window().set_cursor(cursor)
- def update_ui(self): + def update_ui(self) -> None: # The name banner is drawn here BaseControl.update_ui(self) self.update_toolbar() self._update_avatar() self.update_actions()
- def draw_banner_text(self): + def draw_banner_text(self) -> None: """ Draws the chat banner's text (e.g. name, chat state) in the top of the chat window @@ -572,10 +578,11 @@ def draw_banner_text(self): self.xml.banner_name_label.set_tooltip_text(label_tooltip)
def send_message(self, - message, - xhtml=None, - process_commands=True, - attention=False): + message: str, + xhtml: Optional[str] = None, + process_commands: bool = True, + attention: bool = False + ) -> None: """ Send a message to contact """ @@ -598,19 +605,20 @@ def send_message(self, process_commands=process_commands, attention=attention)
- def get_our_nick(self): + def get_our_nick(self) -> str: return app.nicks[self.account]
def add_message(self, - text, - kind, - tim=None, - displaymarking=None, - msg_log_id=None, - stanza_id=None, - message_id=None, - additional_data=None, - notify=True): + text: str, + kind: str, + tim: Optional[float] = None, + displaymarking: Optional[Displaymarking] = None, + msg_log_id: Optional[str] = None, + stanza_id: Optional[str] = None, + message_id: Optional[str] = None, + additional_data: Optional[AdditionalDataDict] = None, + notify: bool = True + ) -> None:
if additional_data is None: additional_data = AdditionalDataDict() @@ -632,7 +640,7 @@ def add_message(self, stanza_id=stanza_id, additional_data=additional_data)
- def shutdown(self): + def shutdown(self) -> None: # PluginSystem: removing GUI extension points connected with ChatControl # instance object app.plugin_manager.remove_gui_extension_point('chat_control', self) @@ -682,7 +690,7 @@ def allow_shutdown(self, _method, on_yes, on_no): def _on_avatar_update(self, _contact, _signal_name): self._update_avatar()
- def _update_avatar(self): + def _update_avatar(self) -> None: scale = app.window.get_scale_factor() surface = self.contact.get_avatar(AvatarSize.CHAT, scale) self.xml.avatar_image.set_from_surface(surface)
===================================== gajim/gtk/controls/groupchat.py ===================================== @@ -24,15 +24,21 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from typing import Any +from typing import List +from typing import Optional + import time import logging
+from nbxmpp import JID from nbxmpp.namespaces import Namespace from nbxmpp.protocol import InvalidJid from nbxmpp.protocol import validate_resourcepart from nbxmpp.const import StatusCode from nbxmpp.const import Affiliation from nbxmpp.errors import StanzaError +from nbxmpp.modules.security_labels import Displaymarking from nbxmpp.modules.vcard_temp import VCard
from gi.repository import Gtk @@ -46,6 +52,7 @@ from gajim.common import app from gajim.common import ged from gajim.common import helpers +from gajim.common.helpers import AdditionalDataDict from gajim.common.helpers import event_filter from gajim.common.helpers import to_user_string from gajim.common.const import AvatarSize @@ -86,7 +93,7 @@ class GroupchatControl(BaseControl): # will be processed with this command host. COMMAND_HOST = GroupChatCommands
- def __init__(self, account, jid): + def __init__(self, account: str, jid: JID) -> None: BaseControl.__init__(self, 'groupchat_control', account, @@ -95,23 +102,27 @@ def __init__(self, account, jid): self._client.connect_signal('state-changed', self._on_client_state_changed)
- self.is_anonymous = True + self.is_anonymous: bool = True
self.toggle_emoticons()
- self.room_jid = str(self.contact.jid) + self.room_jid: str = str(self.contact.jid)
# Stores nickname we want to kick - self._kick_nick = None + self._kick_nick: Optional[str] = None
# Stores nickname we want to ban - self._ban_jid = None + self._ban_jid: Optional[str] = None
# Last sent message text - self.last_sent_txt = '' + self.last_sent_txt: str = ''
# Attribute, encryption plugins use to signal the message can be sent - self.sendmessage = False + self.sendmessage: bool = False + + # XEP-0308 Message Correction + self.correcting: bool = False + self.last_sent_msg: Optional[str] = None
self.roster = GroupchatRoster(self.account, self.room_jid, self) self.xml.roster_revealer.add(self.roster) @@ -126,13 +137,13 @@ def __init__(self, account, jid):
# muc attention flag (when we are mentioned in a muc) # if True, the room has mentioned us - self.attention_flag = False + self.attention_flag: bool = False
# sorted list of nicks who mentioned us (last at the end) - self.attention_list = [] - self.nick_hits = [] - self.__nick_completion = None - self.last_key_tabs = False + self.attention_list: List[str] = [] + self.nick_hits: List[str] = [] + self.__nick_completion: Optional[NickCompletionGenerator] = None + self.last_key_tabs: bool = False
self.setup_seclabel()
@@ -158,7 +169,7 @@ def __init__(self, account, jid): self._update_avatar()
# Holds CaptchaRequest widget - self._captcha_request = None + self._captcha_request: Optional[DataFormWidget] = None
# MUC Info self._subject_data = None @@ -166,7 +177,7 @@ def __init__(self, account, jid): self.xml.info_box.add(self._muc_info_box)
# Groupchat settings - self._groupchat_settings_box = None + self._groupchat_settings_box: Optional[GroupChatSettings] = None
# Groupchat invite self.xml.quick_invite_button.set_action_name( @@ -211,7 +222,7 @@ def __init__(self, account, jid): # instance object app.plugin_manager.gui_extension_point('groupchat_control', self)
- def _connect_contact_signals(self): + def _connect_contact_signals(self) -> None: self.contact.multi_connect({ 'state-changed': self._on_muc_state_changed, 'avatar-update': self._on_avatar_update, @@ -260,7 +271,7 @@ def _nick_completion(self): return self.__nick_completion
@property - def subject(self): + def subject(self) -> str: if self._subject_data is None: return '' return self._subject_data.subject @@ -269,7 +280,7 @@ def subject(self): def disco_info(self): return app.storage.cache.get_last_disco_info(self.contact.jid)
- def add_actions(self): + def add_actions(self) -> None: super().add_actions() actions = [ ('groupchat-settings-', None, self._on_groupchat_settings), @@ -368,7 +379,7 @@ def update_actions(self, *args):
self._get_action('kick-').set_enabled(joined)
- def remove_actions(self): + def remove_actions(self) -> None: super().remove_actions() actions = [ 'groupchat-settings-', @@ -398,7 +409,7 @@ def is_irc(self) -> bool: return False return self.disco_info.is_irc
- def _is_subject_change_allowed(self): + def _is_subject_change_allowed(self) -> Optional[Any]: contact = self.contact.get_self() if contact is None: return False @@ -410,7 +421,7 @@ def _is_subject_change_allowed(self): return False return self.disco_info.muc_subjectmod or False
- def _show_page(self, name): + def _show_page(self, name: str) -> None: transition = Gtk.StackTransitionType.SLIDE_DOWN if name == 'groupchat': transition = Gtk.StackTransitionType.SLIDE_UP @@ -422,7 +433,7 @@ def _show_page(self, name): self.xml.info_close_button.grab_focus() self.xml.stack.set_visible_child_full(name, transition)
- def _get_current_page(self): + def _get_current_page(self) -> str: return self.xml.stack.get_visible_child_name()
def _on_muc_disco_update(self, _event): @@ -624,11 +635,11 @@ def _on_roster_row_activated(self, _roster, nick): def _on_avatar_update(self, _contact, _signal_name): self._update_avatar()
- def _update_avatar(self): + def _update_avatar(self) -> None: surface = self.contact.get_avatar(AvatarSize.CHAT, self.scale_factor) self.xml.avatar_image.set_from_surface(surface)
- def draw_banner_text(self): + def draw_banner_text(self) -> None: """ Draw the text in the fat line at the top of the window that houses the room jid @@ -702,14 +713,15 @@ def _on_gc_message_received(self, event): self._client.get_own_jid().bare)
def add_message(self, - text, - contact='', - tim=None, - displaymarking=None, - message_id=None, - stanza_id=None, - additional_data=None, - notify=True): + text: str, + contact: str = '', + tim: Optional[float] = None, + displaymarking: Optional[Displaymarking] = None, + message_id: Optional[str] = None, + stanza_id: Optional[str] = None, + additional_data: Optional[AdditionalDataDict] = None, + notify: bool = True + ) -> None:
if contact == self.contact.nickname: kind = 'outgoing' @@ -809,7 +821,7 @@ def _nec_ping(self, event): elif event.name == 'ping-error': self.add_info_message(event.error)
- def _set_control_active(self): + def _set_control_active(self) -> None: self.xml.formattings_button.set_sensitive(True) self.msg_textview.set_sensitive(True) self.msg_textview.set_editable(True) @@ -822,7 +834,7 @@ def _set_control_active(self):
self.update_actions()
- def _set_control_inactive(self): + def _set_control_inactive(self) -> None: self.xml.formattings_button.set_sensitive(False) self.msg_textview.set_sensitive(False) self.msg_textview.set_editable(False) @@ -834,7 +846,7 @@ def _set_control_inactive(self):
self.update_actions()
- def rejoin(self): + def rejoin(self) -> None: self._client.get_module('MUC').join(self.room_jid)
# def send_pm(self, nick, message=None): @@ -1115,7 +1127,11 @@ def _on_message_sent(self, event): self.msg_textview.get_style_context().remove_class( 'gajim-msg-correcting')
- def send_message(self, message, xhtml=None, process_commands=True): + def send_message(self, + message: str, + xhtml: Optional[str] = None, + process_commands: bool = True + ) -> None: """ Call this function to send our message """ @@ -1182,7 +1198,7 @@ def shutdown(self, reason=None): super(GroupchatControl, self).shutdown() app.check_finalize(self)
- def safe_shutdown(self): + def safe_shutdown(self) -> bool: # whether to ask for confirmation before closing muc if app.settings.get('confirm_close_muc') and self.contact.is_joined: return False @@ -1219,12 +1235,12 @@ def on_cancel(is_checked):
on_yes(self)
- def _close_control(self): + def _close_control(self) -> None: app.window.activate_action( 'remove-chat', GLib.Variant('as', [self.account, str(self.room_jid)]))
- def set_control_active(self, state): + def set_control_active(self, state: bool) -> None: self.attention_flag = False BaseControl.set_control_active(self, state)
@@ -1396,7 +1412,7 @@ def delegate_action(self, action):
return Gdk.EVENT_PROPAGATE
- def focus(self): + def focus(self) -> None: page_name = self._get_current_page() if page_name == 'groupchat': self.msg_textview.grab_focus() @@ -1547,7 +1563,7 @@ def _on_room_captcha_error(self, _contact, _signal_name, error): self.xml.captcha_error_label.set_text(error_text) self._show_page('captcha-error')
- def _remove_captcha_request(self): + def _remove_captcha_request(self) -> None: if self._captcha_request is None: return if self._captcha_request in self.xml.captcha_box.get_children():
===================================== gajim/gtk/controls/private.py ===================================== @@ -24,6 +24,10 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from typing import Optional + +from nbxmpp import JID + from gajim.common import app from gajim.common import helpers from gajim.common.i18n import _ @@ -43,7 +47,7 @@ class PrivateChatControl(ChatControl): # will be processed with this command host. COMMAND_HOST = PrivateChatCommands
- def __init__(self, account, jid): + def __init__(self, account: str, jid: JID) -> None: self._client = app.get_client(account) self._room_contact = self._client.get_module('Contacts').get_contact( jid.bare) @@ -53,7 +57,7 @@ def __init__(self, account, jid): # self.register_events([ # ('update-gc-avatar', ged.GUI1, self._on_update_gc_avatar),
- def _connect_contact_signals(self): + def _connect_contact_signals(self) -> None: self.contact.multi_connect({ 'user-avatar-update': self._on_user_avatar_update, 'user-joined': self._on_user_joined, @@ -68,10 +72,10 @@ def _connect_contact_signals(self): })
@property - def room_name(self): + def room_name(self) -> str: return self._room_contact.name
- def get_our_nick(self): + def get_our_nick(self) -> str: muc_data = self._client.get_module('MUC').get_muc_data( self._room_contact.jid) return muc_data.nick @@ -136,8 +140,12 @@ def _on_room_joined(self, *args): return self.got_connected()
- def send_message(self, message, xhtml=None, process_commands=True, - attention=False): + def send_message(self, + message: str, + xhtml: Optional[str] = None, + process_commands: bool = True, + attention: bool = False + ) -> None: """ Call this method to send the message """ @@ -161,7 +169,7 @@ def send_message(self, message, xhtml=None, process_commands=True, process_commands=process_commands, attention=attention)
- def update_ui(self): + def update_ui(self) -> None: ChatControl.update_ui(self)
def _on_user_avatar_update(self, *args):
===================================== gajim/gtk/main.py ===================================== @@ -14,6 +14,7 @@ from gajim.common import app from gajim.common import ged from gajim.common.const import Display +from gajim.common.const import Direction from gajim.common.helpers import ask_for_status_message from gajim.common.i18n import _ from gajim.common.nec import EventHelper @@ -260,19 +261,19 @@ def _on_action(self, action, _param): # return None
if action == 'switch-next-tab': - self.select_next_chat(True) + self.select_next_chat(Direction.NEXT) return None
if action == 'switch-prev-tab': - self.select_next_chat(False) + self.select_next_chat(Direction.PREV) return None
if action == 'switch-next-unread-tab-right': - self.select_next_chat(True, unread_first=True) + self.select_next_chat(Direction.NEXT, unread_first=True) return None
if action == 'switch-next-unread-tab-left': - self.select_next_chat(False, unread_first=True) + self.select_next_chat(Direction.PREV, unread_first=True) return None
if action.startswith('switch-tab-'): @@ -443,12 +444,12 @@ def select_chat(self, account: str, jid: JID) -> None: self._main_stack.show_chat_page() self._chat_page.select_chat(account, jid)
- def select_next_chat(self, forwards: bool, + def select_next_chat(self, direction: Direction, unread_first: bool = False) -> None: chat_list_stack = self._chat_page.get_chat_list_stack() chat_list = chat_list_stack.get_current_chat_list() if chat_list is not None: - chat_list.select_next_chat(forwards, unread_first) + chat_list.select_next_chat(direction, unread_first)
def select_chat_number(self, number: int) -> None: chat_list_stack = self._chat_page.get_chat_list_stack()
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/54a0f28d7a42bf787a457e4662723f4e...