diff options
Diffstat (limited to 'src/webtab/webview.cpp')
-rw-r--r-- | src/webtab/webview.cpp | 249 |
1 files changed, 229 insertions, 20 deletions
diff --git a/src/webtab/webview.cpp b/src/webtab/webview.cpp index 06412198..5cb5e81d 100644 --- a/src/webtab/webview.cpp +++ b/src/webtab/webview.cpp @@ -54,6 +54,10 @@ #include <KStandardDirs> #include <KToolInvocation> +#include <sonnet/speller.h> +#include <Sonnet/Dialog> +#include <sonnet/backgroundchecker.h> + // Qt Includes #include <QFile> #include <QTimer> @@ -68,6 +72,19 @@ #include <QNetworkRequest> +// needed for the spellCheck +static QVariant execJScript(QWebHitTestResult result, const QString& script) +{ + QWebElement element(result.element()); + if (element.isNull()) + return QVariant(); + return element.evaluateJavaScript(script); +} + + +// -------------------------------------------------------------------------------------------------- + + WebView::WebView(QWidget* parent) : KWebView(parent, false) , m_autoScrollTimer(new QTimer(this)) @@ -141,9 +158,103 @@ WebPage *WebView::page() } +bool WebView::popupSpellMenu(QContextMenuEvent *event) +{ + // return false if not handled + if (!ReKonfig::automaticSpellChecking()) + return false; + + QWebElement element(m_contextMenuHitResult.element()); + if (element.isNull()) + return false; + + int selStart = element.evaluateJavaScript("this.selectionStart").toInt(); + int selEnd = element.evaluateJavaScript("this.selectionEnd").toInt(); + if (selEnd != selStart) + return false; // selection, handle normally + + // No selection - Spell Checking only + // Get word + QString text = element.evaluateJavaScript("this.value").toString(); + QRegExp ws("\\b"); + int s1 = text.lastIndexOf(ws, selStart); + int s2 = text.indexOf(ws, selStart); + QString word = text.mid(s1, s2 - s1).trimmed(); + + // sanity check + if (word.isEmpty()) + return false; + + kDebug() << s1 << ":" << s2 << ":" << word << ":"; + Sonnet::Speller spellor; + if (spellor.isCorrect(word)) + return false; // no need to popup spell menu + + // find alternates + QStringList words = spellor.suggest(word); + + // Construct popup menu + QMenu mnu(this); + + // Add alternates + if (words.isEmpty()) + { + QAction *a = mnu.addAction(i18n("No suggestions for ") + word); + a->setEnabled(false); + } + else + { + QStringListIterator it(words); + while (it.hasNext()) + { + QString w = it.next(); + QAction *aWord = mnu.addAction(w); + aWord->setData(w); + } + } + + // Add dictionary options + mnu.addSeparator(); + QAction *aIgnore = mnu.addAction(i18n("Ignore")); + QAction *aAddToDict = mnu.addAction(i18n("Add to Dictionary")); + + QAction *aSpellChoice = mnu.exec(event->globalPos()); + if (aSpellChoice) + { + if (aSpellChoice == aAddToDict) + spellor.addToPersonal(word); + else if (aSpellChoice == aIgnore) + { + // Ignore :) + } + else + { + // Choose a replacement word + QString w = aSpellChoice->data().toString(); + if (!w.isEmpty()) + { + // replace word + QString script(QL1S("this.value=this.value.substring(0,")); + script += QString::number(s1); + script += QL1S(") + \""); + script += w; + script += QL1S("\" + this.value.substring("); + script += QString::number(s2); + script += QL1S(")"); + element.evaluateJavaScript(script); + // reposition cursor + element.evaluateJavaScript("this.selectionEnd=this.selectionStart=" + QString::number(selStart) + ";"); + } + } + } + + return true; +} + + void WebView::contextMenuEvent(QContextMenuEvent *event) { - QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos()); + m_contextMenuHitResult = page()->mainFrame()->hitTestContent(event->pos()); WebTab *tab = qobject_cast<WebTab *>(parent()); WebWindow *webwin = tab->webWindow(); @@ -159,23 +270,27 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) // Choose right context int resultHit = 0; - if (result.linkUrl().isEmpty()) + if (m_contextMenuHitResult.linkUrl().isEmpty()) resultHit = WebView::EmptySelection; else resultHit = WebView::LinkSelection; - if (!result.pixmap().isNull()) + if (!m_contextMenuHitResult.pixmap().isNull()) resultHit |= WebView::ImageSelection; - if (result.isContentSelected()) + if (m_contextMenuHitResult.isContentSelected()) resultHit = WebView::TextSelection; // -------------------------------------------------------------------------------- // Ok, let's start filling up the menu... // is content editable? Add PASTE - if (result.isContentEditable()) + if (m_contextMenuHitResult.isContentEditable()) { + // Check to see if handled by speller + if (popupSpellMenu(event)) + return; + menu.addAction(pageAction(KWebPage::Paste)); menu.addSeparator(); } @@ -241,16 +356,16 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) if (resultHit & WebView::LinkSelection) { // send by mail: link url - sendByMailAction->setData(result.linkUrl()); + sendByMailAction->setData(m_contextMenuHitResult.linkUrl()); sendByMailAction->setText(i18n("Share link")); a = new KAction(KIcon("tab-new"), i18n("Open in New &Tab"), this); - a->setData(result.linkUrl()); + a->setData(m_contextMenuHitResult.linkUrl()); connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewTab())); menu.addAction(a); a = new KAction(KIcon("window-new"), i18n("Open in New &Window"), this); - a->setData(result.linkUrl()); + a->setData(m_contextMenuHitResult.linkUrl()); connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow())); menu.addAction(a); @@ -271,13 +386,13 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) if (resultHit & WebView::ImageSelection) { // send by mail: image url - sendByMailAction->setData(result.imageUrl()); + sendByMailAction->setData(m_contextMenuHitResult.imageUrl()); sendByMailAction->setText(i18n("Share image link")); menu.addSeparator(); a = new KAction(KIcon("view-preview"), i18n("&View Image"), this); - a->setData(result.imageUrl()); + a->setData(m_contextMenuHitResult.imageUrl()); connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(viewImage(Qt::MouseButtons, Qt::KeyboardModifiers))); menu.addAction(a); @@ -285,14 +400,14 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) menu.addAction(pageAction(KWebPage::DownloadImageToDisk)); a = new KAction(KIcon("view-media-visualization"), i18n("&Copy Image Location"), this); - a->setData(result.imageUrl()); + a->setData(m_contextMenuHitResult.imageUrl()); connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(slotCopyImageLocation())); menu.addAction(a); if (AdBlockManager::self()->isEnabled()) { a = new KAction(KIcon("preferences-web-browser-adblock"), i18n("Block image"), this); - a->setData(result.imageUrl()); + a->setData(m_contextMenuHitResult.imageUrl()); connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(blockImage())); menu.addAction(a); } @@ -305,22 +420,22 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) sendByMailAction->setData(selectedText()); sendByMailAction->setText(i18n("Share selected text")); - if (result.isContentEditable()) + if (m_contextMenuHitResult.isContentEditable()) { // actions for text selected in field menu.addAction(pageAction(KWebPage::Cut)); } a = pageAction(KWebPage::Copy); - if (!result.linkUrl().isEmpty()) + if (!m_contextMenuHitResult.linkUrl().isEmpty()) a->setText(i18n("Copy Text")); //for link else a->setText(i18n("Copy")); menu.addAction(a); if (selectedText().contains('.') - && selectedText().indexOf('.') < selectedText().length() - && !selectedText().trimmed().contains(" ") + && selectedText().indexOf('.') < selectedText().length() + && !selectedText().trimmed().contains(" ") ) { QString text = selectedText(); @@ -336,13 +451,16 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) truncatedUrl.truncate(truncateSize); truncatedUrl += QL1S("..."); } - //open selected text url in a new tab - QAction * const openInNewTabAction = new KAction(KIcon("tab-new"), i18n("Open '%1' in New Tab", truncatedUrl), this); + + // open selected text url in a new tab + QAction * const openInNewTabAction = new KAction(KIcon("tab-new"), + i18n("Open '%1' in New Tab", truncatedUrl), this); openInNewTabAction->setData(QUrl(urlLikeText)); connect(openInNewTabAction, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewTab())); menu.addAction(openInNewTabAction); //open selected text url in a new window - QAction * const openInNewWindowAction = new KAction(KIcon("window-new"), i18n("Open '%1' in New Window", truncatedUrl), this); + QAction * const openInNewWindowAction = new KAction(KIcon("window-new"), + i18n("Open '%1' in New Window", truncatedUrl), this); openInNewWindowAction->setData(QUrl(urlLikeText)); connect(openInNewWindowAction, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow())); menu.addAction(openInNewWindowAction); @@ -388,7 +506,7 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) if (resultHit & WebView::LinkSelection) { a = new KAction(KIcon("bookmark-new"), i18n("&Bookmark link"), this); - a->setData(result.linkUrl()); + a->setData(m_contextMenuHitResult.linkUrl()); connect(a, SIGNAL(triggered(bool)), this, SLOT(bookmarkLink())); menu.addAction(a); } @@ -400,6 +518,14 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) menu.addAction(sendByMailAction); menu.addAction(inspectAction); + // SPELL CHECK Actions + if (m_contextMenuHitResult.isContentEditable()) + { + menu.addSeparator(); + a = KStandardAction::spelling(this, SLOT(spellCheck()), this); + menu.addAction(a); + } + // finally launch the menu... menu.exec(mapToGlobal(event->pos())); } @@ -1256,3 +1382,86 @@ void WebView::mouseReleaseEvent(QMouseEvent *event) QWebView::mouseReleaseEvent(event); } + + +void WebView::spellCheck() +{ + QString text(execJScript(m_contextMenuHitResult, QL1S("this.value")).toString()); + + if (m_contextMenuHitResult.isContentSelected()) + { + m_spellTextSelectionStart = qMax(0, execJScript(m_contextMenuHitResult, QL1S("this.selectionStart")).toInt()); + m_spellTextSelectionEnd = qMax(0, execJScript(m_contextMenuHitResult, QL1S("this.selectionEnd")).toInt()); + text = text.mid(m_spellTextSelectionStart, (m_spellTextSelectionEnd - m_spellTextSelectionStart)); + } + else + { + m_spellTextSelectionStart = 0; + m_spellTextSelectionEnd = 0; + } + + if (text.isEmpty()) + { + return; + } + + Sonnet::Dialog* spellDialog = new Sonnet::Dialog(new Sonnet::BackgroundChecker(this), this); + + spellDialog->showSpellCheckCompletionMessage(true); + connect(spellDialog, SIGNAL(replace(QString, int, QString)), this, SLOT(spellCheckerCorrected(QString, int, QString))); + connect(spellDialog, SIGNAL(misspelling(QString, int)), this, SLOT(spellCheckerMisspelling(QString, int))); + if (m_contextMenuHitResult.isContentSelected()) + connect(spellDialog, SIGNAL(done(QString)), this, SLOT(slotSpellCheckDone(QString))); + spellDialog->setBuffer(text); + spellDialog->show(); +} + + +void WebView::spellCheckerCorrected(const QString& original, int pos, const QString& replacement) +{ + // Adjust the selection end... + if (m_spellTextSelectionEnd > 0) + { + m_spellTextSelectionEnd += qMax(0, (replacement.length() - original.length())); + } + + const int index = pos + m_spellTextSelectionStart; + QString script(QL1S("this.value=this.value.substring(0,")); + script += QString::number(index); + script += QL1S(") + \""); + script += replacement; + script += QL1S("\" + this.value.substring("); + script += QString::number(index + original.length()); + script += QL1S(")"); + + //kDebug() << "**** script:" << script; + execJScript(m_contextMenuHitResult, script); +} + + +void WebView::spellCheckerMisspelling(const QString& text, int pos) +{ + // kDebug() << text << pos; + QString selectionScript(QL1S("this.setSelectionRange(")); + selectionScript += QString::number(pos + m_spellTextSelectionStart); + selectionScript += QL1C(','); + selectionScript += QString::number(pos + text.length() + m_spellTextSelectionStart); + selectionScript += QL1C(')'); + execJScript(m_contextMenuHitResult, selectionScript); +} + + +void WebView::slotSpellCheckDone(const QString&) +{ + // Restore the text selection if one was present before we started the + // spell check. + if (m_spellTextSelectionStart > 0 || m_spellTextSelectionEnd > 0) + { + QString script(QL1S("; this.setSelectionRange(")); + script += QString::number(m_spellTextSelectionStart); + script += QL1C(','); + script += QString::number(m_spellTextSelectionEnd); + script += QL1C(')'); + execJScript(m_contextMenuHitResult, script); + } +} |