From fe28b526d7a52a6523ab5a9cca54b27fd4a804fa Mon Sep 17 00:00:00 2001 From: ricky Date: Sat, 21 Nov 2015 04:00:47 -0800 Subject: [PATCH 1/7] Added spellchecked and auto-resizing height input area. --- qweechat/input.py | 41 ++++-- qweechat/inputlinespell.py | 254 +++++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 12 deletions(-) create mode 100644 qweechat/inputlinespell.py diff --git a/qweechat/input.py b/qweechat/input.py index 371b287..6d5e6f4 100644 --- a/qweechat/input.py +++ b/qweechat/input.py @@ -21,11 +21,12 @@ # import qt_compat +from inputlinespell import InputLineSpell QtCore = qt_compat.import_module('QtCore') QtGui = qt_compat.import_module('QtGui') -class InputLineEdit(QtGui.QLineEdit): +class InputLineEdit(InputLineSpell): """Input line.""" bufferSwitchPrev = qt_compat.Signal() @@ -33,11 +34,10 @@ class InputLineEdit(QtGui.QLineEdit): textSent = qt_compat.Signal(str) def __init__(self, scroll_widget): - QtGui.QLineEdit.__init__(self) + InputLineSpell.__init__(self, False) self.scroll_widget = scroll_widget self._history = [] self._history_index = -1 - self.returnPressed.connect(self._input_return_pressed) def keyPressEvent(self, event): key = event.key() @@ -49,7 +49,7 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key_PageDown: self.bufferSwitchNext.emit() else: - QtGui.QLineEdit.keyPressEvent(self, event) + InputLineSpell.keyPressEvent(self, event) elif modifiers == QtCore.Qt.AltModifier: if key in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Up): self.bufferSwitchPrev.emit() @@ -64,22 +64,35 @@ def keyPressEvent(self, event): elif key == QtCore.Qt.Key_End: bar.setValue(bar.maximum()) else: - QtGui.QLineEdit.keyPressEvent(self, event) + InputLineSpell.keyPressEvent(self, event) elif key == QtCore.Qt.Key_PageUp: bar.setValue(bar.value() - bar.pageStep()) elif key == QtCore.Qt.Key_PageDown: bar.setValue(bar.value() + bar.pageStep()) - elif key == QtCore.Qt.Key_Up: - self._history_navigate(-1) - elif key == QtCore.Qt.Key_Down: - self._history_navigate(1) + elif key == QtCore.Qt.Key_Up or key == QtCore.Qt.Key_Down: + # Compare position, optionally only nativate history if no change: + pos1 = self.textCursor().position() + InputLineSpell.keyPressEvent(self, event) + pos2 = self.textCursor().position() + if pos1 == pos2: + if key == QtCore.Qt.Key_Up: + # Add to history if there is text like curses weechat: + txt = self.toPlainText().encode('utf-8') + if txt != "" and len(self._history) == self._history_index: + self._history.append(txt) + self._history_navigate(-1) + elif key == QtCore.Qt.Key_Down: + self._history_navigate(1) + elif ((key == QtCore.Qt.Key_Enter or key == QtCore.Qt.Key_Return) + and modifiers != QtCore.Qt.ShiftModifier): + self._input_return_pressed() else: - QtGui.QLineEdit.keyPressEvent(self, event) + InputLineSpell.keyPressEvent(self, event) def _input_return_pressed(self): - self._history.append(self.text().encode('utf-8')) + self._history.append(self.toPlainText().encode('utf-8')) self._history_index = len(self._history) - self.textSent.emit(self.text()) + self.textSent.emit(self.toPlainText()) self.clear() def _history_navigate(self, direction): @@ -93,3 +106,7 @@ def _history_navigate(self, direction): self.clear() return self.setText(self._history[self._history_index]) + # End of line: + textCursor = self.textCursor() + textCursor.setPosition(len(self._history[self._history_index])) + self.setTextCursor(textCursor) diff --git a/qweechat/inputlinespell.py b/qweechat/inputlinespell.py new file mode 100644 index 0000000..80322c1 --- /dev/null +++ b/qweechat/inputlinespell.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 -*- +# +# inputlinespell.py - single line edit with spellcheck for qweechat +# +# Copyright (C) Ricky Brent +# Copyright for auto-resizing portions of code are held by Kamil ƚliwak as +# part of git@github.com:cameel/auto-resizing-text-edit.git and for +# spellcheck portions by John Schember, both under the MIT license. +# +# This file is part of QWeeChat, a Qt remote GUI for WeeChat. +# +# QWeeChat is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# QWeeChat is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with QWeeChat. If not, see . +# + +import qt_compat +QtCore = qt_compat.import_module('QtCore') +QtGui = qt_compat.import_module('QtGui') +import config +import re +import weechat.color as color + +# Spell checker support +try: + import enchant +except ImportError: + enchant = None + +class InputLineSpell(QtGui.QTextEdit): + """Chat area.""" + + def __init__(self, debug, *args): + QtGui.QTextEdit.__init__(*(self,) + args) + self.debug = debug + self.setFontFamily('monospace') + + self._textcolor = self.textColor() + self._bgcolor = QtGui.QColor('#FFFFFF') + self._setcolorcode = { + 'F': (self.setTextColor, self._textcolor), + 'B': (self.setTextBackgroundColor, self._bgcolor) + } + self._setfont = { + '*': self.setFontWeight, + '_': self.setFontUnderline, + '/': self.setFontItalic + } + self._fontvalues = { + False: { + '*': QtGui.QFont.Normal, + '_': False, + '/': False + }, + True: { + '*': QtGui.QFont.Bold, + '_': True, + '/': True + } + } + self._color = color.Color(config.color_options(), self.debug) + self.initDict() + # Set height to one line: + fm = QtGui.QFontMetrics(self.currentFont()) + self.setMinimumHeight(fm.height() + 8) + size_policy = self.sizePolicy() + size_policy.setHeightForWidth(True) + size_policy.setVerticalPolicy(QtGui.QSizePolicy.Preferred) + self.setSizePolicy(size_policy) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.textChanged.connect(lambda: self.updateGeometry()) + + def hasHeightForWidth(self): + return True + + def heightForWidth(self, width): + margins = self.contentsMargins() + + if width >= margins.left() + margins.right(): + document_width = width - margins.left() - margins.right() + else: + # If specified width can't even fit the margin, no space left. + document_width = 0 + # Cloning seems wasteful but is the preferred way to in Qt >= 4. + document = self.document().clone() + document.setTextWidth(document_width) + + return margins.top() + document.size().height() + margins.bottom() + + def sizeHint(self): + original_hint = super(InputLineSpell, self).sizeHint() + return QtCore.QSize(original_hint.width(), + self.heightForWidth(original_hint.width())) + + def scroll_bottom(self): + bar = self.verticalScrollBar() + bar.setValue(bar.maximum()) + + def initDict(self, lang=None): + if enchant: + if lang == None: + # Default dictionary based on the current locale. + try: + self.spelldict = enchant.Dict() + except enchant.DictNotFoundError: + self.spelldict = None + else: + self.spelldict = enchant.Dict(lang) + else: + self.spelldict = None + self.highlighter = SpellHighlighter(self.document()) + if self.spelldict: + self.highlighter.setDict(self.spelldict) + self.highlighter.rehighlight() + + def killDict(self): + self.highlighter.setDocument(None) + self.spelldict = None + + def mousePressEvent(self, event): + if event.button() == QtCore.Qt.RightButton: + # Rewrite the mouse event to a left button event so the cursor + # is moved to the location of the pointer. + event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, + event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, + QtCore.Qt.NoModifier) + QtGui.QTextEdit.mousePressEvent(self, event) + + def contextMenuEvent(self, event): + popup_menu = self.createStandardContextMenu() + pal = QtGui.QApplication.instance().palette() + # This fixes Issue 20 + menu_style = """ * { background-color: %s; + color: %s;} + """%(unicode(pal.color(QtGui.QPalette.Button).name()), + unicode(pal.color(QtGui.QPalette.WindowText).name())) + popup_menu.setStyleSheet(menu_style) + + # Select the word under the cursor. + cursor = self.textCursor() + cursor.select(QtGui.QTextCursor.WordUnderCursor) + self.setTextCursor(cursor) + + # Check if the selected word is misspelled and offer spelling + # suggestions if it is. + if enchant and self.spelldict: + if self.textCursor().hasSelection(): + text = unicode(self.textCursor().selectedText()) + #Add to dictionary + #Spell-checker options + if not self.spelldict.check(text): + suggestions = self.spelldict.suggest(text) + if len(suggestions) != 0: + popup_menu.insertSeparator(popup_menu.actions()[0]) + topAction = popup_menu.actions()[0] + for word in suggestions: + action = SpellAction(word, popup_menu) + action.correct.connect(self.correctWord) + popup_menu.insertAction(topAction, action) + popup_menu.insertSeparator(topAction) + add = SpellAddAction(text, popup_menu) + add.add.connect(self.addWord) + popup_menu.insertAction(topAction, add) + + # FIXME: add change dict and disable spellcheck options + + popup_menu.exec_(event.globalPos()) + + def addWord(self, word): + self.spelldict.add(word) + self.highlighter.rehighlight() + + def correctWord(self, word): + ''' + Replaces the selected text with word. + ''' + cursor = self.textCursor() + cursor.beginEditBlock() + + cursor.removeSelectedText() + cursor.insertText(word) + + cursor.endEditBlock() + + def killDict(self): + self.highlighter.setDocument(None) + self.spelldict = None + + +class SpellHighlighter(QtGui.QSyntaxHighlighter): + + WORDS = u'(?iu)[\w\']+' + + def __init__(self, *args): + QtGui.QSyntaxHighlighter.__init__(self, *args) + + self.spelldict = None + + def setDict(self, spelldict): + self.spelldict = spelldict + + def highlightBlock(self, text): + if not self.spelldict: + return + + text = unicode(text) + + format = QtGui.QTextCharFormat() + format.setUnderlineColor(QtGui.QColor('red')) + format.setUnderlineStyle(QtGui.QTextCharFormat.DotLine) + + for word_object in re.finditer(self.WORDS, text): + if not self.spelldict.check(word_object.group()): + self.setFormat(word_object.start(), + word_object.end() - word_object.start(), format) + +class SpellAction(QtGui.QAction): + + ''' + A special QAction that returns the text in a signal. + ''' + + correct = qt_compat.Signal(unicode) + + def __init__(self, *args): + QtGui.QAction.__init__(self, *args) + + self.triggered.connect(lambda x: self.correct.emit( + unicode(self.text()))) + +class SpellAddAction(QtGui.QAction): + + ''' + An action to add the given word to a dictionary. + ''' + + add = qt_compat.Signal(unicode) + + def __init__(self, word, *args): + QtGui.QAction.__init__(self, "Add to dictionary", *args) + self._word = word + self.triggered.connect(lambda x: self.add.emit( + unicode(self._word))) + From 838abdf7af08bed1abdc46fcd2765eeda0fb06c9 Mon Sep 17 00:00:00 2001 From: ricky Date: Sun, 22 Nov 2015 08:58:18 -0800 Subject: [PATCH 2/7] Fixed indentation and slight refactor of spellchecking code. --- qweechat/input.py | 4 +-- qweechat/inputlinespell.py | 53 ++++++++++++++------------------------ 2 files changed, 21 insertions(+), 36 deletions(-) diff --git a/qweechat/input.py b/qweechat/input.py index 6d5e6f4..c5eda9c 100644 --- a/qweechat/input.py +++ b/qweechat/input.py @@ -43,6 +43,7 @@ def keyPressEvent(self, event): key = event.key() modifiers = event.modifiers() bar = self.scroll_widget.verticalScrollBar() + newline = (key == QtCore.Qt.Key_Enter or key == QtCore.Qt.Key_Return) if modifiers == QtCore.Qt.ControlModifier: if key == QtCore.Qt.Key_PageUp: self.bufferSwitchPrev.emit() @@ -83,8 +84,7 @@ def keyPressEvent(self, event): self._history_navigate(-1) elif key == QtCore.Qt.Key_Down: self._history_navigate(1) - elif ((key == QtCore.Qt.Key_Enter or key == QtCore.Qt.Key_Return) - and modifiers != QtCore.Qt.ShiftModifier): + elif (newline and modifiers != QtCore.Qt.ShiftModifier): self._input_return_pressed() else: InputLineSpell.keyPressEvent(self, event) diff --git a/qweechat/inputlinespell.py b/qweechat/inputlinespell.py index 80322c1..8ffdb43 100644 --- a/qweechat/inputlinespell.py +++ b/qweechat/inputlinespell.py @@ -36,6 +36,7 @@ except ImportError: enchant = None + class InputLineSpell(QtGui.QTextEdit): """Chat area.""" @@ -100,7 +101,7 @@ def heightForWidth(self, width): def sizeHint(self): original_hint = super(InputLineSpell, self).sizeHint() return QtCore.QSize(original_hint.width(), - self.heightForWidth(original_hint.width())) + self.heightForWidth(original_hint.width())) def scroll_bottom(self): bar = self.verticalScrollBar() @@ -108,7 +109,7 @@ def scroll_bottom(self): def initDict(self, lang=None): if enchant: - if lang == None: + if lang is None: # Default dictionary based on the current locale. try: self.spelldict = enchant.Dict() @@ -132,8 +133,9 @@ def mousePressEvent(self, event): # Rewrite the mouse event to a left button event so the cursor # is moved to the location of the pointer. event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, - event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, - QtCore.Qt.NoModifier) + event.pos(), QtCore.Qt.LeftButton, + QtCore.Qt.LeftButton, + QtCore.Qt.NoModifier) QtGui.QTextEdit.mousePressEvent(self, event) def contextMenuEvent(self, event): @@ -142,8 +144,8 @@ def contextMenuEvent(self, event): # This fixes Issue 20 menu_style = """ * { background-color: %s; color: %s;} - """%(unicode(pal.color(QtGui.QPalette.Button).name()), - unicode(pal.color(QtGui.QPalette.WindowText).name())) + """ % (unicode(pal.color(QtGui.QPalette.Button).name()), + unicode(pal.color(QtGui.QPalette.WindowText).name())) popup_menu.setStyleSheet(menu_style) # Select the word under the cursor. @@ -156,8 +158,6 @@ def contextMenuEvent(self, event): if enchant and self.spelldict: if self.textCursor().hasSelection(): text = unicode(self.textCursor().selectedText()) - #Add to dictionary - #Spell-checker options if not self.spelldict.check(text): suggestions = self.spelldict.suggest(text) if len(suggestions) != 0: @@ -167,12 +167,15 @@ def contextMenuEvent(self, event): action = SpellAction(word, popup_menu) action.correct.connect(self.correctWord) popup_menu.insertAction(topAction, action) - popup_menu.insertSeparator(topAction) - add = SpellAddAction(text, popup_menu) - add.add.connect(self.addWord) - popup_menu.insertAction(topAction, add) - # FIXME: add change dict and disable spellcheck options + popup_menu.insertSeparator(topAction) + addAction = QtGui.QAction("Add to dictionary", self) + addAction.triggered.connect(lambda: self.addWord(text)) + popup_menu.insertAction(topAction, addAction) + # FIXME: add change dict and disable spellcheck options + # spellmenu = QtGui.QMenu('Spell-checker options') + # for lang in enchant.list_languages(): + # popup_menu.insertMenu(topAction, spellmenu) popup_menu.exec_(event.globalPos()) @@ -192,10 +195,6 @@ def correctWord(self, word): cursor.endEditBlock() - def killDict(self): - self.highlighter.setDocument(None) - self.spelldict = None - class SpellHighlighter(QtGui.QSyntaxHighlighter): @@ -221,8 +220,9 @@ def highlightBlock(self, text): for word_object in re.finditer(self.WORDS, text): if not self.spelldict.check(word_object.group()): - self.setFormat(word_object.start(), - word_object.end() - word_object.start(), format) + word_len = word_object.end() - word_object.start() + self.setFormat(word_object.start(), word_len, format) + class SpellAction(QtGui.QAction): @@ -237,18 +237,3 @@ def __init__(self, *args): self.triggered.connect(lambda x: self.correct.emit( unicode(self.text()))) - -class SpellAddAction(QtGui.QAction): - - ''' - An action to add the given word to a dictionary. - ''' - - add = qt_compat.Signal(unicode) - - def __init__(self, word, *args): - QtGui.QAction.__init__(self, "Add to dictionary", *args) - self._word = word - self.triggered.connect(lambda x: self.add.emit( - unicode(self._word))) - From 4202c2ba4246e62f73e0af18c8f6f37a7e716b9f Mon Sep 17 00:00:00 2001 From: Ricky Date: Sun, 7 Aug 2016 04:06:21 -0700 Subject: [PATCH 3/7] Style fixes, fixed pyside/pyqt signal difference, added change language/disable. --- qweechat/input.py | 22 +++++------ qweechat/inputlinespell.py | 81 ++++++++++++++++++++++++++------------ 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/qweechat/input.py b/qweechat/input.py index 25c1592..9089a27 100644 --- a/qweechat/input.py +++ b/qweechat/input.py @@ -43,7 +43,7 @@ def __init__(self, scroll_widget): def keyPressEvent(self, event): key = event.key() modifiers = event.modifiers() - bar = self.scroll_widget.verticalScrollBar() + scroll = self.scroll_widget.verticalScrollBar() newline = (key == QtCore.Qt.Key_Enter or key == QtCore.Qt.Key_Return) if modifiers == QtCore.Qt.ControlModifier: if key == QtCore.Qt.Key_PageUp: @@ -58,19 +58,19 @@ def keyPressEvent(self, event): elif key in (QtCore.Qt.Key_Right, QtCore.Qt.Key_Down): self.bufferSwitchNext.emit() elif key == QtCore.Qt.Key_PageUp: - bar.setValue(bar.value() - (bar.pageStep() / 10)) + scroll.setValue(scroll.value() - (scroll.pageStep() / 10)) elif key == QtCore.Qt.Key_PageDown: - bar.setValue(bar.value() + (bar.pageStep() / 10)) + scroll.setValue(scroll.value() + (scroll.pageStep() / 10)) elif key == QtCore.Qt.Key_Home: - bar.setValue(bar.minimum()) + scroll.setValue(scroll.minimum()) elif key == QtCore.Qt.Key_End: - bar.setValue(bar.maximum()) + scroll.setValue(scroll.maximum()) else: InputLineSpell.keyPressEvent(self, event) elif key == QtCore.Qt.Key_PageUp: - bar.setValue(bar.value() - bar.pageStep()) + scroll.setValue(scroll.value() - scroll.pageStep()) elif key == QtCore.Qt.Key_PageDown: - bar.setValue(bar.value() + bar.pageStep()) + scroll.setValue(scroll.value() + scroll.pageStep()) elif key == QtCore.Qt.Key_Up or key == QtCore.Qt.Key_Down: # Compare position, optionally only nativate history if no change: pos1 = self.textCursor().position() @@ -85,7 +85,7 @@ def keyPressEvent(self, event): self._history_navigate(-1) elif key == QtCore.Qt.Key_Down: self._history_navigate(1) - elif (newline and modifiers != QtCore.Qt.ShiftModifier): + elif newline and modifiers != QtCore.Qt.ShiftModifier: self._input_return_pressed() else: InputLineSpell.keyPressEvent(self, event) @@ -108,6 +108,6 @@ def _history_navigate(self, direction): return self.setText(self._history[self._history_index]) # End of line: - textCursor = self.textCursor() - textCursor.setPosition(len(self._history[self._history_index])) - self.setTextCursor(textCursor) + text_cursor = self.textCursor() + text_cursor.setPosition(len(self._history[self._history_index])) + self.setTextCursor(text_cursor) diff --git a/qweechat/inputlinespell.py b/qweechat/inputlinespell.py index ff8c80b..53a4920 100644 --- a/qweechat/inputlinespell.py +++ b/qweechat/inputlinespell.py @@ -22,13 +22,10 @@ # You should have received a copy of the GNU General Public License # along with QWeeChat. If not, see . # - -import qt_compat -QtCore = qt_compat.import_module('QtCore') -QtGui = qt_compat.import_module('QtGui') -import config import functools import re +import config +import qt_compat import weechat.color as color # Spell checker support @@ -36,6 +33,8 @@ import enchant except ImportError: enchant = None +QtCore = qt_compat.import_module('QtCore') +QtGui = qt_compat.import_module('QtGui') class InputLineSpell(QtGui.QTextEdit): @@ -72,8 +71,8 @@ def __init__(self, debug, *args): self._color = color.Color(config.color_options(), self.debug) self.initDict() # Set height to one line: - fm = QtGui.QFontMetrics(self.currentFont()) - self.setMinimumHeight(fm.height() + 8) + font_metric = QtGui.QFontMetrics(self.currentFont()) + self.setMinimumHeight(font_metric.height() + 8) size_policy = self.sizePolicy() size_policy.setHeightForWidth(True) size_policy.setVerticalPolicy(QtGui.QSizePolicy.Preferred) @@ -82,7 +81,8 @@ def __init__(self, debug, *args): self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.textChanged.connect(lambda: self.updateGeometry()) - def hasHeightForWidth(self): + @staticmethod + def hasHeightForWidth(): return True def heightForWidth(self, width): @@ -105,8 +105,8 @@ def sizeHint(self): self.heightForWidth(original_hint.width())) def scroll_bottom(self): - bar = self.verticalScrollBar() - bar.setValue(bar.maximum()) + scroll = self.verticalScrollBar() + scroll.setValue(scroll.maximum()) def initDict(self, lang=None): if enchant: @@ -125,6 +125,12 @@ def initDict(self, lang=None): self.highlighter.setDict(self.spelldict) self.highlighter.rehighlight() + def toggleDict(self, label=None): + if self.spelldict: + self.killDict() + else: + self.initDict() + def killDict(self): self.highlighter.setDocument(None) self.spelldict = None @@ -164,22 +170,47 @@ def contextMenuEvent(self, event): if len(suggestions) != 0: popup_menu.insertSeparator(popup_menu.actions()[0]) - topAction = popup_menu.actions()[0] - for suggestion in suggestions: - action = QtGui.QAction(suggestion, popup_menu) - action.connect(action, QtCore.SIGNAL("triggered()"), functools.partial(self.correctWord, word = suggestion)) - popup_menu.insertAction(topAction, action) - popup_menu.insertSeparator(topAction) - addAction = QtGui.QAction("Add to dictionary", self) - addAction.triggered.connect(lambda: self.addWord(text)) - popup_menu.insertAction(topAction, addAction) - # FIXME: add change dict and disable spellcheck options - # spellmenu = QtGui.QMenu('Spell-checker options') - # for lang in enchant.list_languages(): - # popup_menu.insertMenu(topAction, spellmenu) - + top_action = popup_menu.actions()[0] + for suggest in suggestions: + self._menu_action(suggest, popup_menu, self.correctWord, after=top_action) + popup_menu.insertSeparator(top_action) + add_action = QtGui.QAction("Add to dictionary", self) + add_action.triggered.connect(lambda: self.addWord(text)) + popup_menu.insertAction(top_action, add_action) + # FIXME: disable spellcheck option + spell_menu = QtGui.QMenu(popup_menu) + spell_menu.setTitle('Spellcheck') + popup_menu.insertMenu(top_action, spell_menu) + for lang in enchant.list_languages(): + self._menu_action(lang, spell_menu, self.initDict, + checked=(lang == self.spelldict.tag)) + toggle = self._menu_action('Check the spelling', spell_menu, + self.toggleDict, + checked=(self.spelldict != False)) + spell_menu.insertSeparator(toggle) + elif enchant: + toggle = self._menu_action('Check the spelling', popup_menu, + self.toggleDict, checked=False) + popup_menu.insertSeparator(toggle) popup_menu.exec_(event.globalPos()) + @staticmethod + def _menu_action(text, menu, method, after=None, checked=None): + action = QtGui.QAction(text, menu) + action.connect( + action, + QtCore.SIGNAL("triggered()"), + functools.partial(method, text) + ) + if checked is not None: + action.setCheckable(True) + action.setChecked(checked) + if after is not None: + menu.insertAction(after, action) + else: + menu.addAction(action) + return action + def addWord(self, word): self.spelldict.add(word) self.highlighter.rehighlight() @@ -199,7 +230,7 @@ def correctWord(self, word): class SpellHighlighter(QtGui.QSyntaxHighlighter): - WORDS = u'(?iu)[\w\']+' + WORDS = r'(?iu)[\w\']+' def __init__(self, *args): QtGui.QSyntaxHighlighter.__init__(self, *args) From 0ea1c66f4ebfe3d8b8a2fab9a0c341d4f97c67aa Mon Sep 17 00:00:00 2001 From: Ricky Date: Sun, 7 Aug 2016 04:24:23 -0700 Subject: [PATCH 4/7] Style changes and fixes. --- qweechat/inputlinespell.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/qweechat/inputlinespell.py b/qweechat/inputlinespell.py index 53a4920..25ca311 100644 --- a/qweechat/inputlinespell.py +++ b/qweechat/inputlinespell.py @@ -147,13 +147,6 @@ def mousePressEvent(self, event): def contextMenuEvent(self, event): popup_menu = self.createStandardContextMenu() - pal = QtGui.QApplication.instance().palette() - # This fixes Issue 20 - menu_style = """ * { background-color: %s; - color: %s;} - """ % (unicode(pal.color(QtGui.QPalette.Button).name()), - unicode(pal.color(QtGui.QPalette.WindowText).name())) - popup_menu.setStyleSheet(menu_style) # Select the word under the cursor. cursor = self.textCursor() @@ -169,27 +162,26 @@ def contextMenuEvent(self, event): suggestions = self.spelldict.suggest(text) if len(suggestions) != 0: popup_menu.insertSeparator(popup_menu.actions()[0]) - top_action = popup_menu.actions()[0] for suggest in suggestions: - self._menu_action(suggest, popup_menu, self.correctWord, after=top_action) + self._menu_action(suggest, popup_menu, + self.correctWord, after=top_action) popup_menu.insertSeparator(top_action) add_action = QtGui.QAction("Add to dictionary", self) add_action.triggered.connect(lambda: self.addWord(text)) popup_menu.insertAction(top_action, add_action) - # FIXME: disable spellcheck option spell_menu = QtGui.QMenu(popup_menu) spell_menu.setTitle('Spellcheck') popup_menu.insertMenu(top_action, spell_menu) for lang in enchant.list_languages(): self._menu_action(lang, spell_menu, self.initDict, checked=(lang == self.spelldict.tag)) - toggle = self._menu_action('Check the spelling', spell_menu, + toggle = self._menu_action('Check spelling', spell_menu, self.toggleDict, - checked=(self.spelldict != False)) + checked=(self.spelldict is not False)) spell_menu.insertSeparator(toggle) elif enchant: - toggle = self._menu_action('Check the spelling', popup_menu, + toggle = self._menu_action('Check spelling', popup_menu, self.toggleDict, checked=False) popup_menu.insertSeparator(toggle) popup_menu.exec_(event.globalPos()) From c1e3bb9c8271684d1b92b37868438380b62ccd92 Mon Sep 17 00:00:00 2001 From: Ricky Date: Sun, 7 Aug 2016 04:31:43 -0700 Subject: [PATCH 5/7] Fixed context menu not appearing when no words were mispelled. --- qweechat/inputlinespell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qweechat/inputlinespell.py b/qweechat/inputlinespell.py index 25ca311..0b05bc2 100644 --- a/qweechat/inputlinespell.py +++ b/qweechat/inputlinespell.py @@ -156,13 +156,13 @@ def contextMenuEvent(self, event): # Check if the selected word is misspelled and offer spelling # suggestions if it is. if enchant and self.spelldict: + top_action = popup_menu.actions()[0] if self.textCursor().hasSelection(): text = unicode(self.textCursor().selectedText()) if not self.spelldict.check(text): suggestions = self.spelldict.suggest(text) if len(suggestions) != 0: popup_menu.insertSeparator(popup_menu.actions()[0]) - top_action = popup_menu.actions()[0] for suggest in suggestions: self._menu_action(suggest, popup_menu, self.correctWord, after=top_action) From 936f48f167a293bc20fb618fd7f53bba46c66b25 Mon Sep 17 00:00:00 2001 From: Ricky Brent Date: Mon, 14 Nov 2016 11:28:42 -0800 Subject: [PATCH 6/7] Added pyenchant to the readme as an optional dependency --- README.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.adoc b/README.adoc index f669bc0..378e5bf 100644 --- a/README.adoc +++ b/README.adoc @@ -28,6 +28,10 @@ Following packages are *required*: * Python 2.x >= 2.6 * PySide (recommended, packages: python.pyside.*) or PyQt4 (python-qt4) +Following packages are *optional*: + +* PyEnchant (for spell check) + === Install via source distribution ---- From 22ec16f63b2fa82b5a2e76a9e2349a8ff5bea72b Mon Sep 17 00:00:00 2001 From: Ricky Brent Date: Wed, 30 Nov 2016 17:02:02 -0800 Subject: [PATCH 7/7] Fixed disabling spellcheck failing to disable spellcheck. --- qweechat/inputlinespell.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/qweechat/inputlinespell.py b/qweechat/inputlinespell.py index 0b05bc2..62f82cb 100644 --- a/qweechat/inputlinespell.py +++ b/qweechat/inputlinespell.py @@ -133,6 +133,7 @@ def toggleDict(self, label=None): def killDict(self): self.highlighter.setDocument(None) + self.highlighter.setDict(None) self.spelldict = None def mousePressEvent(self, event): @@ -219,6 +220,12 @@ def correctWord(self, word): cursor.endEditBlock() + @staticmethod + def list_languages(): + if enchant: + return enchant.list_languages() + return [] + class SpellHighlighter(QtGui.QSyntaxHighlighter): @@ -228,6 +235,9 @@ def __init__(self, *args): QtGui.QSyntaxHighlighter.__init__(self, *args) self.spelldict = None + self._mispelled = QtGui.QTextCharFormat() + self._mispelled.setUnderlineColor(QtGui.QColor('red')) + self._mispelled.setUnderlineStyle(QtGui.QTextCharFormat.DotLine) def setDict(self, spelldict): self.spelldict = spelldict @@ -238,11 +248,8 @@ def highlightBlock(self, text): text = unicode(text) - format = QtGui.QTextCharFormat() - format.setUnderlineColor(QtGui.QColor('red')) - format.setUnderlineStyle(QtGui.QTextCharFormat.DotLine) - for word_object in re.finditer(self.WORDS, text): if not self.spelldict.check(word_object.group()): word_len = word_object.end() - word_object.start() - self.setFormat(word_object.start(), word_len, format) + self.setFormat(word_object.start(), + word_len, self._mispelled)