summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlad Voicu <vladvoic@gmail.com>2011-11-28 16:37:32 (GMT)
committerKevin Smith <git@kismith.co.uk>2012-03-09 15:03:59 (GMT)
commitb8f10af29258705b4610e99ba69f5ecab42ef6ae (patch)
tree9f182e47e07b5cfda70079a8e1ac9a80eaa981f1
parent0833f7da453db9cd0fc3a78c793e7532663ab86b (diff)
downloadswift-contrib-b8f10af29258705b4610e99ba69f5ecab42ef6ae.zip
swift-contrib-b8f10af29258705b4610e99ba69f5ecab42ef6ae.tar.bz2
Initial, buggy version of a minimal spell-checker
-rw-r--r--SwifTools/SConscript3
-rw-r--r--SwifTools/SpellChecker.cpp52
-rw-r--r--SwifTools/SpellChecker.h25
-rw-r--r--Swift/QtUI/QtTextEdit.cpp67
-rw-r--r--Swift/QtUI/QtTextEdit.h10
5 files changed, 153 insertions, 4 deletions
diff --git a/SwifTools/SConscript b/SwifTools/SConscript
index e5085cc..3e1a105 100644
--- a/SwifTools/SConscript
+++ b/SwifTools/SConscript
@@ -7,7 +7,7 @@ Import("env")
if env["SCONS_STAGE"] == "flags" :
env["SWIFTOOLS_FLAGS"] = {
"LIBPATH": [Dir(".")],
- "LIBS": ["SwifTools"]
+ "LIBS": ["SwifTools", "libaspell"]
}
################################################################################
@@ -28,6 +28,7 @@ if env["SCONS_STAGE"] == "build" :
"Linkify.cpp",
"TabComplete.cpp",
"LastLineTracker.cpp",
+ "SpellChecker.cpp",
]
if swiftools_env.get("HAVE_SPARKLE", 0) :
diff --git a/SwifTools/SpellChecker.cpp b/SwifTools/SpellChecker.cpp
new file mode 100644
index 0000000..8c9aeda
--- /dev/null
+++ b/SwifTools/SpellChecker.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011 Voicu Vlad
+ * Licensed under the Simplified BSD license.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <SwifTools/SpellChecker.h>
+
+#include <algorithm>
+#include <aspell.h>
+#include <boost/algorithm/string.hpp>
+
+
+namespace Swift {
+
+SpellChecker::SpellChecker() {
+ AspellCanHaveError* ret;
+ config_ = new_aspell_config();
+ ret = new_aspell_speller(config_);
+ if (aspell_error(ret) != 0) {
+ //TODO(vladv): Proper log the error
+ //printf("Error: %s\n",aspell_error_message(ret));
+ delete_aspell_can_have_error(ret);
+ }
+ speller_ = to_aspell_speller(ret);
+}
+
+bool SpellChecker::isCorrect(const std::string& word) {
+ if (!word.empty()) {
+ int aspell_error = aspell_speller_check(speller_, word.c_str(), -1);
+ if (aspell_error == 1) {
+ return true;
+ } else {
+ //TODO(vladv): Proper log the error
+ }
+ }
+ return false;
+}
+
+void SpellChecker::getSuggestions(const std::string& word, std::vector<std::string>& list) {
+ const AspellWordList *alist;
+ if (!word.empty()) {
+ alist = aspell_speller_suggest(speller_, word.c_str(), -1);
+ AspellStringEnumeration *els = aspell_word_list_elements(alist);
+ const char* aword;
+ while (((aword = aspell_string_enumeration_next(els)) != 0) && list.size() <= 5) {
+ list.push_back(std::string(aword));
+ }
+ }
+}
+
+}
diff --git a/SwifTools/SpellChecker.h b/SwifTools/SpellChecker.h
new file mode 100644
index 0000000..3a9ff38
--- /dev/null
+++ b/SwifTools/SpellChecker.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Voicu Vlad
+ * Licensed under the Simplified BSD license.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <vector>
+#include <boost/algorithm/string.hpp>
+
+#pragma once
+
+class AspellSpeller;
+class AspellConfig;
+
+namespace Swift {
+ class SpellChecker {
+ public:
+ SpellChecker();
+ bool isCorrect(const std::string& word);
+ void getSuggestions(const std::string& word, std::vector<std::string>& list);
+ private:
+ AspellSpeller* speller_;
+ AspellConfig* config_;
+ };
+}
diff --git a/Swift/QtUI/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp
index 3a62325..e1fdd26 100644
--- a/Swift/QtUI/QtTextEdit.cpp
+++ b/Swift/QtUI/QtTextEdit.cpp
@@ -4,18 +4,28 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
+#include <SwifTools/SpellChecker.h>
+
#include <Swift/QtUI/QtTextEdit.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
#include <QFontMetrics>
#include <QKeyEvent>
+#include <QDebug>
+#include <QMenu>
namespace Swift {
QtTextEdit::QtTextEdit(QWidget* parent) : QTextEdit(parent){
connect(this, SIGNAL(textChanged()), this, SLOT(handleTextChanged()));
handleTextChanged();
+ checker_ = new SpellChecker();
};
+QtTextEdit::~QtTextEdit() {
+ delete checker_;
+}
+
void QtTextEdit::keyPressEvent(QKeyEvent* event) {
int key = event->key();
Qt::KeyboardModifiers modifiers = event->modifiers();
@@ -35,13 +45,37 @@ void QtTextEdit::keyPressEvent(QKeyEvent* event) {
emit unhandledKeyPressEvent(event);
}
else if ((key == Qt::Key_Up)
- || (key == Qt::Key_Down)
- ){
+ || (key == Qt::Key_Down)) {
emit unhandledKeyPressEvent(event);
QTextEdit::keyPressEvent(event);
}
else {
QTextEdit::keyPressEvent(event);
+ underlineMisspells();
+ }
+}
+
+void QtTextEdit::underlineMisspells() {
+ QTextCharFormat spellingErrorFormat;
+ QTextCharFormat normalFormat;
+ spellingErrorFormat.setUnderlineColor(QColor(Qt::red));
+ spellingErrorFormat.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline);
+ QTextCursor cursor = textCursor();
+ QString word;
+ cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 1);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor, 1);
+ word = cursor.selectedText();
+ // Qt handles punctuation as words. Workaround that.
+ while (!word.isEmpty() && !word.at(0).isLetter()) {
+ cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor, 1);
+ word = cursor.selectedText();
+ }
+ if ((!word.isEmpty()) && !checker_->isCorrect(word.toStdString())) {
+ cursor.mergeCharFormat(spellingErrorFormat);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor, 1);
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor, 1);
+ cursor.setCharFormat(normalFormat);
}
}
@@ -53,6 +87,14 @@ void QtTextEdit::handleTextChanged() {
}
}
+void QtTextEdit::handleReplaceMisspellWord(const QString& word, const QPoint& position) {
+ QTextCursor cursor = cursorForPosition(position);
+ cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 1);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor, 1);
+ QTextCharFormat normalFormat;
+ cursor.insertText(word, normalFormat);
+}
+
QSize QtTextEdit::sizeHint() const {
QFontMetrics inputMetrics(currentFont());
QRect horizontalBounds = contentsRect().adjusted(0,0,0,9999);
@@ -66,7 +108,26 @@ QSize QtTextEdit::sizeHint() const {
//return QSize(QTextEdit::sizeHint().width(), lineHeight * numberOfLines);
}
-}
+void QtTextEdit::contextMenuEvent(QContextMenuEvent* event) {
+ QTextCursor cursor = cursorForPosition(event->pos());
+ cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 1);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor, 1);
+ std::vector<std::string> wordList;
+ checker_->getSuggestions(Q2PSTRING(cursor.selectedText()), wordList);
+ std::vector<QAction*> replaceWordActions;
+ QMenu *menu = createStandardContextMenu();
+ for (std::vector<std::string>::iterator it = wordList.begin(); it != wordList.end(); ++it) {
+ replaceWordActions.push_back(menu->addAction(tr(it->c_str())));
+ }
+ QAction* result = menu->exec(event->globalPos());
+ for (std::vector<QAction*>::iterator it = replaceWordActions.begin(); it != replaceWordActions.end(); ++it) {
+ if (*it == result) {
+ handleReplaceMisspellWord((*it)->text(), event->pos());
+ }
+ }
+ delete menu;
+}
+}
diff --git a/Swift/QtUI/QtTextEdit.h b/Swift/QtUI/QtTextEdit.h
index 075728b..a940879 100644
--- a/Swift/QtUI/QtTextEdit.h
+++ b/Swift/QtUI/QtTextEdit.h
@@ -5,20 +5,30 @@
*/
#pragma once
+
#include <QTextEdit>
namespace Swift {
+ class SpellChecker;
class QtTextEdit : public QTextEdit {
Q_OBJECT
public:
QtTextEdit(QWidget* parent = 0);
+ virtual ~QtTextEdit();
virtual QSize sizeHint() const;
signals:
+ void wordCorrected(QString& word);
void returnPressed();
void unhandledKeyPressEvent(QKeyEvent* event);
+ public slots:
+ void handleReplaceMisspellWord(const QString& word, const QPoint& position);
protected:
virtual void keyPressEvent(QKeyEvent* event);
+ virtual void contextMenuEvent(QContextMenuEvent* event);
private slots:
void handleTextChanged();
+ private:
+ SpellChecker *checker_;
+ void underlineMisspells();
};
}