Refactor Speller

This commit is contained in:
Philipp Hörist 2017-10-19 11:26:22 +02:00
parent 970d6f8c3f
commit 3c103315ec
7 changed files with 111 additions and 104 deletions

View File

@ -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

View File

@ -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):
"""

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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')

View File

@ -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()