From 3c103315ec450fc4f15561ef4136b5632f507ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20H=C3=B6rist?= Date: Thu, 19 Oct 2017 11:26:22 +0200 Subject: [PATCH] Refactor Speller --- gajim/chat_control.py | 12 +------ gajim/chat_control_base.py | 72 +++++++++++++++++++++++++++----------- gajim/config.py | 38 +++++--------------- gajim/dialogs.py | 25 ++++++------- gajim/gtkspell.py | 45 +++++++++++++++++------- gajim/gui_interface.py | 12 ------- gajim/message_textview.py | 11 +++--- 7 files changed, 111 insertions(+), 104 deletions(-) diff --git a/gajim/chat_control.py b/gajim/chat_control.py index d21aaf192..f23d77711 100644 --- a/gajim/chat_control.py +++ b/gajim/chat_control.py @@ -58,13 +58,6 @@ from gajim.common.exceptions import GajimGeneralException from gajim.common.const import AvatarSize from gajim.command_system.implementation.hosts import ChatCommands - -try: - from gajim import gtkspell - HAS_GTK_SPELL = True -except (ImportError, ValueError): - HAS_GTK_SPELL = False - from gajim.chat_control_base import ChatControlBase ################################################################################ @@ -1180,10 +1173,7 @@ class ChatControl(ChatControlBase): self.handlers[i].disconnect(i) del self.handlers[i] self.conv_textview.del_handlers() - if app.config.get('use_speller') and HAS_GTK_SPELL: - spell_obj = gtkspell.get_from_text_view(self.msg_textview) - if spell_obj: - spell_obj.detach() + self.remove_speller() self.msg_textview.destroy() # PluginSystem: calling shutdown of super class (ChatControlBase) to let # it remove it's GUI extension points diff --git a/gajim/chat_control_base.py b/gajim/chat_control_base.py index db05c10d6..09797222e 100644 --- a/gajim/chat_control_base.py +++ b/gajim/chat_control_base.py @@ -35,12 +35,14 @@ from gi.repository import Pango from gi.repository import GObject from gi.repository import GLib from gi.repository import Gio + from gajim import gtkgui_helpers from gajim.gtkgui_helpers import Color from gajim import message_control from gajim import dialogs from gajim import history_window from gajim import notify +from gajim import gtkspell import re from gajim import emoticons @@ -64,11 +66,6 @@ from gajim.command_system.implementation.middleware import CommandTools from gajim.command_system.implementation import standard from gajim.command_system.implementation import execute -try: - from gajim import gtkspell - HAS_GTK_SPELL = True -except (ImportError, ValueError): - HAS_GTK_SPELL = False ################################################################################ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): @@ -355,8 +352,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): self.set_emoticon_popover() # Attach speller - if app.config.get('use_speller') and HAS_GTK_SPELL: - self.set_speller() + self.spell = None + self.spell_handlers = [] + self.set_speller() self.conv_textview.tv.show() # For XEP-0172 @@ -480,24 +478,57 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): image.set_from_pixbuf(icon) def set_speller(self): - # now set the one the user selected + if not gtkspell.HAS_GTK_SPELL or not app.config.get('use_speller'): + return + + def _on_focus_in(*args): + if self.spell is None: + return + self.spell.attach(self.msg_textview) + + def _on_focus_out(*args): + if self.spell is None: + return + if not self.msg_textview.has_text(): + self.spell.detach() + + lang = self.get_speller_language() + if not lang: + return + try: + self.spell = gtkspell.Spell(self.msg_textview, lang) + self.spell.connect('language_changed', self.on_language_changed) + handler_id = self.msg_textview.connect('focus-in-event', + _on_focus_in) + self.spell_handlers.append(handler_id) + handler_id = self.msg_textview.connect('focus-out-event', + _on_focus_out) + self.spell_handlers.append(handler_id) + except OSError: + dialogs.AspellDictError(lang) + app.config.set('use_speller', False) + + def remove_speller(self): + if self.spell is None: + return + self.spell.detach() + for id_ in self.spell_handlers: + self.msg_textview.disconnect(id_) + self.spell_handlers.remove(id_) + self.spell = None + + def get_speller_language(self): per_type = 'contacts' - if self.type_id == message_control.TYPE_GC: + if self.type_id == 'gc': per_type = 'rooms' - lang = app.config.get_per(per_type, self.contact.jid, - 'speller_language') + lang = app.config.get_per( + per_type, self.contact.jid, 'speller_language') if not lang: # use the default one lang = app.config.get('speller_language') if not lang: lang = app.LANG - if lang: - try: - self.spell = gtkspell.Spell(self.msg_textview, lang) - self.msg_textview.lang = lang - self.spell.connect('language_changed', self.on_language_changed) - except (GObject.GError, RuntimeError, TypeError, OSError): - dialogs.AspellDictError(lang) + return lang or None def on_language_changed(self, spell, lang): per_type = 'contacts' @@ -505,9 +536,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): per_type = 'rooms' if not app.config.get_per(per_type, self.contact.jid): app.config.add_per(per_type, self.contact.jid) - app.config.set_per(per_type, self.contact.jid, 'speller_language', - lang) - self.msg_textview.lang = lang + app.config.set_per( + per_type, self.contact.jid, 'speller_language', lang) def on_banner_label_populate_popup(self, label, menu): """ diff --git a/gajim/config.py b/gajim/config.py index 12d1a199e..72b496526 100644 --- a/gajim/config.py +++ b/gajim/config.py @@ -50,12 +50,7 @@ from gajim import message_control from gajim.chat_control_base import ChatControlBase from gajim import dataforms_widget from gajim import gui_menu_builder - -try: - from gajim import gtkspell - HAS_GTK_SPELL = True -except (ImportError, ValueError): - HAS_GTK_SPELL = False +from gajim import gtkspell from gajim.common import helpers from gajim.common import app @@ -192,7 +187,7 @@ class PreferencesWindow: self.xml.get_object('xhtml_checkbutton').set_active(st) # use speller - if HAS_GTK_SPELL: + if gtkspell.HAS_GTK_SPELL: st = app.config.get('use_speller') self.xml.get_object('speller_checkbutton').set_active(st) else: @@ -660,23 +655,13 @@ class PreferencesWindow: def apply_speller(self): for ctrl in self._get_all_controls(): if isinstance(ctrl, ChatControlBase): - try: - spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview) - except (TypeError, RuntimeError, OSError): - spell_obj = None - - if not spell_obj: - ctrl.set_speller() + ctrl.set_speller() def remove_speller(self): for ctrl in self._get_all_controls(): if isinstance(ctrl, ChatControlBase): - try: - spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview) - except (TypeError, RuntimeError): - spell_obj = None - if spell_obj: - spell_obj.detach() + if ctrl.spell is not None: + ctrl.remove_speller() def on_speller_checkbutton_toggled(self, widget): active = widget.get_active() @@ -685,15 +670,10 @@ class PreferencesWindow: lang = app.config.get('speller_language') if not lang: lang = app.LANG - tv = Gtk.TextView() - try: - gtkspell.Spell(tv, lang) - except (TypeError, RuntimeError, OSError): - dialogs.ErrorDialog( - _('Dictionary for lang %s not available') % lang, - _('You have to install %s dictionary to use spellchecking, or ' - 'choose another language by setting the speller_language option.' - ) % lang) + + available = gtkspell.test_language(lang) + if not available: + dialogs.AspellDictError(lang) app.config.set('use_speller', False) widget.set_active(False) else: diff --git a/gajim/dialogs.py b/gajim/dialogs.py index ce48f6571..ebc8451fd 100644 --- a/gajim/dialogs.py +++ b/gajim/dialogs.py @@ -42,6 +42,7 @@ from gajim import gtkgui_helpers from gajim import vcard from gajim import conversation_textview from gajim import dataforms_widget +from gajim import gtkspell from random import randrange from gajim.common import pep @@ -50,12 +51,6 @@ from gajim.common import const from gajim.options_dialog import OptionsDialog from gajim.common.const import Option, OptionKind, OptionType -try: - from gajim import gtkspell - HAS_GTK_SPELL = True -except (ImportError, ValueError): - HAS_GTK_SPELL = False - # those imports are not used in this file, but in files that 'import dialogs' # so they can do dialog.GajimThemesWindow() for example from gajim.filetransfers_window import FileTransfersWindow @@ -1486,11 +1481,11 @@ class FileChooserDialog(Gtk.FileChooserDialog): class AspellDictError: def __init__(self, lang): ErrorDialog( - _('Dictionary for lang %s not available') % lang, - _('You have to install %s dictionary to use spellchecking, or ' - 'choose another language by setting the speller_language option.' - '\n\nHighlighting misspelled words feature will not be used') % lang) - app.config.set('use_speller', False) + _('Dictionary for lang "%s" not available') % lang, + _('You have to install the dictionary "%s" to use spellchecking, ' + 'or choose another language by setting the speller_language ' + 'option.\n\n' + 'Highlighting misspelled words feature will not be used') % lang) class ConfirmationDialog(HigDialog): """ @@ -3082,14 +3077,14 @@ class SingleMessageWindow: else: self.to_entry.set_text(to) - if app.config.get('use_speller') and HAS_GTK_SPELL and action == 'send': + if app.config.get('use_speller') and gtkspell.HAS_GTK_SPELL and action == 'send': try: lang = app.config.get('speller_language') if not lang: lang = app.LANG - gtkspell.Spell(self.conversation_textview.tv, lang) - gtkspell.Spell(self.message_textview, lang) - except (GObject.GError, TypeError, RuntimeError, OSError): + self.spell = gtkspell.Spell(self.message_textview, lang) + self.spell.attach(self.message_textview) + except OSError: AspellDictError(lang) self.prepare_widgets_for(self.action) diff --git a/gajim/gtkspell.py b/gajim/gtkspell.py index 24d52e9d8..f1883f54f 100644 --- a/gajim/gtkspell.py +++ b/gajim/gtkspell.py @@ -19,9 +19,16 @@ from gi.repository import GObject from gi.repository import Gtk +from gi.repository import GLib import gi gi.require_version('GtkSpell', '3.0') -from gi.repository import GtkSpell +try: + from gi.repository import GtkSpell + HAS_GTK_SPELL = True +except ImportError: + HAS_GTK_SPELL = False + +from gajim.common import app def ensure_attached(func): @@ -48,12 +55,13 @@ class Spell(GObject.GObject): if spell: raise RuntimeError("Textview has already a Spell obj attached") self.spell = GtkSpell.Checker.new() - if not self.spell: - raise OSError("Unable to create spell object.") - if not self.spell.attach(textview): - raise OSError("Unable to attach spell object.") - if not self.spell.set_language(language): - raise OSError("Unable to set language: '%s'" % language) + + try: + self.spell.set_language(language) + except GLib.GError as error: + if error.domain == 'gtkspell-error-quark': + raise OSError("Unable to set language: '%s'" % language) + self.spell.connect('language-changed', self.on_language_changed) else: @@ -74,12 +82,25 @@ class Spell(GObject.GObject): def recheck_all(self): self.spell.recheck_all() - @ensure_attached def detach(self): - self.spell.detach() - self.spell = None + if self.spell is not None: + self.spell.detach() + + def attach(self, textview): + spell = GtkSpell.Checker.get_from_text_view(textview) + if spell is None: + print('attached') + self.spell.attach(textview) + GObject.type_register(Spell) -def get_from_text_view(textview): - return Spell(textview, create=False) + +def test_language(lang): + spell = GtkSpell.Checker.new() + try: + spell.set_language(lang) + except GLib.GError as error: + if error.domain == 'gtkspell-error-quark': + return False + return True diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py index cf9274cf0..6b221494a 100644 --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -2858,18 +2858,6 @@ class Interface: # get transports type from DB app.transport_type = app.logger.get_transports_type() - # test is dictionnary is present for speller - if app.config.get('use_speller'): - lang = app.config.get('speller_language') - if not lang: - lang = app.LANG - tv = Gtk.TextView() - try: - from gajim import gtkspell - spell = gtkspell.Spell(tv, lang) - except (ImportError, TypeError, RuntimeError, OSError, ValueError): - dialogs.AspellDictError(lang) - if app.config.get('soundplayer') == '': # only on first time Gajim starts commands = ('paplay', 'aplay', 'play', 'ossplay') diff --git a/gajim/message_textview.py b/gajim/message_textview.py index bd760587a..acd6c4dbf 100644 --- a/gajim/message_textview.py +++ b/gajim/message_textview.py @@ -57,7 +57,7 @@ class MessageTextView(Gtk.TextView): self.undo_list = [] # needed to know if we undid something self.undo_pressed = False - self.lang = None # Lang used for spell checking + _buffer = self.get_buffer() self.begin_tags = {} self.end_tags = {} @@ -92,12 +92,15 @@ class MessageTextView(Gtk.TextView): _buffer.insert_with_tags( start, self.PLACEHOLDER, self.placeholder_tag) - def _on_focus_in(self, *args): + def has_text(self): buf = self.get_buffer() start, end = buf.get_bounds() text = buf.get_text(start, end, True) - if text == self.PLACEHOLDER: - buf.set_text('') + return text != self.PLACEHOLDER + + def _on_focus_in(self, *args): + if not self.has_text(): + self.get_buffer().set_text('') def _on_focus_out(self, *args): buf = self.get_buffer()