diff --git a/src/lib/app/browserwindow.cpp b/src/lib/app/browserwindow.cpp index 4a42935f..4b821572 100644 --- a/src/lib/app/browserwindow.cpp +++ b/src/lib/app/browserwindow.cpp @@ -1,1621 +1,1626 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 . * ============================================================ */ #include "browserwindow.h" #include "tabwidget.h" #include "tabbar.h" #include "webpage.h" #include "tabbedwebview.h" #include "lineedit.h" #include "history.h" #include "locationbar.h" #include "websearchbar.h" #include "pluginproxy.h" #include "sidebar.h" #include "cookiejar.h" #include "cookiemanager.h" #include "bookmarkstoolbar.h" #include "clearprivatedata.h" #include "autofill.h" #include "mainapplication.h" #include "checkboxdialog.h" #include "clickablelabel.h" #include "docktitlebarwidget.h" #include "iconprovider.h" #include "progressbar.h" #include "closedwindowsmanager.h" #include "statusbarmessage.h" #include "browsinglibrary.h" #include "navigationbar.h" #include "bookmarksimport/bookmarksimportdialog.h" #include "qztools.h" #include "reloadstopbutton.h" #include "enhancedmenu.h" #include "navigationcontainer.h" #include "settings.h" #include "qzsettings.h" #include "speeddial.h" #include "menubar.h" #include "bookmarkstools.h" #include "bookmarksmenu.h" #include "historymenu.h" #include "mainmenu.h" #include "downloadsbutton.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef QZ_WS_X11 #include #include #include #endif static const int savedWindowVersion = 2; BrowserWindow::SavedWindow::SavedWindow() { } BrowserWindow::SavedWindow::SavedWindow(BrowserWindow *window) { windowState = window->isFullScreen() ? QByteArray() : window->saveState(); windowGeometry = window->saveGeometry(); windowUiState = window->saveUiState(); #ifdef QZ_WS_X11 virtualDesktop = window->getCurrentVirtualDesktop(); #endif const int tabsCount = window->tabCount(); tabs.reserve(tabsCount); for (int i = 0; i < tabsCount; ++i) { TabbedWebView *webView = window->weView(i); if (!webView) { continue; } WebTab* webTab = webView->webTab(); if (!webTab) { continue; } WebTab::SavedTab tab(webTab); if (!tab.isValid()) { continue; } if (webTab->isCurrentTab()) { currentTab = tabs.size(); } tabs.append(tab); } } bool BrowserWindow::SavedWindow::isValid() const { return currentTab > -1; } void BrowserWindow::SavedWindow::clear() { windowState.clear(); windowGeometry.clear(); virtualDesktop = -1; currentTab = -1; tabs.clear(); } QDataStream &operator<<(QDataStream &stream, const BrowserWindow::SavedWindow &window) { stream << savedWindowVersion; stream << window.windowState; stream << window.windowGeometry; stream << window.virtualDesktop; stream << window.currentTab; stream << window.tabs.count(); for (int i = 0; i < window.tabs.count(); ++i) { stream << window.tabs.at(i); } stream << window.windowUiState; return stream; } QDataStream &operator>>(QDataStream &stream, BrowserWindow::SavedWindow &window) { int version; stream >> version; if (version < 1) { return stream; } stream >> window.windowState; stream >> window.windowGeometry; stream >> window.virtualDesktop; stream >> window.currentTab; int tabsCount = -1; stream >> tabsCount; window.tabs.reserve(tabsCount); for (int i = 0; i < tabsCount; ++i) { WebTab::SavedTab tab; stream >> tab; window.tabs.append(tab); } if (version >= 2) { stream >> window.windowUiState; } return stream; } BrowserWindow::BrowserWindow(Qz::BrowserWindowType type, const QUrl &startUrl) : QMainWindow(0) , m_startUrl(startUrl) , m_windowType(type) , m_startTab(0) , m_startPage(0) , m_sideBarManager(new SideBarManager(this)) , m_statusBarMessage(new StatusBarMessage(this)) , m_isHtmlFullScreen(false) , m_hideNavigationTimer(0) { setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DontCreateNativeAncestors); setObjectName("mainwindow"); setWindowTitle(tr("Falkon")); setProperty("private", mApp->isPrivate()); setupUi(); setupMenu(); m_hideNavigationTimer = new QTimer(this); m_hideNavigationTimer->setInterval(1000); m_hideNavigationTimer->setSingleShot(true); connect(m_hideNavigationTimer, SIGNAL(timeout()), this, SLOT(hideNavigationSlot())); connect(mApp, SIGNAL(settingsReloaded()), this, SLOT(loadSettings())); QTimer::singleShot(0, this, SLOT(postLaunch())); if (mApp->isPrivate()) { QzTools::setWmClass("Falkon Browser (Private Window)", this); } else { QzTools::setWmClass("Falkon Browser", this); } } BrowserWindow::~BrowserWindow() { mApp->plugins()->emitMainWindowDeleted(this); foreach (const QPointer &pointer, m_deleteOnCloseWidgets) { if (pointer) { pointer->deleteLater(); } } } void BrowserWindow::setStartTab(WebTab* tab) { m_startTab = tab; } void BrowserWindow::setStartPage(WebPage *page) { m_startPage = page; } void BrowserWindow::postLaunch() { loadSettings(); bool addTab = true; QUrl startUrl; switch (mApp->afterLaunch()) { case MainApplication::OpenBlankPage: startUrl = QUrl(); break; case MainApplication::OpenSpeedDial: startUrl = QUrl("falkon:speeddial"); break; case MainApplication::OpenHomePage: case MainApplication::RestoreSession: case MainApplication::SelectSession: startUrl = m_homepage; break; default: break; } show(); switch (m_windowType) { case Qz::BW_FirstAppWindow: if (mApp->isStartingAfterCrash()) { addTab = false; startUrl.clear(); m_tabWidget->addView(QUrl("falkon:restore"), Qz::NT_CleanSelectedTabAtTheEnd); } else if (mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) { addTab = m_tabWidget->count() <= 0; } break; case Qz::BW_NewWindow: case Qz::BW_MacFirstWindow: addTab = true; break; case Qz::BW_OtherRestoredWindow: addTab = false; break; } if (!m_startUrl.isEmpty()) { startUrl = m_startUrl; addTab = true; } if (m_startTab) { addTab = false; m_tabWidget->addView(m_startTab, Qz::NT_SelectedTab); } if (m_startPage) { addTab = false; m_tabWidget->addView(QUrl()); weView()->setPage(m_startPage); } if (addTab) { m_tabWidget->addView(startUrl, Qz::NT_CleanSelectedTabAtTheEnd); if (startUrl.isEmpty() || startUrl.toString() == QLatin1String("falkon:speeddial")) { locationBar()->setFocus(); } } // Something went really wrong .. add one tab if (m_tabWidget->count() <= 0) { m_tabWidget->addView(m_homepage, Qz::NT_SelectedTabAtTheEnd); } mApp->plugins()->emitMainWindowCreated(this); emit startingCompleted(); raise(); activateWindow(); updateStartupFocus(); } void BrowserWindow::setupUi() { Settings settings; settings.beginGroup("Browser-View-Settings"); const QByteArray windowGeometry = settings.value(QSL("WindowGeometry")).toByteArray(); const QStringList keys = { QSL("LocationBarWidth"), QSL("WebSearchBarWidth"), QSL("SideBarWidth"), QSL("WebViewWidth"), QSL("SideBar") }; QHash uiState; for (const QString &key : keys) { if (settings.contains(key)) { uiState[key] = settings.value(key); } } settings.endGroup(); QWidget* widget = new QWidget(this); widget->setCursor(Qt::ArrowCursor); setCentralWidget(widget); m_mainLayout = new QVBoxLayout(widget); m_mainLayout->setContentsMargins(0, 0, 0, 0); m_mainLayout->setSpacing(0); m_mainSplitter = new QSplitter(this); m_mainSplitter->setObjectName("sidebar-splitter"); m_tabWidget = new TabWidget(this); m_superMenu = new QMenu(this); m_navigationToolbar = new NavigationBar(this); m_bookmarksToolbar = new BookmarksToolbar(this); m_navigationContainer = new NavigationContainer(this); m_navigationContainer->addWidget(m_navigationToolbar); m_navigationContainer->addWidget(m_bookmarksToolbar); m_navigationContainer->setTabBar(m_tabWidget->tabBar()); m_mainSplitter->addWidget(m_tabWidget); m_mainSplitter->setCollapsible(0, false); m_mainLayout->addWidget(m_navigationContainer); m_mainLayout->addWidget(m_mainSplitter); statusBar()->setObjectName("mainwindow-statusbar"); statusBar()->setCursor(Qt::ArrowCursor); m_progressBar = new ProgressBar(statusBar()); m_ipLabel = new QLabel(this); m_ipLabel->setObjectName("statusbar-ip-label"); m_ipLabel->setToolTip(tr("IP Address of current page")); statusBar()->addPermanentWidget(m_progressBar); statusBar()->addPermanentWidget(m_ipLabel); m_navigationToolbar->addToolButton(new DownloadsButton(this)); QDesktopWidget* desktop = mApp->desktop(); int windowWidth = desktop->availableGeometry().width() / 1.3; int windowHeight = desktop->availableGeometry().height() / 1.3; // Let the WM decides where to put new browser window if (m_windowType != Qz::BW_FirstAppWindow && m_windowType != Qz::BW_MacFirstWindow && mApp->getWindow()) { #ifdef Q_WS_WIN // Windows WM places every new window in the middle of screen .. for some reason QPoint p = mApp->getWindow()->geometry().topLeft(); p.setX(p.x() + 30); p.setY(p.y() + 30); if (!desktop->availableGeometry(mApp->getWindow()).contains(p)) { p.setX(desktop->availableGeometry(mApp->getWindow()).x() + 30); p.setY(desktop->availableGeometry(mApp->getWindow()).y() + 30); } setGeometry(QRect(p, mApp->getWindow()->size())); #else resize(mApp->getWindow()->size()); #endif } else if (!restoreGeometry(windowGeometry)) { #ifdef Q_WS_WIN setGeometry(QRect(desktop->availableGeometry(mApp->getWindow()).x() + 30, desktop->availableGeometry(mApp->getWindow()).y() + 30, windowWidth, windowHeight)); #else resize(windowWidth, windowHeight); #endif } // Workaround for Oxygen tooltips not having transparent background QPalette pal = QToolTip::palette(); QColor col = pal.window().color(); col.setAlpha(0); pal.setColor(QPalette::Window, col); QToolTip::setPalette(pal); restoreUiState(uiState); // Set some sane minimum width setMinimumWidth(300); } void BrowserWindow::setupMenu() { #ifdef Q_OS_MACOS static MainMenu* macMainMenu = 0; if (!macMainMenu) { macMainMenu = new MainMenu(this, 0); macMainMenu->initMenuBar(new QMenuBar(0)); connect(mApp, SIGNAL(activeWindowChanged(BrowserWindow*)), macMainMenu, SLOT(setWindow(BrowserWindow*))); } else { macMainMenu->setWindow(this); } m_mainMenu = macMainMenu; #else setMenuBar(new MenuBar(this)); m_mainMenu = new MainMenu(this, this); m_mainMenu->initMenuBar(menuBar()); #endif m_mainMenu->initSuperMenu(m_superMenu); // Setup other shortcuts QShortcut* reloadBypassCacheAction = new QShortcut(QKeySequence(QSL("Ctrl+F5")), this); QShortcut* reloadBypassCacheAction2 = new QShortcut(QKeySequence(QSL("Ctrl+Shift+R")), this); connect(reloadBypassCacheAction, SIGNAL(activated()), this, SLOT(reloadBypassCache())); connect(reloadBypassCacheAction2, SIGNAL(activated()), this, SLOT(reloadBypassCache())); QShortcut* closeTabAction = new QShortcut(QKeySequence(QSL("Ctrl+W")), this); QShortcut* closeTabAction2 = new QShortcut(QKeySequence(QSL("Ctrl+F4")), this); connect(closeTabAction, SIGNAL(activated()), this, SLOT(closeTab())); connect(closeTabAction2, SIGNAL(activated()), this, SLOT(closeTab())); QShortcut* reloadAction = new QShortcut(QKeySequence("Ctrl+R"), this); connect(reloadAction, SIGNAL(activated()), this, SLOT(reload())); QShortcut* openLocationAction = new QShortcut(QKeySequence("Alt+D"), this); connect(openLocationAction, SIGNAL(activated()), this, SLOT(openLocation())); QShortcut* inspectorAction = new QShortcut(QKeySequence(QSL("F12")), this); connect(inspectorAction, SIGNAL(activated()), this, SLOT(toggleWebInspector())); QShortcut* restoreClosedWindow = new QShortcut(QKeySequence(QSL("Ctrl+Shift+N")), this); connect(restoreClosedWindow, &QShortcut::activated, mApp->closedWindowsManager(), &ClosedWindowsManager::restoreClosedWindow); } void BrowserWindow::updateStartupFocus() { QTimer::singleShot(500, this, [this]() { // Scroll to current tab tabWidget()->tabBar()->ensureVisible(); // Update focus if (!m_startPage && LocationBar::convertUrlToText(weView()->page()->requestedUrl()).isEmpty()) locationBar()->setFocus(); else weView()->setFocus(); }); } QAction* BrowserWindow::createEncodingAction(const QString &codecName, const QString &activeCodecName, QMenu* menu) { QAction* action = new QAction(codecName, menu); action->setData(codecName); action->setCheckable(true); connect(action, SIGNAL(triggered()), this, SLOT(changeEncoding())); if (activeCodecName.compare(codecName, Qt::CaseInsensitive) == 0) { action->setChecked(true); } return action; } void BrowserWindow::createEncodingSubMenu(const QString &name, QStringList &codecNames, QMenu* menu) { if (codecNames.isEmpty()) { return; } QCollator collator; collator.setNumericMode(true); std::sort(codecNames.begin(), codecNames.end(), [collator](const QString &a, const QString &b) { return collator.compare(a, b) < 0; }); QMenu* subMenu = new QMenu(name, menu); const QString activeCodecName = mApp->webSettings()->defaultTextEncoding(); QActionGroup *group = new QActionGroup(subMenu); foreach (const QString &codecName, codecNames) { QAction *act = createEncodingAction(codecName, activeCodecName, subMenu); group->addAction(act); subMenu->addAction(act); } menu->addMenu(subMenu); } QHash BrowserWindow::saveUiState() { saveSideBarSettings(); QHash state; state[QSL("LocationBarWidth")] = m_navigationToolbar->splitter()->sizes().at(0); state[QSL("WebSearchBarWidth")] = m_navigationToolbar->splitter()->sizes().at(1); state[QSL("SideBarWidth")] = m_sideBarWidth; state[QSL("WebViewWidth")] = m_webViewWidth; state[QSL("SideBar")] = m_sideBarManager->activeSideBar(); return state; } void BrowserWindow::restoreUiState(const QHash &state) { const int locationBarWidth = state.value(QSL("LocationBarWidth"), 480).toInt(); const int websearchBarWidth = state.value(QSL("WebSearchBarWidth"), 140).toInt(); m_navigationToolbar->setSplitterSizes(locationBarWidth, websearchBarWidth); m_sideBarWidth = state.value(QSL("SideBarWidth"), 250).toInt(); m_webViewWidth = state.value(QSL("WebViewWidth"), 2000).toInt(); if (m_sideBar) { m_mainSplitter->setSizes({m_sideBarWidth, m_webViewWidth}); } const QString activeSideBar = state.value(QSL("SideBar")).toString(); if (activeSideBar.isEmpty() && m_sideBar) { m_sideBar->close(); } else { m_sideBarManager->showSideBar(activeSideBar, false); } } void BrowserWindow::loadSettings() { Settings settings; //Url settings settings.beginGroup("Web-URL-Settings"); m_homepage = settings.value("homepage", "falkon:start").toUrl(); settings.endGroup(); //Browser Window settings settings.beginGroup("Browser-View-Settings"); bool showStatusBar = settings.value("showStatusBar", false).toBool(); bool showBookmarksToolbar = settings.value("showBookmarksToolbar", true).toBool(); bool showNavigationToolbar = settings.value("showNavigationToolbar", true).toBool(); bool showMenuBar = settings.value("showMenubar", false).toBool(); // Make sure both menubar and navigationbar are not hidden // Fixes #781 if (!showNavigationToolbar) { showMenuBar = true; settings.setValue("showMenubar", true); } settings.endGroup(); settings.beginGroup("Shortcuts"); m_useTabNumberShortcuts = settings.value("useTabNumberShortcuts", true).toBool(); m_useSpeedDialNumberShortcuts = settings.value("useSpeedDialNumberShortcuts", true).toBool(); m_useSingleKeyShortcuts = settings.value("useSingleKeyShortcuts", false).toBool(); settings.endGroup(); settings.beginGroup("Web-Browser-Settings"); QAction *quitAction = m_mainMenu->action(QSL("Standard/Quit")); if (settings.value("closeAppWithCtrlQ", true).toBool()) { quitAction->setShortcut(QzTools::actionShortcut(QKeySequence::Quit, QKeySequence(QSL("Ctrl+Q")))); } else { quitAction->setShortcut(QKeySequence()); } settings.endGroup(); statusBar()->setVisible(!isFullScreen() && showStatusBar); m_bookmarksToolbar->setVisible(showBookmarksToolbar); m_navigationToolbar->setVisible(showNavigationToolbar); #ifndef Q_OS_MACOS menuBar()->setVisible(!isFullScreen() && showMenuBar); #endif m_navigationToolbar->setSuperMenuVisible(!showMenuBar); } void BrowserWindow::goForward() { weView()->forward(); } void BrowserWindow::reload() { weView()->reload(); } void BrowserWindow::reloadBypassCache() { weView()->reloadBypassCache(); } void BrowserWindow::goBack() { weView()->back(); } int BrowserWindow::tabCount() const { return m_tabWidget->count(); } TabbedWebView* BrowserWindow::weView() const { return weView(m_tabWidget->currentIndex()); } TabbedWebView* BrowserWindow::weView(int index) const { WebTab* webTab = qobject_cast(m_tabWidget->widget(index)); if (!webTab) { return 0; } return webTab->webView(); } LocationBar* BrowserWindow::locationBar() const { return qobject_cast(m_tabWidget->locationBars()->currentWidget()); } TabWidget* BrowserWindow::tabWidget() const { return m_tabWidget; } BookmarksToolbar* BrowserWindow::bookmarksToolbar() const { return m_bookmarksToolbar; } StatusBarMessage* BrowserWindow::statusBarMessage() const { return m_statusBarMessage; } NavigationBar* BrowserWindow::navigationBar() const { return m_navigationToolbar; } SideBarManager* BrowserWindow::sideBarManager() const { return m_sideBarManager; } QLabel* BrowserWindow::ipLabel() const { return m_ipLabel; } QMenu* BrowserWindow::superMenu() const { return m_superMenu; } QUrl BrowserWindow::homepageUrl() const { return m_homepage; } Qz::BrowserWindowType BrowserWindow::windowType() const { return m_windowType; } QAction* BrowserWindow::action(const QString &name) const { return m_mainMenu->action(name); } void BrowserWindow::setWindowTitle(const QString &t) { QString title = t; if (mApp->isPrivate()) { title.append(tr(" (Private Browsing)")); } QMainWindow::setWindowTitle(title); } void BrowserWindow::changeEncoding() { if (QAction* action = qobject_cast(sender())) { const QString encoding = action->data().toString(); mApp->webSettings()->setDefaultTextEncoding(encoding); Settings settings; settings.setValue("Web-Browser-Settings/DefaultEncoding", encoding); weView()->reload(); } } void BrowserWindow::printPage() { weView()->printPage(); } void BrowserWindow::bookmarkPage() { TabbedWebView* view = weView(); BookmarksTools::addBookmarkDialog(this, view->url(), view->title()); } void BrowserWindow::bookmarkAllTabs() { BookmarksTools::bookmarkAllTabsDialog(this, m_tabWidget); } void BrowserWindow::addBookmark(const QUrl &url, const QString &title) { BookmarksTools::addBookmarkDialog(this, url, title); } void BrowserWindow::goHome() { loadAddress(m_homepage); } void BrowserWindow::goHomeInNewTab() { m_tabWidget->addView(m_homepage, Qz::NT_SelectedTab); } void BrowserWindow::loadActionUrl(QObject* obj) { if (!obj) { obj = sender(); } if (QAction* action = qobject_cast(obj)) { loadAddress(action->data().toUrl()); } } void BrowserWindow::loadActionUrlInNewTab(QObject* obj) { if (!obj) { obj = sender(); } if (QAction* action = qobject_cast(obj)) { m_tabWidget->addView(action->data().toUrl(), Qz::NT_SelectedTabAtTheEnd); } } void BrowserWindow::loadAddress(const QUrl &url) { if (weView()->webTab()->isPinned()) { int index = m_tabWidget->addView(url, qzSettings->newTabPosition); weView(index)->setFocus(); } else { weView()->load(url); weView()->setFocus(); } } void BrowserWindow::showHistoryManager() { mApp->browsingLibrary()->showHistory(this); } void BrowserWindow::showSource(WebView *view) { if (!view) view = weView(); view->showSource(); } void BrowserWindow::showNormal() { if (m_normalWindowState & Qt::WindowMaximized) { QMainWindow::showMaximized(); } else { QMainWindow::showNormal(); } } SideBar* BrowserWindow::addSideBar() { if (m_sideBar) { return m_sideBar.data(); } m_sideBar = new SideBar(m_sideBarManager, this); m_mainSplitter->insertWidget(0, m_sideBar.data()); m_mainSplitter->setCollapsible(0, false); m_mainSplitter->setSizes({m_sideBarWidth, m_webViewWidth}); return m_sideBar.data(); } void BrowserWindow::saveSideBarSettings() { if (m_sideBar) { // That +1 is important here, without it, the sidebar width would // decrease by 1 pixel every close m_sideBarWidth = m_mainSplitter->sizes().at(0) + 1; m_webViewWidth = width() - m_sideBarWidth; } Settings().setValue(QSL("Browser-View-Settings/SideBar"), m_sideBarManager->activeSideBar()); } void BrowserWindow::toggleShowMenubar() { #ifdef Q_OS_MACOS // We use one shared global menubar on Mac that can't be hidden return; #endif setUpdatesEnabled(false); menuBar()->setVisible(!menuBar()->isVisible()); m_navigationToolbar->setSuperMenuVisible(!menuBar()->isVisible()); setUpdatesEnabled(true); Settings().setValue("Browser-View-Settings/showMenubar", menuBar()->isVisible()); // Make sure we show Navigation Toolbar when Menu Bar is hidden if (!m_navigationToolbar->isVisible() && !menuBar()->isVisible()) { toggleShowNavigationToolbar(); } } void BrowserWindow::toggleShowStatusBar() { setUpdatesEnabled(false); statusBar()->setVisible(!statusBar()->isVisible()); setUpdatesEnabled(true); Settings().setValue("Browser-View-Settings/showStatusBar", statusBar()->isVisible()); } void BrowserWindow::toggleShowBookmarksToolbar() { setUpdatesEnabled(false); m_bookmarksToolbar->setVisible(!m_bookmarksToolbar->isVisible()); setUpdatesEnabled(true); Settings().setValue("Browser-View-Settings/showBookmarksToolbar", m_bookmarksToolbar->isVisible()); Settings().setValue("Browser-View-Settings/instantBookmarksToolbar", false); } void BrowserWindow::toggleShowNavigationToolbar() { setUpdatesEnabled(false); m_navigationToolbar->setVisible(!m_navigationToolbar->isVisible()); setUpdatesEnabled(true); Settings().setValue("Browser-View-Settings/showNavigationToolbar", m_navigationToolbar->isVisible()); #ifndef Q_OS_MACOS // Make sure we show Menu Bar when Navigation Toolbar is hidden if (!m_navigationToolbar->isVisible() && !menuBar()->isVisible()) { toggleShowMenubar(); } #endif } void BrowserWindow::toggleTabsOnTop(bool enable) { qzSettings->tabsOnTop = enable; m_navigationContainer->toggleTabsOnTop(enable); } void BrowserWindow::toggleFullScreen() { if (m_isHtmlFullScreen) { weView()->triggerPageAction(QWebEnginePage::ExitFullScreen); return; } if (isFullScreen()) showNormal(); else showFullScreen(); } void BrowserWindow::toggleHtmlFullScreen(bool enable) { if (enable) showFullScreen(); else showNormal(); if (m_sideBar) m_sideBar.data()->setHidden(enable); m_isHtmlFullScreen = enable; } void BrowserWindow::showWebInspector() { if (weView() && weView()->webTab()) { weView()->webTab()->showWebInspector(); } } void BrowserWindow::toggleWebInspector() { if (weView() && weView()->webTab()) { weView()->webTab()->toggleWebInspector(); } } void BrowserWindow::refreshHistory() { m_navigationToolbar->refreshHistory(); } void BrowserWindow::currentTabChanged() { TabbedWebView* view = weView(); m_navigationToolbar->setCurrentView(view); if (!view) { return; } - setWindowTitle(tr("%1 - Falkon").arg(view->webTab()->title())); + const QString title = view->webTab()->title(/*allowEmpty*/true); + if (title.isEmpty()) { + setWindowTitle(tr("Falkon")); + } else { + setWindowTitle(tr("%1 - Falkon").arg(title)); + } m_ipLabel->setText(view->getIp()); view->setFocus(); updateLoadingActions(); // Setting correct tab order (LocationBar -> WebSearchBar -> WebView) setTabOrder(locationBar(), m_navigationToolbar->webSearchBar()); setTabOrder(m_navigationToolbar->webSearchBar(), view); } void BrowserWindow::updateLoadingActions() { TabbedWebView* view = weView(); if (!view) { return; } bool isLoading = view->isLoading(); m_ipLabel->setVisible(!isLoading); m_progressBar->setVisible(isLoading); action(QSL("View/Stop"))->setEnabled(isLoading); action(QSL("View/Reload"))->setEnabled(!isLoading); if (isLoading) { m_progressBar->setValue(view->loadingProgress()); m_navigationToolbar->showStopButton(); } else { m_navigationToolbar->showReloadButton(); } } void BrowserWindow::addDeleteOnCloseWidget(QWidget* widget) { if (!m_deleteOnCloseWidgets.contains(widget)) { m_deleteOnCloseWidgets.append(widget); } } void BrowserWindow::restoreWindow(const SavedWindow &window) { restoreState(window.windowState); restoreGeometry(window.windowGeometry); restoreUiState(window.windowUiState); #ifdef QZ_WS_X11 moveToVirtualDesktop(window.virtualDesktop); #endif show(); // Window has to be visible before adding QWebEngineView's m_tabWidget->restoreState(window.tabs, window.currentTab); updateStartupFocus(); } void BrowserWindow::createToolbarsMenu(QMenu* menu) { removeActions(menu->actions()); menu->clear(); QAction* action; #ifndef Q_OS_MACOS action = menu->addAction(tr("&Menu Bar"), this, SLOT(toggleShowMenubar())); action->setCheckable(true); action->setChecked(menuBar()->isVisible()); #endif action = menu->addAction(tr("&Navigation Toolbar"), this, SLOT(toggleShowNavigationToolbar())); action->setCheckable(true); action->setChecked(m_navigationToolbar->isVisible()); action = menu->addAction(tr("&Bookmarks Toolbar"), this, SLOT(toggleShowBookmarksToolbar())); action->setCheckable(true); action->setChecked(Settings().value("Browser-View-Settings/showBookmarksToolbar").toBool()); menu->addSeparator(); action = menu->addAction(tr("&Tabs on Top"), this, SLOT(toggleTabsOnTop(bool))); action->setCheckable(true); action->setChecked(qzSettings->tabsOnTop); addActions(menu->actions()); } void BrowserWindow::createSidebarsMenu(QMenu* menu) { m_sideBarManager->createMenu(menu); } void BrowserWindow::createEncodingMenu(QMenu* menu) { const QString activeCodecName = mApp->webSettings()->defaultTextEncoding(); QStringList isoCodecs; QStringList utfCodecs; QStringList windowsCodecs; QStringList isciiCodecs; QStringList ibmCodecs; QStringList otherCodecs; QStringList allCodecs; foreach (const int mib, QTextCodec::availableMibs()) { const QString codecName = QString::fromUtf8(QTextCodec::codecForMib(mib)->name()); if (!allCodecs.contains(codecName)) allCodecs.append(codecName); else continue; if (codecName.startsWith(QLatin1String("ISO"))) isoCodecs.append(codecName); else if (codecName.startsWith(QLatin1String("UTF"))) utfCodecs.append(codecName); else if (codecName.startsWith(QLatin1String("windows"))) windowsCodecs.append(codecName); else if (codecName.startsWith(QLatin1String("Iscii"))) isciiCodecs.append(codecName); else if (codecName.startsWith(QLatin1String("IBM"))) ibmCodecs.append(codecName); else otherCodecs.append(codecName); } if (!menu->isEmpty()) menu->addSeparator(); createEncodingSubMenu("ISO", isoCodecs, menu); createEncodingSubMenu("UTF", utfCodecs, menu); createEncodingSubMenu("Windows", windowsCodecs, menu); createEncodingSubMenu("Iscii", isciiCodecs, menu); createEncodingSubMenu("IBM", ibmCodecs, menu); createEncodingSubMenu(tr("Other"), otherCodecs, menu); } void BrowserWindow::removeActions(const QList &actions) { foreach (QAction *action, actions) { removeAction(action); } } void BrowserWindow::addTab() { m_tabWidget->addView(QUrl(), Qz::NT_SelectedNewEmptyTab, true); m_tabWidget->setCurrentTabFresh(true); if (isFullScreen()) showNavigationWithFullScreen(); } void BrowserWindow::webSearch() { m_navigationToolbar->webSearchBar()->setFocus(); m_navigationToolbar->webSearchBar()->selectAll(); } void BrowserWindow::searchOnPage() { if (weView() && weView()->webTab()) { weView()->webTab()->showSearchToolBar(); } } void BrowserWindow::openFile() { const QString fileTypes = QString("%1(*.html *.htm *.shtml *.shtm *.xhtml);;" "%2(*.png *.jpg *.jpeg *.bmp *.gif *.svg *.tiff);;" "%3(*.txt);;" "%4(*.*)").arg(tr("HTML files"), tr("Image files"), tr("Text files"), tr("All files")); const QString filePath = QzTools::getOpenFileName("MainWindow-openFile", this, tr("Open file..."), QDir::homePath(), fileTypes); if (!filePath.isEmpty()) { loadAddress(QUrl::fromLocalFile(filePath)); } } void BrowserWindow::openLocation() { if (isFullScreen()) { showNavigationWithFullScreen(); } locationBar()->setFocus(); locationBar()->selectAll(); } bool BrowserWindow::fullScreenNavigationVisible() const { return m_navigationContainer->isVisible(); } void BrowserWindow::showNavigationWithFullScreen() { if (m_isHtmlFullScreen) return; if (m_hideNavigationTimer->isActive()) { m_hideNavigationTimer->stop(); } m_navigationContainer->show(); } void BrowserWindow::hideNavigationWithFullScreen() { if (m_tabWidget->isCurrentTabFresh()) return; if (!m_hideNavigationTimer->isActive()) { m_hideNavigationTimer->start(); } } void BrowserWindow::hideNavigationSlot() { TabbedWebView* view = weView(); bool mouseInView = view && view->underMouse(); if (isFullScreen() && mouseInView) { m_navigationContainer->hide(); } } bool BrowserWindow::event(QEvent* event) { switch (event->type()) { case QEvent::WindowStateChange: if (!(m_oldWindowState & Qt::WindowFullScreen) && windowState() & Qt::WindowFullScreen) { // Enter fullscreen m_normalWindowState = m_oldWindowState; m_statusBarVisible = statusBar()->isVisible(); #ifndef Q_OS_MACOS m_menuBarVisible = menuBar()->isVisible(); menuBar()->hide(); #endif statusBar()->hide(); m_navigationContainer->hide(); m_navigationToolbar->enterFullScreen(); } else if (m_oldWindowState & Qt::WindowFullScreen && !(windowState() & Qt::WindowFullScreen)) { // Leave fullscreen statusBar()->setVisible(m_statusBarVisible); #ifndef Q_OS_MACOS menuBar()->setVisible(m_menuBarVisible); #endif m_navigationContainer->show(); m_navigationToolbar->setSuperMenuVisible(!m_menuBarVisible); m_navigationToolbar->leaveFullScreen(); m_isHtmlFullScreen = false; } if (m_hideNavigationTimer) { m_hideNavigationTimer->stop(); } m_oldWindowState = windowState(); break; default: break; } return QMainWindow::event(event); } void BrowserWindow::resizeEvent(QResizeEvent* event) { m_bookmarksToolbar->setMaximumWidth(width()); QMainWindow::resizeEvent(event); } void BrowserWindow::keyPressEvent(QKeyEvent* event) { if (mApp->plugins()->processKeyPress(Qz::ON_BrowserWindow, this, event)) { return; } int number = -1; TabbedWebView* view = weView(); switch (event->key()) { case Qt::Key_Back: if (view) { view->back(); event->accept(); } break; case Qt::Key_Forward: if (view) { view->forward(); event->accept(); } break; case Qt::Key_Stop: if (view) { view->stop(); event->accept(); } break; case Qt::Key_Reload: case Qt::Key_Refresh: if (view) { view->reload(); event->accept(); } break; case Qt::Key_HomePage: goHome(); event->accept(); break; case Qt::Key_Favorites: mApp->browsingLibrary()->showBookmarks(this); event->accept(); break; case Qt::Key_Search: searchOnPage(); event->accept(); break; case Qt::Key_F6: case Qt::Key_OpenUrl: openLocation(); event->accept(); break; case Qt::Key_History: showHistoryManager(); event->accept(); break; case Qt::Key_AddFavorite: bookmarkPage(); event->accept(); break; case Qt::Key_News: action(QSL("Tools/RssReader"))->trigger(); event->accept(); break; case Qt::Key_Tools: action(QSL("Standard/Preferences"))->trigger(); event->accept(); break; case Qt::Key_Tab: if (event->modifiers() == Qt::ControlModifier) { m_tabWidget->nextTab(); event->accept(); } break; case Qt::Key_Backtab: if (event->modifiers() == (Qt::ControlModifier + Qt::ShiftModifier)) { m_tabWidget->previousTab(); event->accept(); } break; case Qt::Key_PageDown: if (event->modifiers() == Qt::ControlModifier) { m_tabWidget->nextTab(); event->accept(); } break; case Qt::Key_PageUp: if (event->modifiers() == Qt::ControlModifier) { m_tabWidget->previousTab(); event->accept(); } break; case Qt::Key_Equal: if (view && event->modifiers() == Qt::ControlModifier) { view->zoomIn(); event->accept(); } break; case Qt::Key_I: if (event->modifiers() == Qt::ControlModifier) { action(QSL("Tools/SiteInfo"))->trigger(); event->accept(); } break; case Qt::Key_U: if (event->modifiers() == Qt::ControlModifier) { action(QSL("View/PageSource"))->trigger(); event->accept(); } break; case Qt::Key_F: if (event->modifiers() == Qt::ControlModifier) { action(QSL("Edit/Find"))->trigger(); event->accept(); } break; case Qt::Key_Slash: if (m_useSingleKeyShortcuts) { action(QSL("Edit/Find"))->trigger(); event->accept(); } break; case Qt::Key_1: number = 1; break; case Qt::Key_2: number = 2; break; case Qt::Key_3: number = 3; break; case Qt::Key_4: number = 4; break; case Qt::Key_5: number = 5; break; case Qt::Key_6: number = 6; break; case Qt::Key_7: number = 7; break; case Qt::Key_8: number = 8; break; case Qt::Key_9: number = 9; break; default: break; } if (number != -1) { if (event->modifiers() & Qt::AltModifier && m_useTabNumberShortcuts) { if (number == 9) { number = m_tabWidget->count(); } m_tabWidget->setCurrentIndex(number - 1); event->accept(); return; } if (event->modifiers() & Qt::ControlModifier && m_useSpeedDialNumberShortcuts) { const QUrl url = mApp->plugins()->speedDial()->urlForShortcut(number - 1); if (url.isValid()) { loadAddress(url); event->accept(); return; } } if (event->modifiers() == Qt::NoModifier && m_useSingleKeyShortcuts) { if (number == 1) m_tabWidget->previousTab(); if (number == 2) m_tabWidget->nextTab(); } } QMainWindow::keyPressEvent(event); } void BrowserWindow::keyReleaseEvent(QKeyEvent* event) { if (mApp->plugins()->processKeyRelease(Qz::ON_BrowserWindow, this, event)) { return; } switch (event->key()) { case Qt::Key_F: if (event->modifiers() == Qt::ControlModifier) { action(QSL("Edit/Find"))->trigger(); event->accept(); } break; default: break; } QMainWindow::keyReleaseEvent(event); } void BrowserWindow::closeEvent(QCloseEvent* event) { if (mApp->isClosing()) { saveSettings(); return; } Settings settings; bool askOnClose = settings.value("Browser-Tabs-Settings/AskOnClosing", true).toBool(); if ((mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) && mApp->windowCount() == 1) { askOnClose = false; } if (askOnClose && m_tabWidget->normalTabsCount() > 1) { CheckBoxDialog dialog(QMessageBox::Yes | QMessageBox::No, this); dialog.setDefaultButton(QMessageBox::No); //~ singular There is still %n open tab and your session won't be stored.\nAre you sure you want to close this window? //~ plural There are still %n open tabs and your session won't be stored.\nAre you sure you want to close this window? dialog.setText(tr("There are still %n open tabs and your session won't be stored.\nAre you sure you want to close this window?", "", m_tabWidget->count())); dialog.setCheckBoxText(tr("Don't ask again")); dialog.setWindowTitle(tr("There are still open tabs")); dialog.setIcon(QMessageBox::Warning); if (dialog.exec() != QMessageBox::Yes) { event->ignore(); return; } if (dialog.isChecked()) { settings.setValue("Browser-Tabs-Settings/AskOnClosing", false); } } saveSettings(); mApp->closedWindowsManager()->saveWindow(this); #ifndef Q_OS_MACOS if (mApp->windowCount() == 1) mApp->quitApplication(); #endif event->accept(); } void BrowserWindow::closeWindow() { #ifdef Q_OS_MACOS close(); return; #endif if (mApp->windowCount() > 1) { close(); } } void BrowserWindow::saveSettings() { if (mApp->isPrivate()) { return; } Settings settings; settings.beginGroup("Browser-View-Settings"); settings.setValue("WindowGeometry", saveGeometry()); const auto state = saveUiState(); for (auto it = state.constBegin(); it != state.constEnd(); ++it) { settings.setValue(it.key(), it.value()); } settings.endGroup(); } void BrowserWindow::closeTab() { // Don't close pinned tabs with keyboard shortcuts (Ctrl+W, Ctrl+F4) if (weView() && !weView()->webTab()->isPinned()) { m_tabWidget->requestCloseTab(); } } #ifdef QZ_WS_X11 int BrowserWindow::getCurrentVirtualDesktop() const { if (QGuiApplication::platformName() != QL1S("xcb")) return 0; xcb_intern_atom_cookie_t intern_atom; xcb_intern_atom_reply_t *atom_reply = 0; xcb_atom_t atom; xcb_get_property_cookie_t cookie; xcb_get_property_reply_t *reply = 0; uint32_t value; intern_atom = xcb_intern_atom(QX11Info::connection(), false, qstrlen("_NET_WM_DESKTOP"), "_NET_WM_DESKTOP"); atom_reply = xcb_intern_atom_reply(QX11Info::connection(), intern_atom, 0); if (!atom_reply) goto error; atom = atom_reply->atom; cookie = xcb_get_property(QX11Info::connection(), false, winId(), atom, XCB_ATOM_CARDINAL, 0, 1); reply = xcb_get_property_reply(QX11Info::connection(), cookie, 0); if (!reply || reply->type != XCB_ATOM_CARDINAL || reply->value_len != 1 || reply->format != sizeof(uint32_t) * 8) goto error; value = *reinterpret_cast(xcb_get_property_value(reply)); free(reply); free(atom_reply); return value; error: free(reply); free(atom_reply); return 0; } void BrowserWindow::moveToVirtualDesktop(int desktopId) { if (QGuiApplication::platformName() != QL1S("xcb")) return; // Don't move when window is already visible or it is first app window if (desktopId < 0 || isVisible() || m_windowType == Qz::BW_FirstAppWindow) return; xcb_intern_atom_cookie_t intern_atom; xcb_intern_atom_reply_t *atom_reply = 0; xcb_atom_t atom; intern_atom = xcb_intern_atom(QX11Info::connection(), false, qstrlen("_NET_WM_DESKTOP"), "_NET_WM_DESKTOP"); atom_reply = xcb_intern_atom_reply(QX11Info::connection(), intern_atom, 0); if (!atom_reply) goto error; atom = atom_reply->atom; xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, winId(), atom, XCB_ATOM_CARDINAL, 32, 1, (const void*) &desktopId); error: free(atom_reply); } #endif diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp index 39449fe2..36691df4 100644 --- a/src/lib/webengine/webview.cpp +++ b/src/lib/webengine/webview.cpp @@ -1,1363 +1,1362 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 . * ============================================================ */ #include "webview.h" #include "webpage.h" #include "mainapplication.h" #include "qztools.h" #include "iconprovider.h" #include "history.h" #include "pluginproxy.h" #include "downloadmanager.h" #include "siteinfo.h" #include "searchenginesmanager.h" #include "browsinglibrary.h" #include "bookmarkstools.h" #include "settings.h" #include "qzsettings.h" #include "enhancedmenu.h" #include "locationbar.h" #include "webinspector.h" #include "scripts.h" #include "webhittestresult.h" #include "webscrollbarmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include bool WebView::s_forceContextMenuOnMouseRelease = false; WebView::WebView(QWidget* parent) : QWebEngineView(parent) , m_progress(100) , m_backgroundActivity(false) , m_page(0) , m_firstLoad(false) { connect(this, &QWebEngineView::loadStarted, this, &WebView::slotLoadStarted); connect(this, &QWebEngineView::loadProgress, this, &WebView::slotLoadProgress); connect(this, &QWebEngineView::loadFinished, this, &WebView::slotLoadFinished); connect(this, &QWebEngineView::iconChanged, this, &WebView::slotIconChanged); connect(this, &QWebEngineView::urlChanged, this, &WebView::slotUrlChanged); connect(this, &QWebEngineView::titleChanged, this, &WebView::slotTitleChanged); m_currentZoomLevel = zoomLevels().indexOf(100); setAcceptDrops(true); installEventFilter(this); if (parentWidget()) { parentWidget()->installEventFilter(this); } WebInspector::registerView(this); // Hack to find widget that receives input events QStackedLayout *l = qobject_cast(layout()); connect(l, &QStackedLayout::currentChanged, this, [this]() { QTimer::singleShot(0, this, [this]() { m_rwhvqt = focusProxy(); if (!m_rwhvqt) { qCritical() << "Focus proxy is null!"; return; } m_rwhvqt->installEventFilter(this); }); }); } WebView::~WebView() { WebInspector::unregisterView(this); WebScrollBarManager::instance()->removeWebView(this); } QIcon WebView::icon(bool allowNull) const { if (!QWebEngineView::icon().isNull()) { return QWebEngineView::icon(); } if (url().scheme() == QLatin1String("ftp")) { return IconProvider::standardIcon(QStyle::SP_ComputerIcon); } if (url().scheme() == QLatin1String("file")) { return IconProvider::standardIcon(QStyle::SP_DriveHDIcon); } return IconProvider::iconForUrl(url(), allowNull); } -QString WebView::title() const +QString WebView::title(bool allowEmpty) const { QString title = QWebEngineView::title(); + if (allowEmpty) { + return title; + } + if (title.isEmpty()) { title = url().toString(QUrl::RemoveFragment); } - if (title.isEmpty() || title == QLatin1String("about:blank")) { + if (title.isEmpty() || title == QL1S("about:blank")) { return tr("Empty Page"); } return title; } -bool WebView::isTitleEmpty() const -{ - return QWebEngineView::title().isEmpty(); -} - WebPage* WebView::page() const { return m_page; } void WebView::setPage(WebPage *page) { if (m_page == page) { return; } if (m_page) { m_page->setView(nullptr); m_page->deleteLater(); } m_page = page; m_page->setParent(this); QWebEngineView::setPage(m_page); connect(m_page, SIGNAL(privacyChanged(bool)), this, SIGNAL(privacyChanged(bool))); // Set default zoom level zoomReset(); // Actions needs to be initialized for every QWebEnginePage change initializeActions(); // Scrollbars must be added only after QWebEnginePage is set WebScrollBarManager::instance()->addWebView(this); emit pageChanged(m_page); mApp->plugins()->emitWebPageCreated(m_page); } void WebView::load(const QUrl &url) { QWebEngineView::load(url); if (!m_firstLoad) { m_firstLoad = true; WebInspector::pushView(this); } } void WebView::load(const LoadRequest &request) { const QUrl reqUrl = request.url(); if (reqUrl.isEmpty()) return; if (reqUrl.scheme() == QL1S("javascript")) { const QString scriptSource = reqUrl.toString().mid(11); // Is the javascript source percent encoded or not? // Looking for % character in source should work in most cases if (scriptSource.contains(QL1C('%'))) page()->runJavaScript(QUrl::fromPercentEncoding(scriptSource.toUtf8())); else page()->runJavaScript(scriptSource); return; } if (isUrlValid(reqUrl)) { loadRequest(request); return; } // Make sure to correctly load hosts like localhost (eg. without the dot) if (!reqUrl.isEmpty() && reqUrl.scheme().isEmpty() && !QzTools::containsSpace(reqUrl.path()) && // See #1622 !reqUrl.path().contains(QL1C('.')) ) { QUrl u(QSL("http://") + reqUrl.path()); if (u.isValid()) { // This is blocking... QHostInfo info = QHostInfo::fromName(u.path()); if (info.error() == QHostInfo::NoError) { LoadRequest req = request; req.setUrl(u); loadRequest(req); return; } } } if (qzSettings->searchFromAddressBar) { const LoadRequest searchRequest = mApp->searchEnginesManager()->searchResult(request.urlString()); loadRequest(searchRequest); } } bool WebView::isLoading() const { return m_progress < 100; } int WebView::loadingProgress() const { return m_progress; } bool WebView::backgroundActivity() const { return m_backgroundActivity; } int WebView::zoomLevel() const { return m_currentZoomLevel; } void WebView::setZoomLevel(int level) { m_currentZoomLevel = level; applyZoom(); } QPointF WebView::mapToViewport(const QPointF &pos) const { return page()->mapToViewport(pos); } QRect WebView::scrollBarGeometry(Qt::Orientation orientation) const { QScrollBar *s = WebScrollBarManager::instance()->scrollBar(orientation, const_cast(this)); return s && s->isVisible() ? s->geometry() : QRect(); } QWidget *WebView::inputWidget() const { return m_rwhvqt ? m_rwhvqt : const_cast(this); } // static bool WebView::isUrlValid(const QUrl &url) { // Valid url must have scheme and actually contains something (therefore scheme:// is invalid) return url.isValid() && !url.scheme().isEmpty() && (!url.host().isEmpty() || !url.path().isEmpty() || url.hasQuery()); } // static QList WebView::zoomLevels() { return QList() << 30 << 40 << 50 << 67 << 80 << 90 << 100 << 110 << 120 << 133 << 150 << 170 << 200 << 220 << 233 << 250 << 270 << 285 << 300; } // static bool WebView::forceContextMenuOnMouseRelease() { return s_forceContextMenuOnMouseRelease; } // static void WebView::setForceContextMenuOnMouseRelease(bool force) { s_forceContextMenuOnMouseRelease = force; } void WebView::addNotification(QWidget* notif) { emit showNotification(notif); } void WebView::applyZoom() { setZoomFactor(qreal(zoomLevels().at(m_currentZoomLevel)) / 100.0); emit zoomLevelChanged(m_currentZoomLevel); } void WebView::zoomIn() { if (m_currentZoomLevel < zoomLevels().count() - 1) { m_currentZoomLevel++; applyZoom(); } } void WebView::zoomOut() { if (m_currentZoomLevel > 0) { m_currentZoomLevel--; applyZoom(); } } void WebView::zoomReset() { if (m_currentZoomLevel != qzSettings->defaultZoomLevel) { m_currentZoomLevel = qzSettings->defaultZoomLevel; applyZoom(); } } void WebView::editUndo() { triggerPageAction(QWebEnginePage::Undo); } void WebView::editRedo() { triggerPageAction(QWebEnginePage::Redo); } void WebView::editCut() { triggerPageAction(QWebEnginePage::Cut); } void WebView::editCopy() { triggerPageAction(QWebEnginePage::Copy); } void WebView::editPaste() { triggerPageAction(QWebEnginePage::Paste); } void WebView::editSelectAll() { triggerPageAction(QWebEnginePage::SelectAll); } void WebView::editDelete() { QKeyEvent ev(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier); QApplication::sendEvent(this, &ev); } void WebView::reloadBypassCache() { triggerPageAction(QWebEnginePage::ReloadAndBypassCache); } void WebView::back() { QWebEngineHistory* history = page()->history(); if (history->canGoBack()) { history->back(); emit urlChanged(url()); } } void WebView::forward() { QWebEngineHistory* history = page()->history(); if (history->canGoForward()) { history->forward(); emit urlChanged(url()); } } void WebView::printPage() { Q_ASSERT(m_page); QPrintDialog* dialog = new QPrintDialog(this); dialog->setOptions(QAbstractPrintDialog::PrintToFile | QAbstractPrintDialog::PrintShowPageSize); #ifndef Q_OS_WIN dialog->setOption(QAbstractPrintDialog::PrintPageRange); dialog->setOption(QAbstractPrintDialog::PrintCollateCopies); #endif dialog->printer()->setCreator(tr("Falkon %1 (%2)").arg(Qz::VERSION, Qz::WWWADDRESS)); dialog->printer()->setDocName(QzTools::getFileNameFromUrl(url())); if (dialog->exec() == QDialog::Accepted) { if (dialog->printer()->outputFormat() == QPrinter::PdfFormat) { m_page->printToPdf(dialog->printer()->outputFileName(), dialog->printer()->pageLayout()); delete dialog; } else { m_page->print(dialog->printer(), [=](bool success) { Q_UNUSED(success); delete dialog; }); } } } void WebView::slotLoadStarted() { m_progress = 0; } void WebView::slotLoadProgress(int progress) { if (m_progress < 100) { m_progress = progress; } // QtWebEngine sometimes forgets applied zoom factor if (!qFuzzyCompare(zoomFactor(), zoomLevels().at(m_currentZoomLevel) / 100.0)) { applyZoom(); } } void WebView::slotLoadFinished(bool ok) { m_progress = 100; if (ok) mApp->history()->addHistoryEntry(this); } void WebView::slotIconChanged() { IconProvider::instance()->saveIcon(this); } void WebView::slotUrlChanged(const QUrl &url) { Q_UNUSED(url) // Don't save blank page / speed dial in tab history if (!history()->canGoForward() && history()->backItems(1).size() == 1) { const QString s = LocationBar::convertUrlToText(history()->backItem().url()); if (s.isEmpty()) history()->clear(); } } void WebView::slotTitleChanged(const QString &title) { Q_UNUSED(title) if (!isVisible() && !isLoading() && !m_backgroundActivity) { m_backgroundActivity = true; emit backgroundActivityChanged(m_backgroundActivity); } } void WebView::openUrlInNewWindow() { if (QAction* action = qobject_cast(sender())) { mApp->createWindow(Qz::BW_NewWindow, action->data().toUrl()); } } void WebView::sendTextByMail() { if (QAction* action = qobject_cast(sender())) { const QUrl mailUrl = QUrl::fromEncoded("mailto:%20?body=" + QUrl::toPercentEncoding(action->data().toString())); QDesktopServices::openUrl(mailUrl); } } void WebView::sendPageByMail() { const QUrl mailUrl = QUrl::fromEncoded("mailto:%20?body=" + QUrl::toPercentEncoding(url().toEncoded()) + "&subject=" + QUrl::toPercentEncoding(title())); QDesktopServices::openUrl(mailUrl); } void WebView::copyLinkToClipboard() { if (QAction* action = qobject_cast(sender())) { QApplication::clipboard()->setText(action->data().toUrl().toEncoded()); } } void WebView::savePageAs() { triggerPageAction(QWebEnginePage::SavePage); } void WebView::copyImageToClipboard() { triggerPageAction(QWebEnginePage::CopyImageToClipboard); } void WebView::downloadLinkToDisk() { triggerPageAction(QWebEnginePage::DownloadLinkToDisk); } void WebView::downloadImageToDisk() { triggerPageAction(QWebEnginePage::DownloadImageToDisk); } void WebView::downloadMediaToDisk() { triggerPageAction(QWebEnginePage::DownloadMediaToDisk); } void WebView::openUrlInNewTab(const QUrl &url, Qz::NewTabPositionFlags position) { loadInNewTab(url, position); } void WebView::openActionUrl() { if (QAction* action = qobject_cast(sender())) { load(action->data().toUrl()); } } void WebView::showSource() { // view-source: doesn't work on itself and custom schemes if (url().scheme() == QL1S("view-source") || url().scheme() == QL1S("falkon") || url().scheme() == QL1S("qrc")) { page()->toHtml([](const QString &html) { std::cout << html.toLocal8Bit().constData() << std::endl; }); return; } triggerPageAction(QWebEnginePage::ViewSource); } void WebView::showSiteInfo() { SiteInfo* s = new SiteInfo(this); s->show(); } void WebView::searchSelectedText() { SearchEngine engine = mApp->searchEnginesManager()->defaultEngine(); if (QAction* act = qobject_cast(sender())) { if (act->data().isValid()) { engine = act->data().value(); } } const LoadRequest req = mApp->searchEnginesManager()->searchResult(engine, selectedText()); loadInNewTab(req, Qz::NT_SelectedTab); } void WebView::searchSelectedTextInBackgroundTab() { SearchEngine engine = mApp->searchEnginesManager()->defaultEngine(); if (QAction* act = qobject_cast(sender())) { if (act->data().isValid()) { engine = act->data().value(); } } const LoadRequest req = mApp->searchEnginesManager()->searchResult(engine, selectedText()); loadInNewTab(req, Qz::NT_NotSelectedTab); } void WebView::bookmarkLink() { if (QAction* action = qobject_cast(sender())) { if (action->data().isNull()) { BookmarksTools::addBookmarkDialog(this, url(), title()); } else { const QVariantList bData = action->data().value(); const QString bookmarkTitle = bData.at(1).toString().isEmpty() ? title() : bData.at(1).toString(); BookmarksTools::addBookmarkDialog(this, bData.at(0).toUrl(), bookmarkTitle); } } } void WebView::openUrlInSelectedTab() { if (QAction* action = qobject_cast(sender())) { openUrlInNewTab(action->data().toUrl(), Qz::NT_CleanSelectedTab); } } void WebView::openUrlInBackgroundTab() { if (QAction* action = qobject_cast(sender())) { openUrlInNewTab(action->data().toUrl(), Qz::NT_CleanNotSelectedTab); } } void WebView::userDefinedOpenUrlInNewTab(const QUrl &url, bool invert) { Qz::NewTabPositionFlags position = qzSettings->newTabPosition; if (invert) { if (position & Qz::NT_SelectedTab) { position &= ~Qz::NT_SelectedTab; position |= Qz::NT_NotSelectedTab; } else { position &= ~Qz::NT_NotSelectedTab; position |= Qz::NT_SelectedTab; } } QUrl actionUrl; if (!url.isEmpty()) { actionUrl = url; } else if (QAction* action = qobject_cast(sender())) { actionUrl = action->data().toUrl(); } openUrlInNewTab(actionUrl, position); } void WebView::userDefinedOpenUrlInBgTab(const QUrl &url) { QUrl actionUrl; if (!url.isEmpty()) { actionUrl = url; } else if (QAction* action = qobject_cast(sender())) { actionUrl = action->data().toUrl(); } userDefinedOpenUrlInNewTab(actionUrl, true); } void WebView::showEvent(QShowEvent *event) { QWebEngineView::showEvent(event); if (m_backgroundActivity) { m_backgroundActivity = false; emit backgroundActivityChanged(m_backgroundActivity); } } void WebView::createContextMenu(QMenu *menu, WebHitTestResult &hitTest) { // cppcheck-suppress variableScope int spellCheckActionCount = 0; const QWebEngineContextMenuData &contextMenuData = page()->contextMenuData(); hitTest.updateWithContextMenuData(contextMenuData); if (!contextMenuData.misspelledWord().isEmpty()) { QFont boldFont = menu->font(); boldFont.setBold(true); for (const QString &suggestion : contextMenuData.spellCheckerSuggestions()) { QAction *action = menu->addAction(suggestion); action->setFont(boldFont); connect(action, &QAction::triggered, this, [=]() { page()->replaceMisspelledWord(suggestion); }); } if (menu->actions().isEmpty()) { menu->addAction(tr("No suggestions"))->setEnabled(false); } menu->addSeparator(); spellCheckActionCount = menu->actions().count(); } if (!hitTest.linkUrl().isEmpty() && hitTest.linkUrl().scheme() != QL1S("javascript")) { createLinkContextMenu(menu, hitTest); } if (!hitTest.imageUrl().isEmpty()) { createImageContextMenu(menu, hitTest); } if (!hitTest.mediaUrl().isEmpty()) { createMediaContextMenu(menu, hitTest); } if (hitTest.isContentEditable()) { // This only checks if the menu is empty (only spellchecker actions added) if (menu->actions().count() == spellCheckActionCount) { menu->addAction(pageAction(QWebEnginePage::Undo)); menu->addAction(pageAction(QWebEnginePage::Redo)); menu->addSeparator(); menu->addAction(pageAction(QWebEnginePage::Cut)); menu->addAction(pageAction(QWebEnginePage::Copy)); menu->addAction(pageAction(QWebEnginePage::Paste)); } if (hitTest.tagName() == QL1S("input")) { QAction *act = menu->addAction(QString()); act->setVisible(false); checkForForm(act, hitTest.pos()); } } if (!selectedText().isEmpty()) { createSelectedTextContextMenu(menu, hitTest); } if (menu->isEmpty()) { createPageContextMenu(menu); } menu->addSeparator(); mApp->plugins()->populateWebViewMenu(menu, this, hitTest); } void WebView::createPageContextMenu(QMenu* menu) { QAction* action = menu->addAction(tr("&Back"), this, SLOT(back())); action->setIcon(IconProvider::standardIcon(QStyle::SP_ArrowBack)); action->setEnabled(history()->canGoBack()); action = menu->addAction(tr("&Forward"), this, SLOT(forward())); action->setIcon(IconProvider::standardIcon(QStyle::SP_ArrowForward)); action->setEnabled(history()->canGoForward()); // Special menu for Speed Dial page if (url().toString() == QL1S("falkon:speeddial")) { menu->addSeparator(); menu->addAction(QIcon::fromTheme("list-add"), tr("&Add New Page"), this, SLOT(addSpeedDial())); menu->addAction(IconProvider::settingsIcon(), tr("&Configure Speed Dial"), this, SLOT(configureSpeedDial())); menu->addSeparator(); menu->addAction(QIcon::fromTheme(QSL("view-refresh")), tr("Reload All Dials"), this, SLOT(reloadAllSpeedDials())); return; } QAction *reloadAction = pageAction(QWebEnginePage::Reload); action = menu->addAction(reloadAction->icon(), reloadAction->text(), reloadAction, &QAction::trigger); action->setVisible(reloadAction->isEnabled()); connect(reloadAction, &QAction::changed, action, [=]() { action->setVisible(reloadAction->isEnabled()); }); QAction *stopAction = pageAction(QWebEnginePage::Stop); action = menu->addAction(stopAction->icon(), stopAction->text(), stopAction, &QAction::trigger); action->setVisible(stopAction->isEnabled()); connect(stopAction, &QAction::changed, action, [=]() { action->setVisible(stopAction->isEnabled()); }); menu->addSeparator(); menu->addAction(QIcon::fromTheme("bookmark-new"), tr("Book&mark page"), this, SLOT(bookmarkLink())); menu->addAction(QIcon::fromTheme("document-save"), tr("&Save page as..."), this, SLOT(savePageAs())); menu->addAction(QIcon::fromTheme("edit-copy"), tr("&Copy page link"), this, SLOT(copyLinkToClipboard()))->setData(url()); menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send page link..."), this, SLOT(sendPageByMail())); menu->addSeparator(); menu->addAction(QIcon::fromTheme("edit-select-all"), tr("Select &all"), this, SLOT(editSelectAll())); menu->addSeparator(); if (url().scheme() == QLatin1String("http") || url().scheme() == QLatin1String("https")) { const QUrl w3url = QUrl::fromEncoded("http://validator.w3.org/check?uri=" + QUrl::toPercentEncoding(url().toEncoded())); menu->addAction(QIcon(":icons/sites/w3.png"), tr("Validate page"), this, SLOT(openUrlInSelectedTab()))->setData(w3url); QByteArray langCode = mApp->currentLanguage().left(2).toUtf8(); const QUrl gturl = QUrl::fromEncoded("http://translate.google.com/translate?sl=auto&tl=" + langCode + "&u=" + QUrl::toPercentEncoding(url().toEncoded())); menu->addAction(QIcon(":icons/sites/translate.png"), tr("Translate page"), this, SLOT(openUrlInSelectedTab()))->setData(gturl); } menu->addSeparator(); menu->addAction(QIcon::fromTheme("text-html"), tr("Show so&urce code"), this, SLOT(showSource())); if (SiteInfo::canShowSiteInfo(url())) menu->addAction(QIcon::fromTheme("dialog-information"), tr("Show info ab&out site"), this, SLOT(showSiteInfo())); } void WebView::createLinkContextMenu(QMenu* menu, const WebHitTestResult &hitTest) { menu->addSeparator(); Action* act = new Action(IconProvider::newTabIcon(), tr("Open link in new &tab")); act->setData(hitTest.linkUrl()); connect(act, SIGNAL(triggered()), this, SLOT(userDefinedOpenUrlInNewTab())); connect(act, SIGNAL(ctrlTriggered()), this, SLOT(userDefinedOpenUrlInBgTab())); menu->addAction(act); menu->addAction(IconProvider::newWindowIcon(), tr("Open link in new &window"), this, SLOT(openUrlInNewWindow()))->setData(hitTest.linkUrl()); menu->addAction(IconProvider::privateBrowsingIcon(), tr("Open link in &private window"), mApp, SLOT(startPrivateBrowsing()))->setData(hitTest.linkUrl()); menu->addSeparator(); QVariantList bData; bData << hitTest.linkUrl() << hitTest.linkTitle(); menu->addAction(QIcon::fromTheme("bookmark-new"), tr("B&ookmark link"), this, SLOT(bookmarkLink()))->setData(bData); menu->addAction(QIcon::fromTheme("document-save"), tr("&Save link as..."), this, SLOT(downloadLinkToDisk())); menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send link..."), this, SLOT(sendTextByMail()))->setData(hitTest.linkUrl().toEncoded()); menu->addAction(QIcon::fromTheme("edit-copy"), tr("&Copy link address"), this, SLOT(copyLinkToClipboard()))->setData(hitTest.linkUrl()); menu->addSeparator(); if (!selectedText().isEmpty()) { pageAction(QWebEnginePage::Copy)->setIcon(QIcon::fromTheme("edit-copy")); menu->addAction(pageAction(QWebEnginePage::Copy)); } } void WebView::createImageContextMenu(QMenu* menu, const WebHitTestResult &hitTest) { menu->addSeparator(); Action* act = new Action(tr("Show i&mage")); act->setData(hitTest.imageUrl()); connect(act, SIGNAL(triggered()), this, SLOT(openActionUrl())); connect(act, SIGNAL(ctrlTriggered()), this, SLOT(userDefinedOpenUrlInNewTab())); menu->addAction(act); menu->addAction(tr("Copy image"), this, SLOT(copyImageToClipboard())); menu->addAction(QIcon::fromTheme("edit-copy"), tr("Copy image ad&dress"), this, SLOT(copyLinkToClipboard()))->setData(hitTest.imageUrl()); menu->addSeparator(); menu->addAction(QIcon::fromTheme("document-save"), tr("&Save image as..."), this, SLOT(downloadImageToDisk())); menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send image..."), this, SLOT(sendTextByMail()))->setData(hitTest.imageUrl().toEncoded()); menu->addSeparator(); if (!selectedText().isEmpty()) { pageAction(QWebEnginePage::Copy)->setIcon(QIcon::fromTheme("edit-copy")); menu->addAction(pageAction(QWebEnginePage::Copy)); } } void WebView::createSelectedTextContextMenu(QMenu* menu, const WebHitTestResult &hitTest) { Q_UNUSED(hitTest) QString selectedText = page()->selectedText(); menu->addSeparator(); if (!menu->actions().contains(pageAction(QWebEnginePage::Copy))) { menu->addAction(pageAction(QWebEnginePage::Copy)); } menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send text..."), this, SLOT(sendTextByMail()))->setData(selectedText); menu->addSeparator(); QString langCode = mApp->currentLanguage().left(2).toUtf8(); QUrl googleTranslateUrl = QUrl(QString("https://translate.google.com/#auto/%1/%2").arg(langCode, selectedText)); Action* gtwact = new Action(QIcon(":icons/sites/translate.png"), tr("Google Translate")); gtwact->setData(googleTranslateUrl); connect(gtwact, SIGNAL(triggered()), this, SLOT(openUrlInSelectedTab())); connect(gtwact, SIGNAL(ctrlTriggered()), this, SLOT(openUrlInBackgroundTab())); menu->addAction(gtwact); Action* dictact = new Action(QIcon::fromTheme("accessories-dictionary"), tr("Dictionary")); dictact->setData(QUrl("http://" + (!langCode.isEmpty() ? langCode + "." : langCode) + "wiktionary.org/wiki/Special:Search?search=" + selectedText)); connect(dictact, SIGNAL(triggered()), this, SLOT(openUrlInSelectedTab())); connect(dictact, SIGNAL(ctrlTriggered()), this, SLOT(openUrlInBackgroundTab())); menu->addAction(dictact); // #379: Remove newlines QString selectedString = selectedText.trimmed().remove(QLatin1Char('\n')); if (!selectedString.contains(QLatin1Char('.'))) { // Try to add .com selectedString.append(QLatin1String(".com")); } QUrl guessedUrl = QUrl::fromUserInput(selectedString); if (isUrlValid(guessedUrl)) { Action* act = new Action(QIcon::fromTheme("document-open-remote"), tr("Go to &web address")); act->setData(guessedUrl); connect(act, SIGNAL(triggered()), this, SLOT(openActionUrl())); connect(act, SIGNAL(ctrlTriggered()), this, SLOT(userDefinedOpenUrlInNewTab())); menu->addAction(act); } menu->addSeparator(); selectedText.truncate(20); // KDE is displaying newlines in menu actions ... weird -,- selectedText.replace(QLatin1Char('\n'), QLatin1Char(' ')).replace(QLatin1Char('\t'), QLatin1Char(' ')); SearchEngine engine = mApp->searchEnginesManager()->defaultEngine(); Action* act = new Action(engine.icon, tr("Search \"%1 ..\" with %2").arg(selectedText, engine.name)); connect(act, SIGNAL(triggered()), this, SLOT(searchSelectedText())); connect(act, SIGNAL(ctrlTriggered()), this, SLOT(searchSelectedTextInBackgroundTab())); menu->addAction(act); // Search with ... Menu* swMenu = new Menu(tr("Search with..."), menu); swMenu->setCloseOnMiddleClick(true); SearchEnginesManager* searchManager = mApp->searchEnginesManager(); foreach (const SearchEngine &en, searchManager->allEngines()) { Action* act = new Action(en.icon, en.name); act->setData(QVariant::fromValue(en)); connect(act, SIGNAL(triggered()), this, SLOT(searchSelectedText())); connect(act, SIGNAL(ctrlTriggered()), this, SLOT(searchSelectedTextInBackgroundTab())); swMenu->addAction(act); } menu->addMenu(swMenu); } void WebView::createMediaContextMenu(QMenu *menu, const WebHitTestResult &hitTest) { bool paused = hitTest.mediaPaused(); bool muted = hitTest.mediaMuted(); menu->addSeparator(); menu->addAction(paused ? tr("&Play") : tr("&Pause"), this, SLOT(toggleMediaPause()))->setIcon(QIcon::fromTheme(paused ? "media-playback-start" : "media-playback-pause")); menu->addAction(muted ? tr("Un&mute") : tr("&Mute"), this, SLOT(toggleMediaMute()))->setIcon(QIcon::fromTheme(muted ? "audio-volume-muted" : "audio-volume-high")); menu->addSeparator(); menu->addAction(QIcon::fromTheme("edit-copy"), tr("&Copy Media Address"), this, SLOT(copyLinkToClipboard()))->setData(hitTest.mediaUrl()); menu->addAction(QIcon::fromTheme("mail-message-new"), tr("&Send Media Address"), this, SLOT(sendTextByMail()))->setData(hitTest.mediaUrl().toEncoded()); menu->addAction(QIcon::fromTheme("document-save"), tr("Save Media To &Disk"), this, SLOT(downloadMediaToDisk())); } void WebView::checkForForm(QAction *action, const QPoint &pos) { m_clickedPos = mapToViewport(pos); QPointer act = action; page()->runJavaScript(Scripts::getFormData(m_clickedPos), WebPage::SafeJsWorld, [this, act](const QVariant &res) { const QVariantMap &map = res.toMap(); if (!act || map.isEmpty()) return; const QUrl url = map.value(QSL("action")).toUrl(); const QString method = map.value(QSL("method")).toString(); if (!url.isEmpty() && (method == QL1S("get") || method == QL1S("post"))) { act->setVisible(true); act->setIcon(QIcon::fromTheme(QSL("edit-find"), QIcon(QSL(":icons/menu/search-icon.svg")))); act->setText(tr("Create Search Engine")); connect(act.data(), &QAction::triggered, this, &WebView::createSearchEngine); } }); } void WebView::createSearchEngine() { page()->runJavaScript(Scripts::getFormData(m_clickedPos), WebPage::SafeJsWorld, [this](const QVariant &res) { mApp->searchEnginesManager()->addEngineFromForm(res.toMap(), this); }); } void WebView::addSpeedDial() { page()->runJavaScript("addSpeedDial()"); } void WebView::configureSpeedDial() { page()->runJavaScript("configureSpeedDial()"); } void WebView::reloadAllSpeedDials() { page()->runJavaScript("reloadAll()"); } void WebView::toggleMediaPause() { triggerPageAction(QWebEnginePage::ToggleMediaPlayPause); } void WebView::toggleMediaMute() { triggerPageAction(QWebEnginePage::ToggleMediaMute); } void WebView::initializeActions() { QAction* undoAction = pageAction(QWebEnginePage::Undo); undoAction->setText(tr("&Undo")); undoAction->setShortcut(QKeySequence("Ctrl+Z")); undoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); undoAction->setIcon(QIcon::fromTheme(QSL("edit-undo"))); QAction* redoAction = pageAction(QWebEnginePage::Redo); redoAction->setText(tr("&Redo")); redoAction->setShortcut(QKeySequence("Ctrl+Shift+Z")); redoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); redoAction->setIcon(QIcon::fromTheme(QSL("edit-redo"))); QAction* cutAction = pageAction(QWebEnginePage::Cut); cutAction->setText(tr("&Cut")); cutAction->setShortcut(QKeySequence("Ctrl+X")); cutAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); cutAction->setIcon(QIcon::fromTheme(QSL("edit-cut"))); QAction* copyAction = pageAction(QWebEnginePage::Copy); copyAction->setText(tr("&Copy")); copyAction->setShortcut(QKeySequence("Ctrl+C")); copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); copyAction->setIcon(QIcon::fromTheme(QSL("edit-copy"))); QAction* pasteAction = pageAction(QWebEnginePage::Paste); pasteAction->setText(tr("&Paste")); pasteAction->setShortcut(QKeySequence("Ctrl+V")); pasteAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); pasteAction->setIcon(QIcon::fromTheme(QSL("edit-paste"))); QAction* selectAllAction = pageAction(QWebEnginePage::SelectAll); selectAllAction->setText(tr("Select All")); selectAllAction->setShortcut(QKeySequence("Ctrl+A")); selectAllAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); selectAllAction->setIcon(QIcon::fromTheme(QSL("edit-select-all"))); QAction* reloadAction = pageAction(QWebEnginePage::Reload); reloadAction->setText(tr("&Reload")); reloadAction->setIcon(QIcon::fromTheme(QSL("view-refresh"))); QAction* stopAction = pageAction(QWebEnginePage::Stop); stopAction->setText(tr("S&top")); stopAction->setIcon(QIcon::fromTheme(QSL("process-stop"))); // Make action shortcuts available for webview addAction(undoAction); addAction(redoAction); addAction(cutAction); addAction(copyAction); addAction(pasteAction); addAction(selectAllAction); } void WebView::_wheelEvent(QWheelEvent *event) { if (mApp->plugins()->processWheelEvent(Qz::ON_WebView, this, event)) { event->accept(); return; } if (event->modifiers() & Qt::ControlModifier) { m_wheelHelper.processEvent(event); while (WheelHelper::Direction direction = m_wheelHelper.takeDirection()) { switch (direction) { case WheelHelper::WheelUp: case WheelHelper::WheelLeft: zoomIn(); break; case WheelHelper::WheelDown: case WheelHelper::WheelRight: zoomOut(); break; default: break; } } event->accept(); return; } m_wheelHelper.reset(); // QtWebEngine ignores QApplication::wheelScrollLines() and instead always scrolls 3 lines if (event->spontaneous()) { const qreal multiplier = QApplication::wheelScrollLines() / 3.0; if (multiplier != 1.0) { QWheelEvent e(event->pos(), event->globalPos(), event->pixelDelta(), event->angleDelta() * multiplier, 0, Qt::Horizontal, event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted()); QApplication::sendEvent(m_rwhvqt, &e); event->accept(); } } } void WebView::_mousePressEvent(QMouseEvent *event) { m_clickedUrl = QUrl(); m_clickedPos = QPointF(); if (mApp->plugins()->processMousePress(Qz::ON_WebView, this, event)) { event->accept(); return; } switch (event->button()) { case Qt::XButton1: back(); event->accept(); break; case Qt::XButton2: forward(); event->accept(); break; case Qt::MiddleButton: m_clickedUrl = page()->hitTestContent(event->pos()).linkUrl(); if (!m_clickedUrl.isEmpty()) event->accept(); break; case Qt::LeftButton: m_clickedUrl = page()->hitTestContent(event->pos()).linkUrl(); break; default: break; } } void WebView::_mouseReleaseEvent(QMouseEvent *event) { if (mApp->plugins()->processMouseRelease(Qz::ON_WebView, this, event)) { event->accept(); return; } switch (event->button()) { case Qt::MiddleButton: if (!m_clickedUrl.isEmpty()) { const QUrl link = page()->hitTestContent(event->pos()).linkUrl(); if (m_clickedUrl == link && isUrlValid(link)) { userDefinedOpenUrlInNewTab(link, event->modifiers() & Qt::ShiftModifier); event->accept(); } } break; case Qt::LeftButton: if (!m_clickedUrl.isEmpty()) { const QUrl link = page()->hitTestContent(event->pos()).linkUrl(); if (m_clickedUrl == link && isUrlValid(link)) { if (event->modifiers() & Qt::ControlModifier) { userDefinedOpenUrlInNewTab(link, event->modifiers() & Qt::ShiftModifier); event->accept(); } } } break; case Qt::RightButton: if (s_forceContextMenuOnMouseRelease) { QContextMenuEvent ev(QContextMenuEvent::Mouse, event->pos(), event->globalPos(), event->modifiers()); _contextMenuEvent(&ev); event->accept(); } break; default: break; } } void WebView::_mouseMoveEvent(QMouseEvent *event) { if (mApp->plugins()->processMouseMove(Qz::ON_WebView, this, event)) { event->accept(); } } void WebView::_keyPressEvent(QKeyEvent *event) { if (mApp->plugins()->processKeyPress(Qz::ON_WebView, this, event)) { event->accept(); return; } switch (event->key()) { case Qt::Key_ZoomIn: zoomIn(); event->accept(); break; case Qt::Key_ZoomOut: zoomOut(); event->accept(); break; case Qt::Key_Plus: if (event->modifiers() & Qt::ControlModifier) { zoomIn(); event->accept(); } break; case Qt::Key_Minus: if (event->modifiers() & Qt::ControlModifier) { zoomOut(); event->accept(); } break; case Qt::Key_0: if (event->modifiers() & Qt::ControlModifier) { zoomReset(); event->accept(); } break; case Qt::Key_M: if (event->modifiers() & Qt::ControlModifier) { page()->setAudioMuted(!page()->isAudioMuted()); event->accept(); } break; default: break; } } void WebView::_keyReleaseEvent(QKeyEvent *event) { if (mApp->plugins()->processKeyRelease(Qz::ON_WebView, this, event)) { event->accept(); } switch (event->key()) { case Qt::Key_Escape: if (isFullScreen()) { triggerPageAction(QWebEnginePage::ExitFullScreen); event->accept(); } break; default: break; } } void WebView::_contextMenuEvent(QContextMenuEvent *event) { Q_UNUSED(event) } void WebView::resizeEvent(QResizeEvent *event) { QWebEngineView::resizeEvent(event); emit viewportResized(size()); } void WebView::contextMenuEvent(QContextMenuEvent *event) { // Context menu is created in mouseReleaseEvent if (s_forceContextMenuOnMouseRelease) return; const QPoint pos = event->pos(); const QContextMenuEvent::Reason reason = event->reason(); QTimer::singleShot(0, this, [this, pos, reason]() { QContextMenuEvent ev(reason, pos); _contextMenuEvent(&ev); }); } void WebView::loadRequest(const LoadRequest &req) { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) QWebEngineView::load(req.webRequest()); #else if (req.operation() == LoadRequest::GetOperation) load(req.url()); else page()->runJavaScript(Scripts::sendPostData(req.url(), req.data()), WebPage::SafeJsWorld); #endif } bool WebView::eventFilter(QObject *obj, QEvent *event) { // Keyboard events are sent to parent widget if (obj == this && event->type() == QEvent::ParentChange && parentWidget()) { parentWidget()->installEventFilter(this); } // Forward events to WebView #define HANDLE_EVENT(f, t) \ { \ bool wasAccepted = event->isAccepted(); \ event->setAccepted(false); \ f(static_cast(event)); \ bool ret = event->isAccepted(); \ event->setAccepted(wasAccepted); \ return ret; \ } if (obj == m_rwhvqt) { switch (event->type()) { case QEvent::MouseButtonPress: HANDLE_EVENT(_mousePressEvent, QMouseEvent); case QEvent::MouseButtonRelease: HANDLE_EVENT(_mouseReleaseEvent, QMouseEvent); case QEvent::MouseMove: HANDLE_EVENT(_mouseMoveEvent, QMouseEvent); case QEvent::Wheel: HANDLE_EVENT(_wheelEvent, QWheelEvent); default: break; } } if (obj == parentWidget()) { switch (event->type()) { case QEvent::KeyPress: HANDLE_EVENT(_keyPressEvent, QKeyEvent); case QEvent::KeyRelease: HANDLE_EVENT(_keyReleaseEvent, QKeyEvent); default: break; } } #undef HANDLE_EVENT // Block already handled events if (obj == this) { switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: case QEvent::Wheel: return true; case QEvent::Hide: if (isFullScreen()) { triggerPageAction(QWebEnginePage::ExitFullScreen); } break; default: break; } } const bool res = QWebEngineView::eventFilter(obj, event); if (obj == m_rwhvqt) { switch (event->type()) { case QEvent::FocusIn: case QEvent::FocusOut: emit focusChanged(hasFocus()); break; default: break; } } return res; } diff --git a/src/lib/webengine/webview.h b/src/lib/webengine/webview.h index 185a5640..a0a4af4a 100644 --- a/src/lib/webengine/webview.h +++ b/src/lib/webengine/webview.h @@ -1,198 +1,196 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 . * ============================================================ */ #ifndef WEBVIEW_H #define WEBVIEW_H #include #include #include "qzcommon.h" #include "loadrequest.h" #include "wheelhelper.h" class WebPage; class LoadRequest; class WebHitTestResult; class FALKON_EXPORT WebView : public QWebEngineView { Q_OBJECT public: explicit WebView(QWidget* parent = 0); ~WebView(); QIcon icon(bool allowNull = false) const; - - QString title() const; - bool isTitleEmpty() const; + QString title(bool allowEmpty = false) const; WebPage* page() const; void setPage(WebPage* page); void load(const QUrl &url); void load(const LoadRequest &request); bool isLoading() const; int loadingProgress() const; bool backgroundActivity() const; // Set zoom level (0 - 17) int zoomLevel() const; void setZoomLevel(int level); QPointF mapToViewport(const QPointF &pos) const; QRect scrollBarGeometry(Qt::Orientation orientation) const; void addNotification(QWidget* notif); bool eventFilter(QObject *obj, QEvent *event); QWidget *inputWidget() const; virtual QWidget *overlayWidget() = 0; static bool isUrlValid(const QUrl &url); static QList zoomLevels(); // Force context menu event to be sent on mouse release // This allows to override right mouse button events (eg. for mouse gestures) static bool forceContextMenuOnMouseRelease(); static void setForceContextMenuOnMouseRelease(bool force); signals: void pageChanged(WebPage *page); void focusChanged(bool); void viewportResized(QSize); void showNotification(QWidget*); void privacyChanged(bool); void zoomLevelChanged(int); void backgroundActivityChanged(bool); public slots: void zoomIn(); void zoomOut(); void zoomReset(); void editUndo(); void editRedo(); void editCut(); void editCopy(); void editPaste(); void editSelectAll(); void editDelete(); void reloadBypassCache(); void back(); void forward(); void printPage(); void showSource(); void sendPageByMail(); void openUrlInNewTab(const QUrl &url, Qz::NewTabPositionFlags position); virtual void closeView() = 0; virtual void loadInNewTab(const LoadRequest &req, Qz::NewTabPositionFlags position) = 0; virtual bool isFullScreen() = 0; virtual void requestFullScreen(bool enable) = 0; protected slots: void slotLoadStarted(); void slotLoadProgress(int progress); void slotLoadFinished(bool ok); void slotIconChanged(); void slotUrlChanged(const QUrl &url); void slotTitleChanged(const QString &title); // Context menu slots void openUrlInNewWindow(); void sendTextByMail(); void copyLinkToClipboard(); void savePageAs(); void copyImageToClipboard(); void downloadLinkToDisk(); void downloadImageToDisk(); void downloadMediaToDisk(); void openActionUrl(); void showSiteInfo(); void searchSelectedText(); void searchSelectedTextInBackgroundTab(); void bookmarkLink(); void openUrlInSelectedTab(); void openUrlInBackgroundTab(); // To support user's option whether to open in selected or background tab void userDefinedOpenUrlInNewTab(const QUrl &url = QUrl(), bool invert = false); void userDefinedOpenUrlInBgTab(const QUrl &url = QUrl()); protected: void showEvent(QShowEvent *event) override; void resizeEvent(QResizeEvent *event); void contextMenuEvent(QContextMenuEvent *event); virtual void _wheelEvent(QWheelEvent *event); virtual void _mousePressEvent(QMouseEvent *event); virtual void _mouseReleaseEvent(QMouseEvent *event); virtual void _mouseMoveEvent(QMouseEvent *event); virtual void _keyPressEvent(QKeyEvent *event); virtual void _keyReleaseEvent(QKeyEvent *event); virtual void _contextMenuEvent(QContextMenuEvent *event); void loadRequest(const LoadRequest &req); void applyZoom(); void createContextMenu(QMenu *menu, WebHitTestResult &hitTest); void createPageContextMenu(QMenu *menu); void createLinkContextMenu(QMenu *menu, const WebHitTestResult &hitTest); void createImageContextMenu(QMenu *menu, const WebHitTestResult &hitTest); void createSelectedTextContextMenu(QMenu *menu, const WebHitTestResult &hitTest); void createMediaContextMenu(QMenu *menu, const WebHitTestResult &hitTest); void checkForForm(QAction *action, const QPoint &pos); void createSearchEngine(); private slots: void addSpeedDial(); void configureSpeedDial(); void reloadAllSpeedDials(); void toggleMediaPause(); void toggleMediaMute(); private: void initializeActions(); int m_currentZoomLevel; int m_progress; bool m_backgroundActivity; QUrl m_clickedUrl; QPointF m_clickedPos; WebPage* m_page; bool m_firstLoad; QPointer m_rwhvqt; WheelHelper m_wheelHelper; static bool s_forceContextMenuOnMouseRelease; }; #endif // WEBVIEW_H diff --git a/src/lib/webtab/webtab.cpp b/src/lib/webtab/webtab.cpp index 49773297..b4c411ac 100644 --- a/src/lib/webtab/webtab.cpp +++ b/src/lib/webtab/webtab.cpp @@ -1,500 +1,500 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 . * ============================================================ */ #include "webtab.h" #include "browserwindow.h" #include "tabbedwebview.h" #include "webinspector.h" #include "webpage.h" #include "tabbar.h" #include "tabicon.h" #include "tabwidget.h" #include "locationbar.h" #include "qztools.h" #include "qzsettings.h" #include "mainapplication.h" #include "iconprovider.h" #include "searchtoolbar.h" #include #include #include #include #include static const int savedTabVersion = 3; WebTab::SavedTab::SavedTab() : isPinned(false) , zoomLevel(qzSettings->defaultZoomLevel) { } WebTab::SavedTab::SavedTab(WebTab* webTab) { title = webTab->title(); url = webTab->url(); icon = webTab->icon(true); history = webTab->historyData(); isPinned = webTab->isPinned(); zoomLevel = webTab->zoomLevel(); } bool WebTab::SavedTab::isValid() const { return !url.isEmpty() || !history.isEmpty(); } void WebTab::SavedTab::clear() { title.clear(); url.clear(); icon = QIcon(); history.clear(); isPinned = false; zoomLevel = qzSettings->defaultZoomLevel; } QDataStream &operator <<(QDataStream &stream, const WebTab::SavedTab &tab) { stream << savedTabVersion; stream << tab.title; stream << tab.url; stream << tab.icon.pixmap(16); stream << tab.history; stream << tab.isPinned; stream << tab.zoomLevel; return stream; } QDataStream &operator >>(QDataStream &stream, WebTab::SavedTab &tab) { int version; stream >> version; if (version < 1) return stream; QPixmap pixmap; stream >> tab.title; stream >> tab.url; stream >> pixmap; stream >> tab.history; if (version >= 2) stream >> tab.isPinned; if (version >= 3) stream >> tab.zoomLevel; tab.icon = QIcon(pixmap); return stream; } WebTab::WebTab(BrowserWindow* window) : QWidget() , m_window(window) , m_tabBar(0) , m_isPinned(false) { setObjectName(QSL("webtab")); m_webView = new TabbedWebView(this); m_webView->setBrowserWindow(m_window); m_webView->setWebPage(new WebPage); m_webView->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); m_locationBar = new LocationBar(m_window); m_locationBar->setWebView(m_webView); m_tabIcon = new TabIcon(this); m_tabIcon->setWebTab(this); m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(0); m_layout->addWidget(m_webView); QWidget *viewWidget = new QWidget(this); viewWidget->setLayout(m_layout); m_splitter = new QSplitter(Qt::Vertical, this); m_splitter->setChildrenCollapsible(false); m_splitter->addWidget(viewWidget); QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(m_splitter); setLayout(layout); m_notificationWidget = new QWidget(this); m_notificationWidget->setAutoFillBackground(true); QPalette pal = m_notificationWidget->palette(); pal.setColor(QPalette::Background, pal.window().color().darker(110)); m_notificationWidget->setPalette(pal); QVBoxLayout *nlayout = new QVBoxLayout(m_notificationWidget); nlayout->setSizeConstraint(QLayout::SetMinAndMaxSize); nlayout->setContentsMargins(0, 0, 0, 0); nlayout->setSpacing(1); connect(m_webView, SIGNAL(showNotification(QWidget*)), this, SLOT(showNotification(QWidget*))); connect(m_webView, SIGNAL(loadStarted()), this, SLOT(loadStarted())); connect(m_webView, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished())); connect(m_webView, SIGNAL(titleChanged(QString)), this, SLOT(titleChanged(QString))); // Workaround QTabBar not immediately noticing resizing of tab buttons connect(m_tabIcon, &TabIcon::resized, this, [this]() { if (m_tabBar) { m_tabBar->setTabButton(tabIndex(), m_tabBar->iconButtonPosition(), m_tabIcon); } }); } TabbedWebView* WebTab::webView() const { return m_webView; } bool WebTab::haveInspector() const { return m_splitter->count() > 1 && m_splitter->widget(1)->inherits("WebInspector"); } void WebTab::showWebInspector(bool inspectElement) { if (!WebInspector::isEnabled() || haveInspector()) return; WebInspector *inspector = new WebInspector(this); inspector->setView(m_webView); if (inspectElement) inspector->inspectElement(); m_splitter->addWidget(inspector); } void WebTab::toggleWebInspector() { if (!haveInspector()) showWebInspector(); else delete m_splitter->widget(1); } void WebTab::showSearchToolBar() { const int index = 1; SearchToolBar *toolBar = nullptr; if (m_layout->count() == 1) { toolBar = new SearchToolBar(m_webView, this); m_layout->insertWidget(index, toolBar); } else if (m_layout->count() == 2) { Q_ASSERT(qobject_cast(m_layout->itemAt(index)->widget())); toolBar = static_cast(m_layout->itemAt(index)->widget()); } Q_ASSERT(toolBar); toolBar->focusSearchLine(); } QUrl WebTab::url() const { if (isRestored()) { return m_webView->url(); } else { return m_savedTab.url; } } -QString WebTab::title() const +QString WebTab::title(bool allowEmpty) const { if (isRestored()) { - return m_webView->title(); + return m_webView->title(allowEmpty); } else { return m_savedTab.title; } } QIcon WebTab::icon(bool allowNull) const { if (isRestored()) { return m_webView->icon(allowNull); } if (allowNull || !m_savedTab.icon.isNull()) { return m_savedTab.icon; } return IconProvider::emptyWebIcon(); } QWebEngineHistory* WebTab::history() const { return m_webView->history(); } int WebTab::zoomLevel() const { return m_webView->zoomLevel(); } void WebTab::setZoomLevel(int level) { m_webView->setZoomLevel(level); } void WebTab::detach() { Q_ASSERT(m_window); Q_ASSERT(m_tabBar); // Remove icon from tab m_tabBar->setTabButton(tabIndex(), m_tabBar->iconButtonPosition(), nullptr); m_tabIcon->setParent(this); // Remove the tab from tabbar m_window->tabWidget()->removeTab(tabIndex()); setParent(0); // Remove the locationbar from window m_locationBar->setParent(this); // Detach TabbedWebView m_webView->setBrowserWindow(0); // WebTab is now standalone widget m_window = 0; m_tabBar = 0; } void WebTab::attach(BrowserWindow* window) { m_window = window; m_tabBar = m_window->tabWidget()->tabBar(); m_webView->setBrowserWindow(m_window); m_tabBar->setTabText(tabIndex(), title()); m_tabBar->setTabButton(tabIndex(), m_tabBar->iconButtonPosition(), m_tabIcon); m_tabIcon->updateIcon(); } QByteArray WebTab::historyData() const { if (isRestored()) { QByteArray historyArray; QDataStream historyStream(&historyArray, QIODevice::WriteOnly); historyStream << *m_webView->history(); return historyArray; } else { return m_savedTab.history; } } void WebTab::reload() { m_webView->reload(); } void WebTab::stop() { m_webView->stop(); } bool WebTab::isLoading() const { return m_webView->isLoading(); } bool WebTab::isPinned() const { return m_isPinned; } void WebTab::setPinned(bool state) { m_isPinned = state; } bool WebTab::isMuted() const { return m_webView->page()->isAudioMuted(); } void WebTab::setMuted(bool muted) { m_webView->page()->setAudioMuted(muted); } void WebTab::toggleMuted() { bool muted = isMuted(); setMuted(!muted); } LocationBar* WebTab::locationBar() const { return m_locationBar; } TabIcon* WebTab::tabIcon() const { return m_tabIcon; } bool WebTab::isRestored() const { return !m_savedTab.isValid(); } void WebTab::restoreTab(const WebTab::SavedTab &tab) { Q_ASSERT(m_tabBar); m_isPinned = tab.isPinned; if (!m_isPinned && qzSettings->loadTabsOnActivation) { m_savedTab = tab; int index = tabIndex(); m_tabBar->setTabText(index, tab.title); m_locationBar->showUrl(tab.url); m_tabIcon->updateIcon(); } else { // This is called only on restore session and restoring tabs immediately // crashes QtWebEngine, waiting after initialization is complete fixes it QTimer::singleShot(1000, this, [=]() { p_restoreTab(tab); }); } } void WebTab::p_restoreTab(const QUrl &url, const QByteArray &history, int zoomLevel) { m_webView->load(url); // Restoring history of internal pages crashes QtWebEngine 5.8 static const QStringList blacklistedSchemes = { QSL("view-source"), QSL("chrome") }; if (!blacklistedSchemes.contains(url.scheme())) { QDataStream stream(history); stream >> *m_webView->history(); } m_webView->setZoomLevel(zoomLevel); m_webView->setFocus(); } void WebTab::p_restoreTab(const WebTab::SavedTab &tab) { p_restoreTab(tab.url, tab.history, tab.zoomLevel); } void WebTab::showNotification(QWidget* notif) { m_notificationWidget->setParent(nullptr); m_notificationWidget->setParent(this); m_notificationWidget->setFixedWidth(width()); m_notificationWidget->layout()->addWidget(notif); m_notificationWidget->show(); notif->show(); } void WebTab::loadStarted() { - if (m_tabBar && m_webView->isTitleEmpty()) { + if (m_tabBar && m_webView->title(/*allowEmpty*/true).isEmpty()) { m_tabBar->setTabText(tabIndex(), tr("Loading...")); } } void WebTab::loadFinished() { titleChanged(m_webView->title()); } void WebTab::titleChanged(const QString &title) { if (!m_tabBar || !m_window || title.isEmpty()) { return; } if (isCurrentTab()) { m_window->setWindowTitle(tr("%1 - Falkon").arg(title)); } m_tabBar->setTabText(tabIndex(), title); } void WebTab::slotRestore() { Q_ASSERT(m_tabBar); if (isRestored()) { return; } p_restoreTab(m_savedTab); m_savedTab.clear(); } void WebTab::tabActivated() { if (isRestored()) { return; } QTimer::singleShot(0, this, SLOT(slotRestore())); } void WebTab::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); m_notificationWidget->setFixedWidth(width()); } bool WebTab::isCurrentTab() const { return m_tabBar && tabIndex() == m_tabBar->currentIndex(); } int WebTab::tabIndex() const { Q_ASSERT(m_tabBar); return m_tabBar->tabWidget()->indexOf(const_cast(this)); } void WebTab::togglePinned() { Q_ASSERT(m_tabBar); Q_ASSERT(m_window); m_isPinned = !m_isPinned; m_window->tabWidget()->pinUnPinTab(tabIndex(), title()); } diff --git a/src/lib/webtab/webtab.h b/src/lib/webtab/webtab.h index 06d26139..8ead83ed 100644 --- a/src/lib/webtab/webtab.h +++ b/src/lib/webtab/webtab.h @@ -1,134 +1,133 @@ /* ============================================================ * Falkon - Qt web browser -* Copyright (C) 2010-2017 David Rosca +* Copyright (C) 2010-2018 David Rosca * * 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 3 of the License, or * (at your option) any later version. * * 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 . * ============================================================ */ #ifndef WEBTAB_H #define WEBTAB_H #include #include #include #include "qzcommon.h" class QVBoxLayout; class QWebEngineHistory; class QSplitter; class BrowserWindow; class TabbedWebView; class WebInspector; class LocationBar; class TabIcon; class TabBar; class FALKON_EXPORT WebTab : public QWidget { Q_OBJECT public: struct SavedTab { QString title; QUrl url; QIcon icon; QByteArray history; bool isPinned; int zoomLevel; SavedTab(); SavedTab(WebTab* webTab); bool isValid() const; void clear(); friend FALKON_EXPORT QDataStream &operator<<(QDataStream &stream, const SavedTab &tab); friend FALKON_EXPORT QDataStream &operator>>(QDataStream &stream, SavedTab &tab); }; explicit WebTab(BrowserWindow* window); TabbedWebView* webView() const; LocationBar* locationBar() const; TabIcon* tabIcon() const; QUrl url() const; - QString title() const; + QString title(bool allowEmpty = false) const; QIcon icon(bool allowNull = false) const; QWebEngineHistory* history() const; int zoomLevel() const; void setZoomLevel(int level); void detach(); void attach(BrowserWindow* window); QByteArray historyData() const; void stop(); void reload(); bool isLoading() const; bool isPinned() const; void setPinned(bool state); void togglePinned(); bool isMuted() const; void setMuted(bool muted); void toggleMuted(); int tabIndex() const; bool isCurrentTab() const; bool haveInspector() const; void showWebInspector(bool inspectElement = false); void toggleWebInspector(); void showSearchToolBar(); bool isRestored() const; void restoreTab(const SavedTab &tab); void p_restoreTab(const SavedTab &tab); void p_restoreTab(const QUrl &url, const QByteArray &history, int zoomLevel); void tabActivated(); private slots: void showNotification(QWidget* notif); void loadStarted(); void loadFinished(); void titleChanged(const QString &title); void slotRestore(); private: void resizeEvent(QResizeEvent *event) override; BrowserWindow* m_window; QVBoxLayout* m_layout; QSplitter* m_splitter; TabbedWebView* m_webView; WebInspector* m_inspector; LocationBar* m_locationBar; TabIcon* m_tabIcon; TabBar* m_tabBar; QWidget *m_notificationWidget; SavedTab m_savedTab; bool m_isPinned; - }; #endif // WEBTAB_H