summaryrefslogtreecommitdiff
path: root/src/webtab/webview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/webtab/webview.cpp')
-rw-r--r--src/webtab/webview.cpp249
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);
+ }
+}