/* ============================================================ * * This file is a part of the rekonq project * * Copyright (C) 2008-2012 by Andrea Diamantini * Copyright (C) 2009-2011 by Lionel Chauvin * * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . * * ============================================================ */ // Self Includes #include "webview.h" #include "webview.moc" // Auto Includes #include "rekonq.h" // Local Includes #include "application.h" #include "bookmarkmanager.h" #include "iconmanager.h" #include "mainview.h" #include "mainwindow.h" #include "searchengine.h" #include "urlbar.h" #include "webpage.h" #include "webtab.h" // KDE Includes #include #include #include #include #include #include #include // Qt Includes #include #include #include #include #include #include WebView::WebView(QWidget* parent) : KWebView(parent, false) , m_autoScrollTimer(new QTimer(this)) , m_verticalAutoScrollSpeed(0) , m_horizontalAutoScrollSpeed(0) , m_isViewAutoScrolling(false) , m_autoScrollIndicator(QPixmap(KStandardDirs::locate("appdata" , "pics/autoscroll.png"))) , m_smoothScrollTimer(new QTimer(this)) , m_dy(0) , m_smoothScrollSteps(0) , m_isViewSmoothScrolling(false) , m_accessKeysPressed(false) , m_accessKeysActive(false) { WebPage *page = new WebPage(this); setPage(page); // download system connect(this, SIGNAL(linkShiftClicked(KUrl)), page, SLOT(downloadUrl(KUrl))); // middle click || ctrl + click signal connect(this, SIGNAL(linkMiddleOrCtrlClicked(KUrl)), this, SLOT(loadUrlInNewTab(KUrl))); // loadUrl signal connect(this, SIGNAL(loadUrl(KUrl, Rekonq::OpenType)), rApp, SLOT(loadUrl(KUrl, Rekonq::OpenType))); // Auto scroll timer connect(m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(scrollFrameChanged())); m_autoScrollTimer->setInterval(100); // Smooth scroll timer connect(m_smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick())); m_smoothScrollTimer->setInterval(16); connect(this, SIGNAL(iconChanged()), this, SLOT(changeWindowIcon())); connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted())); } WebView::~WebView() { if (m_isViewSmoothScrolling) stopSmoothScrolling(); } void WebView::loadStarted() { hideAccessKeys(); } void WebView::changeWindowIcon() { if (ReKonfig::useFavicon()) { MainWindow *const mainWindow = rApp->mainWindow(); if (url() == mainWindow->currentTab()->url()) { const int index = mainWindow->mainView()->currentIndex(); mainWindow->changeWindowIcon(index); } } } WebPage *WebView::page() { WebPage *const page = qobject_cast(KWebView::page()); Q_ASSERT(page); return page; } void WebView::contextMenuEvent(QContextMenuEvent *event) { QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos()); MainWindow *mainwindow = rApp->mainWindow(); KMenu menu(this); QAction *a; KAction *inspectAction = new KAction(KIcon("layer-visible-on"), i18n("Inspect Element"), this); connect(inspectAction, SIGNAL(triggered(bool)), this, SLOT(inspect())); KAction *sendByMailAction = new KAction(this); sendByMailAction->setIcon(KIcon("mail-send")); connect(sendByMailAction, SIGNAL(triggered(bool)), this, SLOT(sendByMail())); // Choose right context int resultHit = 0; if (result.linkUrl().isEmpty()) resultHit = WebView::EmptySelection; else resultHit = WebView::LinkSelection; if (!result.pixmap().isNull()) resultHit |= WebView::ImageSelection; if (result.isContentSelected()) resultHit = WebView::TextSelection; // -------------------------------------------------------------------------------- // Ok, let's start filling up the menu... // is content editable? Add PASTE if (result.isContentEditable()) { menu.addAction(pageAction(KWebPage::Paste)); menu.addSeparator(); } // EMPTY PAGE ACTIONS ------------------------------------------------------------- if (resultHit == WebView::EmptySelection) { // send by mail: page url sendByMailAction->setData(page()->currentFrame()->url()); sendByMailAction->setText(i18n("Share page url")); // navigation QWebHistory *history = page()->history(); if (history->canGoBack()) { menu.addAction(pageAction(KWebPage::Back)); } if (history->canGoForward()) { menu.addAction(pageAction(KWebPage::Forward)); } menu.addAction(mainwindow->actionByName("view_redisplay")); menu.addSeparator(); //Frame KActionMenu *frameMenu = new KActionMenu(i18n("Current Frame"), this); frameMenu->addAction(pageAction(KWebPage::OpenFrameInNewWindow)); a = new KAction(KIcon("document-print-frame"), i18n("Print Frame"), this); connect(a, SIGNAL(triggered()), this, SLOT(printFrame())); frameMenu->addAction(a); menu.addAction(frameMenu); menu.addSeparator(); // Page Actions menu.addAction(pageAction(KWebPage::SelectAll)); menu.addAction(mainwindow->actionByName(KStandardAction::name(KStandardAction::SaveAs))); if (!KStandardDirs::findExe("kget").isNull() && ReKonfig::kgetList()) { a = new KAction(KIcon("kget"), i18n("List All Links"), this); connect(a, SIGNAL(triggered(bool)), page(), SLOT(downloadAllContentsWithKGet())); menu.addAction(a); } menu.addAction(mainwindow->actionByName("page_source")); menu.addAction(inspectAction); if (mainwindow->isFullScreen()) { menu.addSeparator(); menu.addAction(mainwindow->actionByName("fullscreen")); } } // LINK ACTIONS ------------------------------------------------------------------- if (resultHit & WebView::LinkSelection) { // send by mail: link url sendByMailAction->setData(result.linkUrl()); sendByMailAction->setText(i18n("Share link")); a = new KAction(KIcon("tab-new"), i18n("Open in New &Tab"), this); a->setData(result.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()); connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow())); menu.addAction(a); menu.addSeparator(); menu.addAction(pageAction(KWebPage::DownloadLinkToDisk)); menu.addAction(pageAction(KWebPage::CopyLinkToClipboard)); } // IMAGE ACTIONS ------------------------------------------------------------------ if (resultHit & WebView::ImageSelection) { // send by mail: image url sendByMailAction->setData(result.imageUrl()); sendByMailAction->setText(i18n("Share image link")); menu.addSeparator(); a = new KAction(KIcon("view-preview"), i18n("&View Image"), this); a->setData(result.imageUrl()); connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(viewImage(Qt::MouseButtons, Qt::KeyboardModifiers))); menu.addAction(a); menu.addAction(pageAction(KWebPage::DownloadImageToDisk)); a = new KAction(KIcon("view-media-visualization"), i18n("&Copy Image Location"), this); a->setData(result.imageUrl()); connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(slotCopyImageLocation())); menu.addAction(a); } // ACTIONS FOR TEXT SELECTION ----------------------------------------------------- if (resultHit & WebView::TextSelection) { // send by mail: text sendByMailAction->setData(selectedText()); sendByMailAction->setText(i18n("Share selected text")); if (result.isContentEditable()) { // actions for text selected in field menu.addAction(pageAction(KWebPage::Cut)); } a = pageAction(KWebPage::Copy); if (!result.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(" ") ) { QString text = selectedText(); text = text.trimmed(); KUrl urlLikeText(text); if (urlLikeText.isValid()) { QString truncatedUrl = text; const int maxTextSize = 18; if (truncatedUrl.length() > maxTextSize) { const int truncateSize = 15; 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); 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); openInNewWindowAction->setData(QUrl(urlLikeText)); connect(openInNewWindowAction, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow())); menu.addAction(openInNewWindowAction); menu.addSeparator(); } } //Default SearchEngine KService::Ptr defaultEngine = SearchEngine::defaultEngine(); if (defaultEngine) // check if a default engine is set { a = new KAction(i18nc("Search selected text with the default search engine", "Search with %1", defaultEngine->name()), this); a->setIcon(rApp->iconManager()->iconForUrl(SearchEngine::buildQuery(defaultEngine, ""))); a->setData(defaultEngine->entryPath()); connect(a, SIGNAL(triggered(bool)), this, SLOT(search())); menu.addAction(a); } //All favourite ones KActionMenu *searchMenu = new KActionMenu(KIcon("edit-find"), i18nc("@title:menu", "Search"), this); Q_FOREACH(const KService::Ptr & engine, SearchEngine::favorites()) { a = new KAction(i18nc("@item:inmenu Search, %1 = search engine", "With %1", engine->name()), this); a->setIcon(rApp->iconManager()->iconForUrl(SearchEngine::buildQuery(engine, ""))); a->setData(engine->entryPath()); connect(a, SIGNAL(triggered(bool)), this, SLOT(search())); searchMenu->addAction(a); } a = new KAction(KIcon("edit-find"), i18n("On Current Page"), this); connect(a, SIGNAL(triggered()), rApp->mainWindow(), SLOT(findSelectedText())); searchMenu->addAction(a); if (!searchMenu->menu()->isEmpty()) { menu.addAction(searchMenu); } } // DEFAULT ACTIONs (on the bottom) ------------------------------------------------ menu.addSeparator(); if (resultHit & WebView::LinkSelection) { a = new KAction(KIcon("bookmark-new"), i18n("&Bookmark link"), this); a->setData(result.linkUrl()); connect(a, SIGNAL(triggered(bool)), this, SLOT(bookmarkLink())); menu.addAction(a); } else { a = rApp->bookmarkManager()->actionByName("rekonq_add_bookmark"); menu.addAction(a); } menu.addAction(sendByMailAction); menu.addAction(inspectAction); // finally launch the menu... menu.exec(mapToGlobal(event->pos())); } void WebView::mousePressEvent(QMouseEvent *event) { if (m_isViewAutoScrolling) { m_verticalAutoScrollSpeed = 0; m_horizontalAutoScrollSpeed = 0; m_autoScrollTimer->stop(); m_isViewAutoScrolling = false; update(); return; } QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos()); bool weCanDoMiddleClickActions = !result.isContentEditable() && result.linkUrl().isEmpty(); switch (event->button()) { case Qt::XButton1: triggerPageAction(KWebPage::Back); break; case Qt::XButton2: triggerPageAction(KWebPage::Forward); break; case Qt::MidButton: switch (ReKonfig::middleClickAction()) { case 0: // AutoScroll if (weCanDoMiddleClickActions && !m_isViewAutoScrolling && !page()->currentFrame()->scrollBarGeometry(Qt::Horizontal).contains(event->pos()) && !page()->currentFrame()->scrollBarGeometry(Qt::Vertical).contains(event->pos())) { if (!page()->currentFrame()->scrollBarGeometry(Qt::Horizontal).isNull() || !page()->currentFrame()->scrollBarGeometry(Qt::Vertical).isNull()) { m_clickPos = event->pos(); m_isViewAutoScrolling = true; update(); } } break; case 1: // Load Clipboard URL if (weCanDoMiddleClickActions) { const QString clipboardContent = rApp->clipboard()->text(); if (clipboardContent.isEmpty()) break; if (QUrl::fromUserInput(clipboardContent).isValid()) loadUrl(clipboardContent, Rekonq::CurrentTab); else // Search with default Engine { KService::Ptr defaultEngine = SearchEngine::defaultEngine(); if (defaultEngine) // check if a default engine is set loadUrl(KUrl(SearchEngine::buildQuery(defaultEngine, clipboardContent)), Rekonq::CurrentTab); } } break; default: // Do Nothing break; } break; default: break; }; KWebView::mousePressEvent(event); } void WebView::mouseMoveEvent(QMouseEvent *event) { QPoint mousePos = event->pos(); if (m_isViewAutoScrolling) { QPoint r = mousePos - m_clickPos; m_horizontalAutoScrollSpeed = r.x() / 2; // you are too fast.. m_verticalAutoScrollSpeed = r.y() / 2; if (!m_autoScrollTimer->isActive()) m_autoScrollTimer->start(); return; } MainWindow *w = rApp->mainWindow(); if (w->isFullScreen()) { if (event->pos().y() >= 0 && event->pos().y() <= 4) { w->setWidgetsVisible(true); } else { if (!w->mainView()->currentUrlBar()->hasFocus()) w->setWidgetsVisible(false); } } KWebView::mouseMoveEvent(event); } void WebView::dropEvent(QDropEvent *event) { bool isEditable = page()->frameAt(event->pos())->hitTestContent(event->pos()).isContentEditable(); if (event->mimeData()->hasFormat("application/rekonq-bookmark")) { QByteArray addresses = event->mimeData()->data("application/rekonq-bookmark"); KBookmark bookmark = rApp->bookmarkManager()->findByAddress(QString::fromLatin1(addresses.data())); if (bookmark.isGroup()) { rApp->bookmarkManager()->openFolderinTabs(bookmark.toGroup()); } else { emit loadUrl(bookmark.url(), Rekonq::CurrentTab); } } else if (event->mimeData()->hasUrls() && event->source() != this && !isEditable) //dropped links { Q_FOREACH(const QUrl & url, event->mimeData()->urls()) { emit loadUrl(url, Rekonq::NewFocusedTab); } } else if (event->mimeData()->hasFormat("text/plain") && event->source() != this && !isEditable) //dropped plain text with url format { QUrl url = QUrl::fromUserInput(event->mimeData()->data("text/plain")); if (url.isValid()) emit loadUrl(url, Rekonq::NewFocusedTab); } else { KWebView::dropEvent(event); } } void WebView::paintEvent(QPaintEvent* event) { KWebView::paintEvent(event); if (m_isViewAutoScrolling) { QPoint centeredPoint = m_clickPos; centeredPoint.setX(centeredPoint.x() - m_autoScrollIndicator.width() / 2); centeredPoint.setY(centeredPoint.y() - m_autoScrollIndicator.height() / 2); QPainter painter(this); painter.setOpacity(0.8); painter.drawPixmap(centeredPoint, m_autoScrollIndicator); } } void WebView::search() { KAction *a = qobject_cast(sender()); KService::Ptr engine = KService::serviceByDesktopPath(a->data().toString()); KUrl urlSearch = KUrl(SearchEngine::buildQuery(engine, selectedText())); emit loadUrl(urlSearch, Rekonq::NewTab); } void WebView::printFrame() { rApp->mainWindow()->printRequested(page()->currentFrame()); } void WebView::viewImage(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) { KAction *a = qobject_cast(sender()); KUrl url(a->data().toUrl()); if (modifiers & Qt::ControlModifier || buttons == Qt::MidButton) { emit loadUrl(url, Rekonq::NewTab); } else { emit loadUrl(url, Rekonq::CurrentTab); } } void WebView::slotCopyImageLocation() { KAction *a = qobject_cast(sender()); KUrl imageUrl(a->data().toUrl()); #ifndef QT_NO_MIMECLIPBOARD // Set it in both the mouse selection and in the clipboard QMimeData* mimeData = new QMimeData; imageUrl.populateMimeData(mimeData); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); mimeData = new QMimeData; imageUrl.populateMimeData(mimeData); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Selection); #else QApplication::clipboard()->setText(imageUrl.url()); #endif } void WebView::openLinkInNewWindow() { KAction *a = qobject_cast(sender()); KUrl url(a->data().toUrl()); emit loadUrl(url, Rekonq::NewWindow); } void WebView::openLinkInNewTab() { KAction *a = qobject_cast(sender()); KUrl url(a->data().toUrl()); emit loadUrl(url, Rekonq::NewTab); } void WebView::bookmarkLink() { KAction *a = qobject_cast(sender()); KUrl url(a->data().toUrl()); rApp->bookmarkManager()->rootGroup().addBookmark(url.prettyUrl(), url); rApp->bookmarkManager()->emitChanged(); } void WebView::keyPressEvent(QKeyEvent *event) { // If CTRL was hit, be prepared for access keys if (ReKonfig::accessKeysEnabled() && !m_accessKeysActive && event->key() == Qt::Key_Control && !(event->modifiers() & ~Qt::ControlModifier) ) { m_accessKeysPressed = true; event->accept(); return; } const QString tagName = page()->mainFrame()->evaluateJavaScript("document.activeElement.tagName").toString(); if (event->modifiers() == Qt::ControlModifier) { if (event->key() == Qt::Key_C) { triggerPageAction(KWebPage::Copy); event->accept(); return; } if (event->key() == Qt::Key_A) { triggerPageAction(KWebPage::SelectAll); event->accept(); return; } // CTRL + RETURN: open link into another tab if (event->key() == Qt::Key_Return && tagName == QL1S("A")) { KUrl u = KUrl(page()->mainFrame()->evaluateJavaScript("document.activeElement.attributes[\"href\"].value").toString()); emit loadUrl(u, Rekonq::NewTab); event->accept(); return; } } // Auto Scrolling if (event->modifiers() == Qt::ShiftModifier && tagName != QL1S("INPUT") && tagName != QL1S("TEXTAREA") ) { // NOTE and FIXME // This check is doabled because it presents strange behavior: QtWebKit check works well in pages like gmail // and fails on sites like g+. The opposite is true for javascript check. // Please, help me finding the right way to check this EVERY TIME. bool isContentEditableQW = page()->mainFrame()->hitTestContent(QCursor::pos()).isContentEditable(); bool isContentEditableJS = page()->mainFrame()->evaluateJavaScript("document.activeElement.isContentEditable").toBool(); if (!isContentEditableQW && !isContentEditableJS) { if (event->key() == Qt::Key_Up) { m_verticalAutoScrollSpeed--; if (!m_autoScrollTimer->isActive()) m_autoScrollTimer->start(); event->accept(); return; } if (event->key() == Qt::Key_Down) { m_verticalAutoScrollSpeed++; if (!m_autoScrollTimer->isActive()) m_autoScrollTimer->start(); event->accept(); return; } if (event->key() == Qt::Key_Right) { m_horizontalAutoScrollSpeed++; if (!m_autoScrollTimer->isActive()) m_autoScrollTimer->start(); event->accept(); return; } if (event->key() == Qt::Key_Left) { m_horizontalAutoScrollSpeed--; if (!m_autoScrollTimer->isActive()) m_autoScrollTimer->start(); event->accept(); return; } if (m_autoScrollTimer->isActive()) { m_autoScrollTimer->stop(); event->accept(); return; } else { if (m_verticalAutoScrollSpeed || m_horizontalAutoScrollSpeed) { m_autoScrollTimer->start(); event->accept(); return; } } } // if you arrived here, I hope it means SHIFT has been pressed NOT for autoscroll management... } if (ReKonfig::accessKeysEnabled() && m_accessKeysActive) { hideAccessKeys(); event->accept(); return; } // vi-like navigation if (ReKonfig::enableViShortcuts()) { if (event->modifiers() == Qt::NoModifier && tagName != QL1S("INPUT") && tagName != QL1S("TEXTAREA") ) { // See note up! bool isContentEditableQW = page()->mainFrame()->hitTestContent(QCursor::pos()).isContentEditable(); bool isContentEditableJS = page()->mainFrame()->evaluateJavaScript("document.activeElement.isContentEditable").toBool(); if (!isContentEditableQW && !isContentEditableJS) { switch (event->key()) { case Qt::Key_J: event->accept(); event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier); break; case Qt::Key_K: event->accept(); event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier); break; case Qt::Key_L: event->accept(); event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); break; case Qt::Key_H: event->accept(); event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); break; default: break; } } } } KWebView::keyPressEvent(event); } void WebView::keyReleaseEvent(QKeyEvent *event) { // access keys management if (ReKonfig::accessKeysEnabled()) { if (m_accessKeysPressed && event->key() != Qt::Key_Control) m_accessKeysPressed = false; if (m_accessKeysPressed && !(event->modifiers() & Qt::ControlModifier)) { kDebug() << "Shotting access keys"; QTimer::singleShot(200, this, SLOT(accessKeyShortcut())); event->accept(); return; } else { checkForAccessKey(event); kDebug() << "Hiding access keys"; hideAccessKeys(); event->accept(); return; } } KWebView::keyReleaseEvent(event); } void WebView::wheelEvent(QWheelEvent *event) { if (event->orientation() == Qt::Vertical || !ReKonfig::hScrollWheelHistory()) { // To let some websites (eg: google maps) to handle wheel events int prevPos = page()->currentFrame()->scrollPosition().y(); KWebView::wheelEvent(event); int newPos = page()->currentFrame()->scrollPosition().y(); // Sync with the zoom slider if (event->modifiers() == Qt::ControlModifier) { // Limits of the slider if (zoomFactor() > 1.9) setZoomFactor(1.9); else if (zoomFactor() < 0.1) setZoomFactor(0.1); // Round the factor (Fix slider's end value) int newFactor = zoomFactor() * 10; if ((zoomFactor() * 10 - newFactor) > 0.5) newFactor++; emit zoomChanged(newFactor); } else if (ReKonfig::smoothScrolling() && prevPos != newPos) { page()->currentFrame()->setScrollPosition(QPoint(page()->currentFrame()->scrollPosition().x(), prevPos)); if ((event->delta() > 0) != !m_smoothScrollBottomReached) stopSmoothScrolling(); if (event->delta() > 0) m_smoothScrollBottomReached = false; else m_smoothScrollBottomReached = true; setupSmoothScrolling(abs(newPos - prevPos)); } } // use horizontal wheel events to go back and forward in tab history else { // left -> go to previous page if (event->delta() > 0) { emit openPreviousInHistory(); } // right -> go to next page if (event->delta() < 0) { emit openNextInHistory(); } } } void WebView::inspect() { QAction *a = rApp->mainWindow()->actionByName("web_inspector"); if (a && !a->isChecked()) a->trigger(); pageAction(QWebPage::InspectElement)->trigger(); } void WebView::loadUrlInNewTab(const KUrl &url) { emit loadUrl(url, Rekonq::NewTab); } void WebView::scrollFrameChanged() { // do the scrolling page()->currentFrame()->scroll(m_horizontalAutoScrollSpeed, m_verticalAutoScrollSpeed); // check if we reached the end int y = page()->currentFrame()->scrollPosition().y(); if (y == 0 || y == page()->currentFrame()->scrollBarMaximum(Qt::Vertical)) m_verticalAutoScrollSpeed = 0; int x = page()->currentFrame()->scrollPosition().x(); if (x == 0 || x == page()->currentFrame()->scrollBarMaximum(Qt::Horizontal)) m_horizontalAutoScrollSpeed = 0; } void WebView::setupSmoothScrolling(int posY) { int ddy = qMax(m_smoothScrollSteps ? abs(m_dy) / m_smoothScrollSteps : 0, 3); m_dy += posY; if (m_dy <= 0) { stopSmoothScrolling(); return; } m_smoothScrollSteps = 8; if (m_dy / m_smoothScrollSteps < ddy) { m_smoothScrollSteps = (abs(m_dy) + ddy - 1) / ddy; if (m_smoothScrollSteps < 1) m_smoothScrollSteps = 1; } m_smoothScrollTime.start(); if (!m_isViewSmoothScrolling) { m_isViewSmoothScrolling = true; m_smoothScrollTimer->start(); scrollTick(); } } void WebView::scrollTick() { if (m_dy == 0) { stopSmoothScrolling(); return; } if (m_smoothScrollSteps < 1) m_smoothScrollSteps = 1; int takesteps = m_smoothScrollTime.restart() / 16; int scroll_y = 0; if (takesteps < 1) takesteps = 1; if (takesteps > m_smoothScrollSteps) takesteps = m_smoothScrollSteps; for (int i = 0; i < takesteps; i++) { int ddy = (m_dy / (m_smoothScrollSteps + 1)) * 2; // limit step to requested scrolling distance if (abs(ddy) > abs(m_dy)) ddy = m_dy; // update remaining scroll m_dy -= ddy; scroll_y += ddy; m_smoothScrollSteps--; } if (m_smoothScrollBottomReached) page()->currentFrame()->scroll(0, scroll_y); else page()->currentFrame()->scroll(0, -scroll_y); } void WebView::stopSmoothScrolling() { m_smoothScrollTimer->stop(); m_dy = 0; m_isViewSmoothScrolling = false; } void WebView::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() || event->mimeData()->hasText()) event->acceptProposedAction(); else KWebView::dragEnterEvent(event); } void WebView::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasUrls() || event->mimeData()->hasText()) event->acceptProposedAction(); else KWebView::dragMoveEvent(event); } void WebView::hideAccessKeys() { if (!m_accessKeyLabels.isEmpty()) { for (int i = 0; i < m_accessKeyLabels.count(); ++i) { QLabel *label = m_accessKeyLabels[i]; label->hide(); label->deleteLater(); } m_accessKeyLabels.clear(); m_accessKeyNodes.clear(); update(); } } void WebView::showAccessKeys() { QStringList supportedElement; supportedElement << QLatin1String("a") << QLatin1String("input") << QLatin1String("area") << QLatin1String("button") << QLatin1String("label") << QLatin1String("legend") << QLatin1String("textarea"); QList unusedKeys; for (char c = 'A'; c <= 'Z'; ++c) unusedKeys << QLatin1Char(c); for (char c = '0'; c <= '9'; ++c) unusedKeys << QLatin1Char(c); QRect viewport = QRect(page()->mainFrame()->scrollPosition(), page()->viewportSize()); // Priority first goes to elements with accesskey attributes QList alreadyLabeled; Q_FOREACH(const QString & elementType, supportedElement) { QList result = page()->mainFrame()->findAllElements(elementType).toList(); Q_FOREACH(const QWebElement & element, result) { const QRect geometry = element.geometry(); if (geometry.size().isEmpty() || !viewport.contains(geometry.topLeft())) { continue; } QString accessKeyAttribute = element.attribute(QLatin1String("accesskey")).toUpper(); if (accessKeyAttribute.isEmpty()) continue; QChar accessKey; for (int i = 0; i < accessKeyAttribute.count(); i += 2) { const QChar &possibleAccessKey = accessKeyAttribute[i]; if (unusedKeys.contains(possibleAccessKey)) { accessKey = possibleAccessKey; break; } } if (accessKey.isNull()) { continue; } unusedKeys.removeOne(accessKey); makeAccessKeyLabel(accessKey, element); alreadyLabeled.append(element); } } // Pick an access key first from the letters in the text and then from the // list of unused access keys Q_FOREACH(const QString & elementType, supportedElement) { QWebElementCollection result = page()->mainFrame()->findAllElements(elementType); Q_FOREACH(const QWebElement & element, result) { const QRect geometry = element.geometry(); if (unusedKeys.isEmpty() || alreadyLabeled.contains(element) || geometry.size().isEmpty() || !viewport.contains(geometry.topLeft())) { continue; } QChar accessKey; QString text = element.toPlainText().toUpper(); for (int i = 0; i < text.count(); ++i) { const QChar &c = text.at(i); if (unusedKeys.contains(c)) { accessKey = c; break; } } if (accessKey.isNull()) accessKey = unusedKeys.takeFirst(); unusedKeys.removeOne(accessKey); makeAccessKeyLabel(accessKey, element); } } } void WebView::makeAccessKeyLabel(const QChar &accessKey, const QWebElement &element) { QLabel *label = new QLabel(this); label->setText(QString(QLatin1String("%1")).arg(accessKey)); label->setAutoFillBackground(true); label->setFrameStyle(QFrame::Box | QFrame::Plain); QPoint point = element.geometry().center(); point -= page()->mainFrame()->scrollPosition(); label->move(point); label->show(); point.setX(point.x() - label->width() / 2); label->move(point); m_accessKeyLabels.append(label); m_accessKeyNodes[accessKey] = element; } bool WebView::checkForAccessKey(QKeyEvent *event) { if (m_accessKeyLabels.isEmpty()) return false; QString text = event->text(); if (text.isEmpty()) return false; QChar key = text.at(0).toUpper(); bool handled = false; if (m_accessKeyNodes.contains(key)) { QWebElement element = m_accessKeyNodes[key]; QPoint p = element.geometry().center(); QWebFrame *frame = element.webFrame(); Q_ASSERT(frame); do { p -= frame->scrollPosition(); frame = frame->parentFrame(); } while (frame && frame != page()->mainFrame()); QMouseEvent pevent(QEvent::MouseButtonPress, p, Qt::LeftButton, 0, 0); rApp->sendEvent(this, &pevent); QMouseEvent revent(QEvent::MouseButtonRelease, p, Qt::LeftButton, 0, 0); rApp->sendEvent(this, &revent); handled = true; } kDebug() << "checking for access keys: " << handled; return handled; } void WebView::accessKeyShortcut() { if (!hasFocus() || !m_accessKeysPressed || !ReKonfig::accessKeysEnabled()) return; if (m_accessKeyLabels.isEmpty()) { showAccessKeys(); } else { hideAccessKeys(); } m_accessKeysPressed = false; } void WebView::sendByMail() { KAction *a = qobject_cast(sender()); QString url = a->data().toString(); kDebug() << "URL " << url; KToolInvocation::invokeMailer("", "", "", "", url); }