summaryrefslogtreecommitdiff
path: root/src/webtab
diff options
context:
space:
mode:
authorAndrea Diamantini <adjam7@gmail.com>2012-09-19 23:22:13 +0200
committerAndrea Diamantini <adjam7@gmail.com>2012-12-10 02:48:05 +0100
commite315ff15daf26cbc70b6f2da50e6ca18081fc7c4 (patch)
tree6da0556f2c5c7efb13d8e8b03ba0092e7a5838d3 /src/webtab
parentImplement RekonqWindow (diff)
downloadrekonq-e315ff15daf26cbc70b6f2da50e6ca18081fc7c4.tar.xz
Integrated spell checking for rekonq2 (lindsay's work imported)
- inline spell highlighter. This requires WebKit 2.3 to work. - Addition of a suggested replacement word list to the context menu, for the word right clicked on - A standard modeless spell check dialog that works on the current editable text (or selection). Heavily cribbed from Dawit's work on kdewebkitpart. oops... also a codingstyle script round here... :)
Diffstat (limited to 'src/webtab')
-rw-r--r--src/webtab/networkaccessmanager.cpp22
-rw-r--r--src/webtab/previewselectorbar.cpp2
-rw-r--r--src/webtab/protocolhandler.cpp4
-rw-r--r--src/webtab/protocolhandler.h2
-rw-r--r--src/webtab/webpage.cpp4
-rw-r--r--src/webtab/webtab.cpp2
-rw-r--r--src/webtab/webtab.h2
-rw-r--r--src/webtab/webview.cpp249
-rw-r--r--src/webtab/webview.h11
9 files changed, 259 insertions, 39 deletions
diff --git a/src/webtab/networkaccessmanager.cpp b/src/webtab/networkaccessmanager.cpp
index e817faca..95bfa47d 100644
--- a/src/webtab/networkaccessmanager.cpp
+++ b/src/webtab/networkaccessmanager.cpp
@@ -85,15 +85,15 @@ static void hideBlockedElements(const QUrl& url, QWebElementCollection& collecti
{
for (QWebElementCollection::iterator it = collection.begin(); it != collection.end(); ++it)
{
- const QUrl baseUrl ((*it).webFrame()->baseUrl());
+ const QUrl baseUrl((*it).webFrame()->baseUrl());
QString src = (*it).attribute(QL1S("src"));
-
+
if (src.isEmpty())
src = (*it).evaluateJavaScript(QL1S("this.src")).toString();
if (src.isEmpty())
continue;
- const QUrl resolvedUrl (baseUrl.resolved(src));
+ const QUrl resolvedUrl(baseUrl.resolved(src));
if (url == resolvedUrl)
{
//kDebug() << "*** HIDING ELEMENT: " << (*it).tagName() << resolvedUrl;
@@ -125,11 +125,11 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent)
QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData)
{
bool blocked = false;
-
+
// Handle GET operations with AdBlock
if (op == QNetworkAccessManager::GetOperation)
blocked = AdBlockManager::self()->blockRequest(req);
-
+
if (!blocked)
{
if (KProtocolInfo::isHelperProtocol(req.url()))
@@ -162,10 +162,10 @@ void NetworkAccessManager::slotFinished(bool ok)
if (!ok)
return;
- if(!AdBlockManager::self()->isEnabled())
+ if (!AdBlockManager::self()->isEnabled())
return;
- if(!AdBlockManager::self()->isHidingElements())
+ if (!AdBlockManager::self()->isHidingElements())
return;
QWebFrame* frame = qobject_cast<QWebFrame*>(sender());
@@ -176,10 +176,10 @@ void NetworkAccessManager::slotFinished(bool ok)
if (urls.isEmpty())
return;
- QWebElementCollection collection = frame->findAllElements(HIDABLE_ELEMENTS);
- if (frame->parentFrame())
+ QWebElementCollection collection = frame->findAllElements(HIDABLE_ELEMENTS);
+ if (frame->parentFrame())
collection += frame->parentFrame()->findAllElements(HIDABLE_ELEMENTS);
- Q_FOREACH(const QUrl& url, urls)
- hideBlockedElements(url, collection);
+ Q_FOREACH(const QUrl & url, urls)
+ hideBlockedElements(url, collection);
}
diff --git a/src/webtab/previewselectorbar.cpp b/src/webtab/previewselectorbar.cpp
index 24461e79..b37b000c 100644
--- a/src/webtab/previewselectorbar.cpp
+++ b/src/webtab/previewselectorbar.cpp
@@ -101,7 +101,7 @@ void PreviewSelectorBar::loadFinished()
void PreviewSelectorBar::clicked()
{
WebTab *tab = qobject_cast<WebTab *>(parent());
-
+
if (tab->page())
{
KUrl url = tab->url();
diff --git a/src/webtab/protocolhandler.cpp b/src/webtab/protocolhandler.cpp
index bca5a007..ab2f0b8e 100644
--- a/src/webtab/protocolhandler.cpp
+++ b/src/webtab/protocolhandler.cpp
@@ -99,7 +99,7 @@ ProtocolHandler::ProtocolHandler(QObject *parent)
void ProtocolHandler::setWindow(WebWindow *w)
{
_webwin = w;
- _lister->setMainWindow(_webwin);
+ _lister->setMainWindow(_webwin);
}
@@ -276,7 +276,7 @@ void ProtocolHandler::showResults(const KFileItemList &list)
_webwin->urlBar()->setQUrl(_url);
_webwin->view()->setFocus();
-
+
HistoryManager::self()->addHistoryEntry(_url, _url.prettyUrl());
}
}
diff --git a/src/webtab/protocolhandler.h b/src/webtab/protocolhandler.h
index c4bc6faf..7d685ea4 100644
--- a/src/webtab/protocolhandler.h
+++ b/src/webtab/protocolhandler.h
@@ -68,7 +68,7 @@ public:
bool postHandling(const QNetworkRequest &request, QWebFrame *frame);
void setWindow(WebWindow *);
-
+
Q_SIGNALS:
void downloadUrl(const KUrl &);
diff --git a/src/webtab/webpage.cpp b/src/webtab/webpage.cpp
index 385a6ac3..cb4ea373 100644
--- a/src/webtab/webpage.cpp
+++ b/src/webtab/webpage.cpp
@@ -131,7 +131,7 @@ WebPage::WebPage(QWidget *parent)
WebTab *tab = qobject_cast<WebTab *>(view->parent());
WebWindow *w = tab->webWindow();
_protHandler.setWindow(w);
-
+
// handling unsupported content...
setForwardUnsupportedContent(true);
connect(this, SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(handleUnsupportedContent(QNetworkReply*)));
@@ -425,7 +425,7 @@ void WebPage::handleUnsupportedContent(QNetworkReply *reply)
WebView *view = qobject_cast<WebView *>(parent());
WebTab *tab = qobject_cast<WebTab *>(view->parent());
tab->setPart(pa, replyUrl);
-
+
// FIXME: Is this enough?
}
else
diff --git a/src/webtab/webtab.cpp b/src/webtab/webtab.cpp
index e53d7b48..bc40ecca 100644
--- a/src/webtab/webtab.cpp
+++ b/src/webtab/webtab.cpp
@@ -133,7 +133,7 @@ WebWindow *WebTab::webWindow()
WebWindow *w = qobject_cast<WebWindow *>(parent());
return w;
}
-
+
KUrl WebTab::url()
{
if (page() && page()->isOnRekonqPage())
diff --git a/src/webtab/webtab.h b/src/webtab/webtab.h
index 8b60f0ff..a0e6890d 100644
--- a/src/webtab/webtab.h
+++ b/src/webtab/webtab.h
@@ -62,7 +62,7 @@ public:
WebView *view();
WebPage *page();
WebWindow *webWindow();
-
+
inline int progress() const
{
return m_progress;
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);
+ }
+}
diff --git a/src/webtab/webview.h b/src/webtab/webview.h
index b1648d0d..5487a59f 100644
--- a/src/webtab/webview.h
+++ b/src/webtab/webview.h
@@ -39,6 +39,7 @@
#include <QTime>
#include <QPoint>
#include <QPixmap>
+#include <QWebHitTestResult>
// Forward Declarations
class WebPage;
@@ -72,6 +73,7 @@ public:
const QByteArray & body = QByteArray());
protected:
+ bool popupSpellMenu(QContextMenuEvent *event);
void contextMenuEvent(QContextMenuEvent *event);
void mouseMoveEvent(QMouseEvent *event);
@@ -95,6 +97,10 @@ private Q_SLOTS:
void openLinkInNewWindow();
void openLinkInNewTab();
void bookmarkLink();
+ void spellCheck();
+ void spellCheckerCorrected(const QString& original, int pos, const QString& replacement);
+ void spellCheckerMisspelling(const QString& text, int pos);
+ void slotSpellCheckDone(const QString&);
void sendByMail();
void viewImage(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
@@ -130,6 +136,11 @@ private:
private:
QPoint m_clickPos;
+ QWebHitTestResult m_contextMenuHitResult;
+
+ // Spell Checking
+ int m_spellTextSelectionStart;
+ int m_spellTextSelectionEnd;
// Auto Scroll
QTimer *const m_autoScrollTimer;