File indexing completed on 2024-05-12 04:57:52

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "browserwindow.h"
0019 #include "tabwidget.h"
0020 #include "tabbar.h"
0021 #include "webpage.h"
0022 #include "tabbedwebview.h"
0023 #include "lineedit.h"
0024 #include "history.h"
0025 #include "locationbar.h"
0026 #include "websearchbar.h"
0027 #include "pluginproxy.h"
0028 #include "sidebar.h"
0029 #include "cookiejar.h"
0030 #include "cookiemanager.h"
0031 #include "bookmarkstoolbar.h"
0032 #include "clearprivatedata.h"
0033 #include "autofill.h"
0034 #include "mainapplication.h"
0035 #include "checkboxdialog.h"
0036 #include "clickablelabel.h"
0037 #include "docktitlebarwidget.h"
0038 #include "iconprovider.h"
0039 #include "progressbar.h"
0040 #include "closedwindowsmanager.h"
0041 #include "statusbar.h"
0042 #include "browsinglibrary.h"
0043 #include "navigationbar.h"
0044 #include "bookmarksimport/bookmarksimportdialog.h"
0045 #include "qztools.h"
0046 #include "reloadstopbutton.h"
0047 #include "enhancedmenu.h"
0048 #include "navigationcontainer.h"
0049 #include "settings.h"
0050 #include "qzsettings.h"
0051 #include "speeddial.h"
0052 #include "menubar.h"
0053 #include "bookmarkstools.h"
0054 #include "bookmarksmenu.h"
0055 #include "historymenu.h"
0056 #include "mainmenu.h"
0057 #include "downloadsbutton.h"
0058 #include "tabmodel.h"
0059 #include "tabmrumodel.h"
0060 
0061 #include <algorithm>
0062 
0063 #include <QKeyEvent>
0064 #include <QSplitter>
0065 #include <QMenuBar>
0066 #include <QTimer>
0067 #include <QShortcut>
0068 #include <QStackedWidget>
0069 #include <QTextCodec>
0070 #include <QFileDialog>
0071 #include <QDesktopServices>
0072 #include <QWebEngineHistory>
0073 #include <QWebEngineSettings>
0074 #include <QMessageBox>
0075 #include <QToolTip>
0076 #include <QScrollArea>
0077 #include <QCollator>
0078 #include <QTemporaryFile>
0079 #include <QActionGroup>
0080 
0081 #ifdef QZ_WS_X11
0082 #include <xcb/xcb.h>
0083 #include <xcb/xcb_atom.h>
0084 #endif
0085 
0086 static const int savedWindowVersion = 2;
0087 
0088 BrowserWindow::SavedWindow::SavedWindow()
0089 = default;
0090 
0091 BrowserWindow::SavedWindow::SavedWindow(BrowserWindow *window)
0092 {
0093     windowState = window->isFullScreen() ? QByteArray() : window->saveState();
0094     windowGeometry = window->saveGeometry();
0095     windowUiState = window->saveUiState();
0096 #ifdef QZ_WS_X11
0097     virtualDesktop = window->getCurrentVirtualDesktop();
0098 #endif
0099 
0100     const int tabsCount = window->tabCount();
0101     tabs.reserve(tabsCount);
0102     for (int i = 0; i < tabsCount; ++i) {
0103         TabbedWebView *webView = window->weView(i);
0104         if (!webView) {
0105             continue;
0106         }
0107         WebTab* webTab = webView->webTab();
0108         if (!webTab) {
0109             continue;
0110         }
0111         WebTab::SavedTab tab(webTab);
0112         if (!tab.isValid()) {
0113             continue;
0114         }
0115         if (webTab->isCurrentTab()) {
0116             currentTab = tabs.size();
0117         }
0118         tabs.append(tab);
0119     }
0120 }
0121 
0122 bool BrowserWindow::SavedWindow::isValid() const
0123 {
0124     for (const WebTab::SavedTab &tab : std::as_const(tabs)) {
0125         if (!tab.isValid()) {
0126             return false;
0127         }
0128     }
0129     return currentTab > -1;
0130 }
0131 
0132 void BrowserWindow::SavedWindow::clear()
0133 {
0134     windowState.clear();
0135     windowGeometry.clear();
0136     virtualDesktop = -1;
0137     currentTab = -1;
0138     tabs.clear();
0139 }
0140 
0141 QDataStream &operator<<(QDataStream &stream, const BrowserWindow::SavedWindow &window)
0142 {
0143     stream << savedWindowVersion;
0144     stream << window.windowState;
0145     stream << window.windowGeometry;
0146     stream << window.virtualDesktop;
0147     stream << window.currentTab;
0148     stream << static_cast<int>(window.tabs.count());
0149 
0150     for (int i = 0; i < window.tabs.count(); ++i) {
0151         stream << window.tabs.at(i);
0152     }
0153 
0154     stream << window.windowUiState;
0155 
0156     return stream;
0157 }
0158 
0159 QDataStream &operator>>(QDataStream &stream, BrowserWindow::SavedWindow &window)
0160 {
0161     int version;
0162     stream >> version;
0163 
0164     if (version < 1) {
0165         return stream;
0166     }
0167 
0168     stream >> window.windowState;
0169     stream >> window.windowGeometry;
0170     stream >> window.virtualDesktop;
0171     stream >> window.currentTab;
0172 
0173     int tabsCount = -1;
0174     stream >> tabsCount;
0175     window.tabs.reserve(tabsCount);
0176 
0177     for (int i = 0; i < tabsCount; ++i) {
0178         WebTab::SavedTab tab;
0179         stream >> tab;
0180         window.tabs.append(tab);
0181     }
0182 
0183     if (version >= 2) {
0184         stream >> window.windowUiState;
0185     }
0186 
0187     return stream;
0188 }
0189 
0190 BrowserWindow::BrowserWindow(Qz::BrowserWindowType type, const QUrl &startUrl)
0191     : QMainWindow(nullptr)
0192     , m_startUrl(startUrl)
0193     , m_windowType(type)
0194     , m_startTab(nullptr)
0195     , m_startPage(nullptr)
0196     , m_sideBarManager(new SideBarManager(this))
0197     , m_hideNavigationTimer(nullptr)
0198 {
0199     setAttribute(Qt::WA_DeleteOnClose);
0200     setAttribute(Qt::WA_DontCreateNativeAncestors);
0201 
0202     setObjectName(QSL("mainwindow"));
0203     setWindowTitle(tr("Falkon"));
0204     setProperty("private", mApp->isPrivate());
0205 
0206     setupUi();
0207     setupMenu();
0208 
0209     m_hideNavigationTimer = new QTimer(this);
0210     m_hideNavigationTimer->setInterval(1000);
0211     m_hideNavigationTimer->setSingleShot(true);
0212     connect(m_hideNavigationTimer, &QTimer::timeout, this, &BrowserWindow::hideNavigationSlot);
0213 
0214     connect(mApp, &MainApplication::settingsReloaded, this, &BrowserWindow::loadSettings);
0215 
0216     QTimer::singleShot(0, this, &BrowserWindow::postLaunch);
0217 
0218     if (mApp->isPrivate()) {
0219         QzTools::setWmClass(QSL("Falkon Browser (Private Window)"), this);
0220     }
0221     else {
0222         QzTools::setWmClass(QSL("Falkon Browser"), this);
0223     }
0224 }
0225 
0226 BrowserWindow::~BrowserWindow()
0227 {
0228     mApp->plugins()->emitMainWindowDeleted(this);
0229 
0230     for (const QPointer<QWidget> &pointer : std::as_const(m_deleteOnCloseWidgets)) {
0231         if (pointer) {
0232             pointer->deleteLater();
0233         }
0234     }
0235 }
0236 
0237 void BrowserWindow::setStartTab(WebTab* tab)
0238 {
0239     m_startTab = tab;
0240 }
0241 
0242 void BrowserWindow::setStartPage(WebPage *page)
0243 {
0244     m_startPage = page;
0245 }
0246 
0247 void BrowserWindow::postLaunch()
0248 {
0249     loadSettings();
0250 
0251     bool addTab = true;
0252     QUrl startUrl;
0253 
0254     switch (mApp->afterLaunch()) {
0255     case MainApplication::OpenBlankPage:
0256         startUrl = QUrl();
0257         break;
0258 
0259     case MainApplication::OpenSpeedDial:
0260         startUrl = QUrl(QSL("falkon:speeddial"));
0261         break;
0262 
0263     case MainApplication::OpenHomePage:
0264     case MainApplication::RestoreSession:
0265     case MainApplication::SelectSession:
0266         startUrl = m_homepage;
0267         break;
0268 
0269     default:
0270         break;
0271     }
0272 
0273     if (!mApp->isTestModeEnabled()) {
0274         show();
0275     }
0276 
0277     switch (m_windowType) {
0278     case Qz::BW_FirstAppWindow:
0279         if (mApp->isStartingAfterCrash()) {
0280             addTab = false;
0281             startUrl.clear();
0282             m_tabWidget->addView(QUrl(QSL("falkon:restore")), Qz::NT_CleanSelectedTabAtTheEnd);
0283         }
0284         else if (mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) {
0285             addTab = m_tabWidget->count() <= 0;
0286         }
0287         break;
0288 
0289     case Qz::BW_NewWindow:
0290     case Qz::BW_MacFirstWindow:
0291         addTab = true;
0292         break;
0293 
0294     case Qz::BW_OtherRestoredWindow:
0295         addTab = false;
0296         break;
0297     }
0298 
0299     if (!m_startUrl.isEmpty()) {
0300         startUrl = m_startUrl;
0301         addTab = true;
0302     }
0303 
0304     if (m_startTab) {
0305         addTab = false;
0306         m_tabWidget->addView(m_startTab, Qz::NT_SelectedTab);
0307     }
0308 
0309     if (m_startPage) {
0310         addTab = false;
0311         m_tabWidget->addView(QUrl());
0312         weView()->setPage(m_startPage);
0313     }
0314 
0315     if (addTab) {
0316         m_tabWidget->addView(startUrl, Qz::NT_CleanSelectedTabAtTheEnd);
0317 
0318         if (startUrl.isEmpty() || startUrl.toString() == QLatin1String("falkon:speeddial")) {
0319             locationBar()->setFocus();
0320         }
0321     }
0322 
0323     // Something went really wrong .. add one tab
0324     if (m_tabWidget->count() <= 0) {
0325         m_tabWidget->addView(m_homepage, Qz::NT_SelectedTabAtTheEnd);
0326     }
0327 
0328     mApp->plugins()->emitMainWindowCreated(this);
0329     Q_EMIT startingCompleted();
0330 
0331     raise();
0332     activateWindow();
0333     updateStartupFocus();
0334 }
0335 
0336 void BrowserWindow::setupUi()
0337 {
0338     Settings settings;
0339     settings.beginGroup(QSL("Browser-View-Settings"));
0340     const QByteArray windowGeometry = settings.value(QSL("WindowGeometry")).toByteArray();
0341 
0342     const QStringList keys = {
0343         QSL("LocationBarWidth"),
0344         QSL("WebSearchBarWidth"),
0345         QSL("SideBarWidth"),
0346         QSL("WebViewWidth"),
0347         QSL("SideBar")
0348     };
0349     QHash<QString, QVariant> uiState;
0350     for (const QString &key : keys) {
0351         if (settings.contains(key)) {
0352             uiState[key] = settings.value(key);
0353         }
0354     }
0355     settings.endGroup();
0356 
0357     auto* widget = new QWidget(this);
0358     widget->setCursor(Qt::ArrowCursor);
0359     setCentralWidget(widget);
0360 
0361     m_mainLayout = new QVBoxLayout(widget);
0362     m_mainLayout->setContentsMargins(0, 0, 0, 0);
0363     m_mainLayout->setSpacing(0);
0364     m_mainSplitter = new QSplitter(this);
0365     m_mainSplitter->setObjectName(QSL("sidebar-splitter"));
0366     m_tabWidget = new TabWidget(this);
0367     m_superMenu = new QMenu(this);
0368     m_navigationToolbar = new NavigationBar(this);
0369     m_bookmarksToolbar = new BookmarksToolbar(this);
0370 
0371     m_tabModel = new TabModel(this, this);
0372     m_tabMruModel = new TabMruModel(this, this);
0373     m_tabMruModel->setSourceModel(m_tabModel);
0374 
0375     m_navigationContainer = new NavigationContainer(this);
0376     m_navigationContainer->addWidget(m_navigationToolbar);
0377     m_navigationContainer->addWidget(m_bookmarksToolbar);
0378     m_navigationContainer->setTabBar(m_tabWidget->tabBar());
0379 
0380     m_mainSplitter->addWidget(m_tabWidget);
0381     m_mainSplitter->setCollapsible(0, false);
0382 
0383     m_mainLayout->addWidget(m_navigationContainer);
0384     m_mainLayout->addWidget(m_mainSplitter);
0385 
0386     m_statusBar = new StatusBar(this);
0387     m_statusBar->setObjectName(QSL("mainwindow-statusbar"));
0388     m_statusBar->setCursor(Qt::ArrowCursor);
0389     setStatusBar(m_statusBar);
0390     m_progressBar = new ProgressBar(m_statusBar);
0391     m_ipLabel = new QLabel(this);
0392     m_ipLabel->setObjectName(QSL("statusbar-ip-label"));
0393     m_ipLabel->setToolTip(tr("IP Address of current page"));
0394 
0395     m_statusBar->addPermanentWidget(m_progressBar);
0396     m_statusBar->addPermanentWidget(m_ipLabel);
0397 
0398     auto *downloadsButton = new DownloadsButton(this);
0399     m_statusBar->addButton(downloadsButton);
0400     m_navigationToolbar->addToolButton(downloadsButton);
0401 
0402     auto desktop = QGuiApplication::primaryScreen();
0403     int windowWidth = desktop->availableGeometry().width() / 1.3;
0404     int windowHeight = desktop->availableGeometry().height() / 1.3;
0405 
0406     // Let the WM decides where to put new browser window
0407     if (m_windowType != Qz::BW_FirstAppWindow && m_windowType != Qz::BW_MacFirstWindow && mApp->getWindow()) {
0408 #ifdef Q_WS_WIN
0409         // Windows WM places every new window in the middle of screen .. for some reason
0410         QPoint p = mApp->getWindow()->geometry().topLeft();
0411         p.setX(p.x() + 30);
0412         p.setY(p.y() + 30);
0413 
0414         if (!desktop->availableGeometry(mApp->getWindow()).contains(p)) {
0415             p.setX(desktop->availableGeometry(mApp->getWindow()).x() + 30);
0416             p.setY(desktop->availableGeometry(mApp->getWindow()).y() + 30);
0417         }
0418 
0419         setGeometry(QRect(p, mApp->getWindow()->size()));
0420 #else
0421         resize(mApp->getWindow()->size());
0422 #endif
0423     } else if (!restoreGeometry(windowGeometry)) {
0424 #ifdef Q_WS_WIN
0425         setGeometry(QRect(desktop->availableGeometry(mApp->getWindow()).x() + 30,
0426                           desktop->availableGeometry(mApp->getWindow()).y() + 30, windowWidth, windowHeight));
0427 #else
0428         resize(windowWidth, windowHeight);
0429 #endif
0430     }
0431 
0432     restoreUiState(uiState);
0433 
0434     // Set some sane minimum width
0435     setMinimumWidth(300);
0436 }
0437 
0438 void BrowserWindow::setupMenu()
0439 {
0440 #ifdef Q_OS_MACOS
0441     static MainMenu* macMainMenu = 0;
0442 
0443     if (!macMainMenu) {
0444         macMainMenu = new MainMenu(this, 0);
0445         macMainMenu->initMenuBar(new QMenuBar(0));
0446         connect(mApp, SIGNAL(activeWindowChanged(BrowserWindow*)), macMainMenu, SLOT(setWindow(BrowserWindow*)));
0447     }
0448     else {
0449         macMainMenu->setWindow(this);
0450     }
0451 
0452     m_mainMenu = macMainMenu;
0453 #else
0454     setMenuBar(new MenuBar(this));
0455 
0456     m_mainMenu = new MainMenu(this, this);
0457     m_mainMenu->initMenuBar(menuBar());
0458 #endif
0459     m_mainMenu->initSuperMenu(m_superMenu);
0460 
0461     // Setup other shortcuts
0462     auto* reloadBypassCacheAction = new QShortcut(QKeySequence(QSL("Ctrl+F5")), this);
0463     auto* reloadBypassCacheAction2 = new QShortcut(QKeySequence(QSL("Ctrl+Shift+R")), this);
0464     connect(reloadBypassCacheAction, &QShortcut::activated, this, &BrowserWindow::reloadBypassCache);
0465     connect(reloadBypassCacheAction2, &QShortcut::activated, this, &BrowserWindow::reloadBypassCache);
0466 
0467     auto* closeTabAction = new QShortcut(QKeySequence(QSL("Ctrl+W")), this);
0468     auto* closeTabAction2 = new QShortcut(QKeySequence(QSL("Ctrl+F4")), this);
0469     connect(closeTabAction, &QShortcut::activated, this, &BrowserWindow::closeTab);
0470     connect(closeTabAction2, &QShortcut::activated, this, &BrowserWindow::closeTab);
0471 
0472     auto* reloadAction = new QShortcut(QKeySequence(QSL("Ctrl+R")), this);
0473     connect(reloadAction, &QShortcut::activated, this, &BrowserWindow::reload);
0474 
0475     auto* openLocationAction = new QShortcut(QKeySequence(QSL("Alt+D")), this);
0476     connect(openLocationAction, &QShortcut::activated, this, &BrowserWindow::openLocation);
0477 
0478     auto* inspectorAction = new QShortcut(QKeySequence(QSL("F12")), this);
0479     connect(inspectorAction, &QShortcut::activated, this, &BrowserWindow::toggleWebInspector);
0480 
0481     auto* restoreClosedWindow = new QShortcut(QKeySequence(QSL("Ctrl+Shift+N")), this);
0482     connect(restoreClosedWindow, &QShortcut::activated, mApp->closedWindowsManager(), &ClosedWindowsManager::restoreClosedWindow);
0483 }
0484 
0485 void BrowserWindow::updateStartupFocus()
0486 {
0487     QTimer::singleShot(500, this, [this]() {
0488         // Scroll to current tab
0489         tabWidget()->tabBar()->ensureVisible();
0490         // Update focus
0491         if (!m_startPage && LocationBar::convertUrlToText(weView()->page()->requestedUrl()).isEmpty())
0492             locationBar()->setFocus();
0493         else
0494             weView()->setFocus();
0495     });
0496 }
0497 
0498 QAction* BrowserWindow::createEncodingAction(const QString &codecName,
0499                                              const QString &activeCodecName, QMenu* menu)
0500 {
0501     auto* action = new QAction(codecName, menu);
0502     action->setData(codecName);
0503     action->setCheckable(true);
0504     connect(action, &QAction::triggered, this, &BrowserWindow::changeEncoding);
0505     if (activeCodecName.compare(codecName, Qt::CaseInsensitive) == 0) {
0506         action->setChecked(true);
0507     }
0508     return action;
0509 }
0510 
0511 void BrowserWindow::createEncodingSubMenu(const QString &name, QStringList &codecNames, QMenu* menu)
0512 {
0513     if (codecNames.isEmpty()) {
0514         return;
0515     }
0516 
0517     QCollator collator;
0518     collator.setNumericMode(true);
0519     std::sort(codecNames.begin(), codecNames.end(), [collator](const QString &a, const QString &b) {
0520         return collator.compare(a, b) < 0;
0521     });
0522 
0523     auto* subMenu = new QMenu(name, menu);
0524     const QString activeCodecName = mApp->webSettings()->defaultTextEncoding();
0525 
0526     auto *group = new QActionGroup(subMenu);
0527 
0528     for (const QString &codecName : std::as_const(codecNames)) {
0529         QAction *act = createEncodingAction(codecName, activeCodecName, subMenu);
0530         group->addAction(act);
0531         subMenu->addAction(act);
0532     }
0533 
0534     menu->addMenu(subMenu);
0535 }
0536 
0537 QHash<QString, QVariant> BrowserWindow::saveUiState()
0538 {
0539     saveSideBarSettings();
0540 
0541     QHash<QString, QVariant> state;
0542     state[QSL("LocationBarWidth")] = m_navigationToolbar->splitter()->sizes().at(0);
0543     state[QSL("WebSearchBarWidth")] = m_navigationToolbar->splitter()->sizes().at(1);
0544     state[QSL("SideBarWidth")] = m_sideBarWidth;
0545     state[QSL("WebViewWidth")] = m_webViewWidth;
0546     state[QSL("SideBar")] = m_sideBarManager->activeSideBar();
0547     return state;
0548 }
0549 
0550 void BrowserWindow::restoreUiState(const QHash<QString, QVariant> &state)
0551 {
0552     const int locationBarWidth = state.value(QSL("LocationBarWidth"), 480).toInt();
0553     const int websearchBarWidth = state.value(QSL("WebSearchBarWidth"), 140).toInt();
0554     m_navigationToolbar->setSplitterSizes(locationBarWidth, websearchBarWidth);
0555 
0556     m_sideBarWidth = state.value(QSL("SideBarWidth"), 250).toInt();
0557     m_webViewWidth = state.value(QSL("WebViewWidth"), 2000).toInt();
0558     if (m_sideBar) {
0559         m_mainSplitter->setSizes({m_sideBarWidth, m_webViewWidth});
0560     }
0561 
0562     const QString activeSideBar = state.value(QSL("SideBar")).toString();
0563     if (activeSideBar.isEmpty() && m_sideBar) {
0564         m_sideBar->close();
0565     } else {
0566         m_sideBarManager->showSideBar(activeSideBar, false);
0567     }
0568 }
0569 
0570 void BrowserWindow::loadSettings()
0571 {
0572     Settings settings;
0573 
0574     //Url settings
0575     settings.beginGroup(QSL("Web-URL-Settings"));
0576     m_homepage = settings.value(QSL("homepage"), QSL("falkon:start")).toUrl();
0577     settings.endGroup();
0578 
0579     //Browser Window settings
0580     settings.beginGroup(QSL("Browser-View-Settings"));
0581     bool showStatusBar = settings.value(QSL("showStatusBar"), false).toBool();
0582     bool showBookmarksToolbar = settings.value(QSL("showBookmarksToolbar"), false).toBool();
0583     bool showNavigationToolbar = settings.value(QSL("showNavigationToolbar"), true).toBool();
0584     bool showMenuBar = settings.value(QSL("showMenubar"), false).toBool();
0585 
0586     // Make sure both menubar and navigationbar are not hidden
0587     // Fixes #781
0588     if (!showNavigationToolbar) {
0589         showMenuBar = true;
0590         settings.setValue(QSL("showMenubar"), true);
0591     }
0592 
0593     settings.endGroup();
0594 
0595     settings.beginGroup(QSL("Shortcuts"));
0596     m_useTabNumberShortcuts = settings.value(QSL("useTabNumberShortcuts"), true).toBool();
0597     m_useSpeedDialNumberShortcuts = settings.value(QSL("useSpeedDialNumberShortcuts"), true).toBool();
0598     m_useSingleKeyShortcuts = settings.value(QSL("useSingleKeyShortcuts"), false).toBool();
0599     settings.endGroup();
0600 
0601     settings.beginGroup(QSL("Web-Browser-Settings"));
0602     QAction *quitAction = m_mainMenu->action(QSL("Standard/Quit"));
0603     if (settings.value(QSL("closeAppWithCtrlQ"), true).toBool()) {
0604         quitAction->setShortcut(QzTools::actionShortcut(QKeySequence::Quit, QKeySequence(QSL("Ctrl+Q"))));
0605     } else {
0606         quitAction->setShortcut(QKeySequence());
0607     }
0608     settings.endGroup();
0609 
0610     m_statusBarVisible = showStatusBar;
0611     statusBar()->setVisible(!isFullScreen() && showStatusBar);
0612     m_bookmarksToolbar->setVisible(showBookmarksToolbar);
0613     m_navigationToolbar->setVisible(showNavigationToolbar);
0614 
0615 #ifndef Q_OS_MACOS
0616     m_menuBarVisible = showMenuBar;
0617     menuBar()->setVisible(!isFullScreen() && showMenuBar);
0618 #endif
0619 
0620     m_navigationToolbar->setSuperMenuVisible(isFullScreen() || !showMenuBar);
0621 }
0622 
0623 void BrowserWindow::goForward()
0624 {
0625     weView()->forward();
0626 }
0627 
0628 void BrowserWindow::reload()
0629 {
0630     weView()->reload();
0631 }
0632 
0633 void BrowserWindow::reloadBypassCache()
0634 {
0635     weView()->reloadBypassCache();
0636 }
0637 
0638 void BrowserWindow::goBack()
0639 {
0640     weView()->back();
0641 }
0642 
0643 int BrowserWindow::tabCount() const
0644 {
0645     return m_tabWidget->count();
0646 }
0647 
0648 TabbedWebView* BrowserWindow::weView() const
0649 {
0650     return weView(m_tabWidget->currentIndex());
0651 }
0652 
0653 TabbedWebView* BrowserWindow::weView(int index) const
0654 {
0655     auto* webTab = qobject_cast<WebTab*>(m_tabWidget->widget(index));
0656     if (!webTab) {
0657         return nullptr;
0658     }
0659 
0660     return webTab->webView();
0661 }
0662 
0663 LocationBar* BrowserWindow::locationBar() const
0664 {
0665     return qobject_cast<LocationBar*>(m_tabWidget->locationBars()->currentWidget());
0666 }
0667 
0668 TabWidget* BrowserWindow::tabWidget() const
0669 {
0670     return m_tabWidget;
0671 }
0672 
0673 BookmarksToolbar* BrowserWindow::bookmarksToolbar() const
0674 {
0675     return m_bookmarksToolbar;
0676 }
0677 
0678 StatusBar* BrowserWindow::statusBar() const
0679 {
0680     return m_statusBar;
0681 }
0682 
0683 NavigationBar* BrowserWindow::navigationBar() const
0684 {
0685     return m_navigationToolbar;
0686 }
0687 
0688 SideBarManager* BrowserWindow::sideBarManager() const
0689 {
0690     return m_sideBarManager;
0691 }
0692 
0693 QLabel* BrowserWindow::ipLabel() const
0694 {
0695     return m_ipLabel;
0696 }
0697 
0698 QMenu* BrowserWindow::superMenu() const
0699 {
0700     return m_superMenu;
0701 }
0702 
0703 QUrl BrowserWindow::homepageUrl() const
0704 {
0705     return m_homepage;
0706 }
0707 
0708 Qz::BrowserWindowType BrowserWindow::windowType() const
0709 {
0710     return m_windowType;
0711 }
0712 
0713 QAction* BrowserWindow::action(const QString &name) const
0714 {
0715     return m_mainMenu->action(name);
0716 }
0717 
0718 TabModel *BrowserWindow::tabModel() const
0719 {
0720     return m_tabModel;
0721 }
0722 
0723 TabMruModel *BrowserWindow::tabMruModel() const
0724 {
0725     return m_tabMruModel;
0726 }
0727 
0728 void BrowserWindow::setWindowTitle(const QString &t)
0729 {
0730     QString title = t;
0731 
0732     if (mApp->isPrivate()) {
0733         title.append(tr(" (Private Browsing)"));
0734     }
0735 
0736     QMainWindow::setWindowTitle(title);
0737 }
0738 
0739 void BrowserWindow::changeEncoding()
0740 {
0741     if (auto* action = qobject_cast<QAction*>(sender())) {
0742         const QString encoding = action->data().toString();
0743         mApp->webSettings()->setDefaultTextEncoding(encoding);
0744 
0745         Settings settings;
0746         settings.setValue(QSL("Web-Browser-Settings/DefaultEncoding"), encoding);
0747 
0748         weView()->reload();
0749     }
0750 }
0751 
0752 void BrowserWindow::printPage()
0753 {
0754     weView()->printPage();
0755 }
0756 
0757 void BrowserWindow::bookmarkPage()
0758 {
0759     TabbedWebView* view = weView();
0760     BookmarksTools::addBookmarkDialog(this, view->url(), view->title());
0761 }
0762 
0763 void BrowserWindow::bookmarkAllTabs()
0764 {
0765     BookmarksTools::bookmarkAllTabsDialog(this, m_tabWidget);
0766 }
0767 
0768 void BrowserWindow::addBookmark(const QUrl &url, const QString &title)
0769 {
0770     BookmarksTools::addBookmarkDialog(this, url, title);
0771 }
0772 
0773 void BrowserWindow::goHome()
0774 {
0775     loadAddress(m_homepage);
0776 }
0777 
0778 void BrowserWindow::goHomeInNewTab()
0779 {
0780     m_tabWidget->addView(m_homepage, Qz::NT_SelectedTab);
0781 }
0782 
0783 void BrowserWindow::loadActionUrl(QObject* obj)
0784 {
0785     if (!obj) {
0786         obj = sender();
0787     }
0788 
0789     if (auto* action = qobject_cast<QAction*>(obj)) {
0790         loadAddress(action->data().toUrl());
0791     }
0792 }
0793 
0794 void BrowserWindow::loadActionUrlInNewTab(QObject* obj)
0795 {
0796     if (!obj) {
0797         obj = sender();
0798     }
0799 
0800     if (auto* action = qobject_cast<QAction*>(obj)) {
0801         m_tabWidget->addView(action->data().toUrl(), Qz::NT_SelectedTabAtTheEnd);
0802     }
0803 }
0804 
0805 void BrowserWindow::loadAddress(const QUrl &url)
0806 {
0807     if (weView()->webTab()->isPinned()) {
0808         int index = m_tabWidget->addView(url, qzSettings->newTabPosition);
0809         weView(index)->setFocus();
0810     } else {
0811         weView()->setFocus();
0812         weView()->load(LoadRequest(url));
0813     }
0814 }
0815 
0816 void BrowserWindow::showHistoryManager()
0817 {
0818     mApp->browsingLibrary()->showHistory(this);
0819 }
0820 
0821 void BrowserWindow::showSource(WebView *view)
0822 {
0823     if (!view)
0824         view = weView();
0825     view->showSource();
0826 }
0827 
0828 SideBar* BrowserWindow::addSideBar()
0829 {
0830     if (m_sideBar) {
0831         return m_sideBar.data();
0832     }
0833 
0834     m_sideBar = new SideBar(m_sideBarManager, this);
0835 
0836     m_mainSplitter->insertWidget(0, m_sideBar.data());
0837     m_mainSplitter->setCollapsible(0, false);
0838     m_mainSplitter->setSizes({m_sideBarWidth, m_webViewWidth});
0839 
0840     return m_sideBar.data();
0841 }
0842 
0843 void BrowserWindow::saveSideBarSettings()
0844 {
0845     if (m_sideBar) {
0846         // That +1 is important here, without it, the sidebar width would
0847         // decrease by 1 pixel every close
0848         m_sideBarWidth = m_mainSplitter->sizes().at(0) + 1;
0849         m_webViewWidth = width() - m_sideBarWidth;
0850     }
0851 
0852     Settings().setValue(QSL("Browser-View-Settings/SideBar"), m_sideBarManager->activeSideBar());
0853 }
0854 
0855 void BrowserWindow::toggleShowMenubar()
0856 {
0857 #ifdef Q_OS_MACOS
0858     // We use one shared global menubar on Mac that can't be hidden
0859     return;
0860 #endif
0861 
0862     setUpdatesEnabled(false);
0863 
0864     menuBar()->setVisible(!menuBar()->isVisible());
0865     m_navigationToolbar->setSuperMenuVisible(!menuBar()->isVisible());
0866 
0867     setUpdatesEnabled(true);
0868 
0869     Settings().setValue(QSL("Browser-View-Settings/showMenubar"), menuBar()->isVisible());
0870 
0871     // Make sure we show Navigation Toolbar when Menu Bar is hidden
0872     if (!m_navigationToolbar->isVisible() && !menuBar()->isVisible()) {
0873         toggleShowNavigationToolbar();
0874     }
0875 }
0876 
0877 void BrowserWindow::toggleShowStatusBar()
0878 {
0879     setUpdatesEnabled(false);
0880 
0881     m_statusBar->setVisible(!m_statusBar->isVisible());
0882 
0883     setUpdatesEnabled(true);
0884 
0885     Settings().setValue(QSL("Browser-View-Settings/showStatusBar"), m_statusBar->isVisible());
0886 
0887 }
0888 
0889 void BrowserWindow::toggleShowBookmarksToolbar()
0890 {
0891     setUpdatesEnabled(false);
0892 
0893     m_bookmarksToolbar->setVisible(!m_bookmarksToolbar->isVisible());
0894 
0895     setUpdatesEnabled(true);
0896 
0897     Settings().setValue(QSL("Browser-View-Settings/showBookmarksToolbar"), m_bookmarksToolbar->isVisible());
0898     Settings().setValue(QSL("Browser-View-Settings/instantBookmarksToolbar"), false);
0899 }
0900 
0901 void BrowserWindow::toggleShowNavigationToolbar()
0902 {
0903     setUpdatesEnabled(false);
0904 
0905     m_navigationToolbar->setVisible(!m_navigationToolbar->isVisible());
0906 
0907     setUpdatesEnabled(true);
0908 
0909     Settings().setValue(QSL("Browser-View-Settings/showNavigationToolbar"), m_navigationToolbar->isVisible());
0910 
0911 #ifndef Q_OS_MACOS
0912     // Make sure we show Menu Bar when Navigation Toolbar is hidden
0913     if (!m_navigationToolbar->isVisible() && !menuBar()->isVisible()) {
0914         toggleShowMenubar();
0915     }
0916 #endif
0917 }
0918 
0919 void BrowserWindow::toggleTabsOnTop(bool enable)
0920 {
0921     qzSettings->tabsOnTop = enable;
0922     m_navigationContainer->toggleTabsOnTop(enable);
0923 }
0924 
0925 void BrowserWindow::toggleFullScreen()
0926 {
0927     if (m_htmlFullScreenView) {
0928         weView()->triggerPageAction(QWebEnginePage::ExitFullScreen);
0929         return;
0930     }
0931 
0932     if (isFullScreen()) {
0933         setWindowState(windowState() & ~Qt::WindowFullScreen);
0934     } else {
0935         setWindowState(windowState() | Qt::WindowFullScreen);
0936     }
0937 }
0938 
0939 void BrowserWindow::requestHtmlFullScreen(TabbedWebView *view, bool enable)
0940 {
0941     if (enable) {
0942         setWindowState(windowState() | Qt::WindowFullScreen);
0943     } else {
0944         setWindowState(windowState() & ~Qt::WindowFullScreen);
0945     }
0946 
0947     if (m_sideBar)
0948         m_sideBar.data()->setHidden(enable);
0949 
0950     m_htmlFullScreenView = enable ? view : nullptr;
0951 }
0952 
0953 void BrowserWindow::showWebInspector()
0954 {
0955     if (weView() && weView()->webTab()) {
0956         weView()->webTab()->showWebInspector();
0957     }
0958 }
0959 
0960 void BrowserWindow::toggleWebInspector()
0961 {
0962     if (weView() && weView()->webTab()) {
0963         weView()->webTab()->toggleWebInspector();
0964     }
0965 }
0966 
0967 void BrowserWindow::currentTabChanged()
0968 {
0969     TabbedWebView* view = weView();
0970     m_navigationToolbar->setCurrentView(view);
0971 
0972     if (!view) {
0973         return;
0974     }
0975 
0976     const QString title = view->webTab()->title(/*allowEmpty*/true);
0977     if (title.isEmpty()) {
0978         setWindowTitle(tr("Falkon"));
0979     } else {
0980         setWindowTitle(tr("%1 - Falkon").arg(title));
0981     }
0982     m_ipLabel->setText(view->getIp());
0983     view->setFocus();
0984 
0985     updateLoadingActions();
0986 
0987     // Setting correct tab order (LocationBar -> WebSearchBar -> WebView)
0988     setTabOrder(locationBar(), m_navigationToolbar->webSearchBar());
0989     setTabOrder(m_navigationToolbar->webSearchBar(), view);
0990 }
0991 
0992 void BrowserWindow::updateLoadingActions()
0993 {
0994     TabbedWebView* view = weView();
0995     if (!view) {
0996         return;
0997     }
0998 
0999     bool isLoading = view->isLoading();
1000 
1001     m_ipLabel->setVisible(!isLoading);
1002     m_progressBar->setVisible(isLoading);
1003 
1004     action(QSL("View/Stop"))->setEnabled(isLoading);
1005     action(QSL("View/Reload"))->setEnabled(!isLoading);
1006 
1007     if (isLoading) {
1008         m_progressBar->setValue(view->loadingProgress());
1009         m_navigationToolbar->showStopButton();
1010     }
1011     else {
1012         m_navigationToolbar->showReloadButton();
1013     }
1014 }
1015 
1016 void BrowserWindow::addDeleteOnCloseWidget(QWidget* widget)
1017 {
1018     if (!m_deleteOnCloseWidgets.contains(widget)) {
1019         m_deleteOnCloseWidgets.append(widget);
1020     }
1021 }
1022 
1023 void BrowserWindow::restoreWindow(const SavedWindow &window)
1024 {
1025     restoreState(window.windowState);
1026     restoreGeometry(window.windowGeometry);
1027     restoreUiState(window.windowUiState);
1028 #ifdef QZ_WS_X11
1029     moveToVirtualDesktop(window.virtualDesktop);
1030 #endif
1031     if (!mApp->isTestModeEnabled()) {
1032         show(); // Window has to be visible before adding QWebEngineView's
1033     }
1034     m_tabWidget->restoreState(window.tabs, window.currentTab);
1035     updateStartupFocus();
1036 }
1037 
1038 void BrowserWindow::createToolbarsMenu(QMenu* menu)
1039 {
1040     removeActions(menu->actions());
1041     menu->clear();
1042 
1043     QAction* action;
1044 
1045 #ifndef Q_OS_MACOS
1046     action = menu->addAction(tr("&Menu Bar"), this, &BrowserWindow::toggleShowMenubar);
1047     action->setCheckable(true);
1048     action->setChecked(menuBar()->isVisible());
1049 #endif
1050 
1051     action = menu->addAction(tr("&Navigation Toolbar"), this, &BrowserWindow::toggleShowNavigationToolbar);
1052     action->setCheckable(true);
1053     action->setChecked(m_navigationToolbar->isVisible());
1054 
1055     action = menu->addAction(tr("&Bookmarks Toolbar"), this, &BrowserWindow::toggleShowBookmarksToolbar);
1056     action->setCheckable(true);
1057     action->setChecked(Settings().value(QSL("Browser-View-Settings/showBookmarksToolbar")).toBool());
1058 
1059     menu->addSeparator();
1060 
1061     action = menu->addAction(tr("&Tabs on Top"), this, SLOT(toggleTabsOnTop(bool)));
1062     action->setCheckable(true);
1063     action->setChecked(qzSettings->tabsOnTop);
1064 
1065     addActions(menu->actions());
1066 }
1067 
1068 void BrowserWindow::createSidebarsMenu(QMenu* menu)
1069 {
1070     m_sideBarManager->createMenu(menu);
1071 }
1072 
1073 void BrowserWindow::createEncodingMenu(QMenu* menu)
1074 {
1075     const QString activeCodecName = mApp->webSettings()->defaultTextEncoding();
1076 
1077     QStringList isoCodecs;
1078     QStringList utfCodecs;
1079     QStringList windowsCodecs;
1080     QStringList isciiCodecs;
1081     QStringList ibmCodecs;
1082     QStringList otherCodecs;
1083     QStringList allCodecs;
1084 
1085     const auto mibs = QTextCodec::availableMibs();
1086     for (const int mib : mibs) {
1087         const QString codecName = QString::fromUtf8(QTextCodec::codecForMib(mib)->name());
1088 
1089         if (!allCodecs.contains(codecName))
1090             allCodecs.append(codecName);
1091         else
1092             continue;
1093 
1094         if (codecName.startsWith(QLatin1String("ISO")))
1095             isoCodecs.append(codecName);
1096         else if (codecName.startsWith(QLatin1String("UTF")))
1097             utfCodecs.append(codecName);
1098         else if (codecName.startsWith(QLatin1String("windows")))
1099             windowsCodecs.append(codecName);
1100         else if (codecName.startsWith(QLatin1String("Iscii")))
1101             isciiCodecs.append(codecName);
1102         else if (codecName.startsWith(QLatin1String("IBM")))
1103             ibmCodecs.append(codecName);
1104         else
1105             otherCodecs.append(codecName);
1106     }
1107 
1108     if (!menu->isEmpty())
1109         menu->addSeparator();
1110 
1111     createEncodingSubMenu(QSL("ISO"), isoCodecs, menu);
1112     createEncodingSubMenu(QSL("UTF"), utfCodecs, menu);
1113     createEncodingSubMenu(QSL("Windows"), windowsCodecs, menu);
1114     createEncodingSubMenu(QSL("Iscii"), isciiCodecs, menu);
1115     createEncodingSubMenu(QSL("IBM"), ibmCodecs, menu);
1116     createEncodingSubMenu(tr("Other"), otherCodecs, menu);
1117 }
1118 
1119 void BrowserWindow::removeActions(const QList<QAction *> &actions)
1120 {
1121     for (QAction *action : actions) {
1122         removeAction(action);
1123     }
1124 }
1125 
1126 void BrowserWindow::addTab()
1127 {
1128     m_tabWidget->addView(QUrl(), Qz::NT_SelectedNewEmptyTab, true);
1129     m_tabWidget->setCurrentTabFresh(true);
1130 
1131     if (isFullScreen())
1132         showNavigationWithFullScreen();
1133 }
1134 
1135 void BrowserWindow::webSearch()
1136 {
1137     m_navigationToolbar->webSearchBar()->setFocus();
1138     m_navigationToolbar->webSearchBar()->selectAll();
1139 }
1140 
1141 void BrowserWindow::searchOnPage()
1142 {
1143     if (weView() && weView()->webTab()) {
1144         const QString searchText = weView()->page()->selectedText();
1145         if (!searchText.contains(QL1C('\n'))) {
1146             weView()->webTab()->showSearchToolBar(searchText);
1147         } else {
1148             weView()->webTab()->showSearchToolBar();
1149         }
1150     }
1151 }
1152 
1153 void BrowserWindow::openFile()
1154 {
1155     const QString fileTypes = QSL("%1(*.html *.htm *.shtml *.shtm *.xhtml);;"
1156                                       "%2(*.png *.jpg *.jpeg *.bmp *.gif *.svg *.tiff);;"
1157                                       "%3(*.txt);;"
1158                                       "%4(*.*)").arg(tr("HTML files"), tr("Image files"), tr("Text files"), tr("All files"));
1159 
1160     const QString filePath = QzTools::getOpenFileName(QSL("MainWindow-openFile"), this, tr("Open file..."), QDir::homePath(), fileTypes);
1161 
1162     if (!filePath.isEmpty()) {
1163         loadAddress(QUrl::fromLocalFile(filePath));
1164     }
1165 }
1166 
1167 void BrowserWindow::openLocation()
1168 {
1169     if (isFullScreen()) {
1170         showNavigationWithFullScreen();
1171     }
1172 
1173     locationBar()->setFocus();
1174     locationBar()->selectAll();
1175 }
1176 
1177 bool BrowserWindow::fullScreenNavigationVisible() const
1178 {
1179     return m_navigationContainer->isVisible();
1180 }
1181 
1182 void BrowserWindow::showNavigationWithFullScreen()
1183 {
1184     if (m_htmlFullScreenView)
1185         return;
1186 
1187     if (m_hideNavigationTimer->isActive()) {
1188         m_hideNavigationTimer->stop();
1189     }
1190 
1191     m_navigationContainer->show();
1192 }
1193 
1194 void BrowserWindow::hideNavigationWithFullScreen()
1195 {
1196     if (m_tabWidget->isCurrentTabFresh())
1197         return;
1198 
1199     if (!m_hideNavigationTimer->isActive()) {
1200         m_hideNavigationTimer->start();
1201     }
1202 }
1203 
1204 void BrowserWindow::hideNavigationSlot()
1205 {
1206     TabbedWebView* view = weView();
1207     bool mouseInView = view && view->underMouse();
1208 
1209     if (isFullScreen() && mouseInView) {
1210         m_navigationContainer->hide();
1211     }
1212 }
1213 
1214 bool BrowserWindow::event(QEvent *event)
1215 {
1216     if (event->type() == QEvent::WindowStateChange) {
1217         auto *e = static_cast<QWindowStateChangeEvent*>(event);
1218         if (!(e->oldState() & Qt::WindowFullScreen) && windowState() & Qt::WindowFullScreen) {
1219             // Enter fullscreen
1220             m_statusBarVisible = m_statusBar->isVisible();
1221 #ifndef Q_OS_MACOS
1222             m_menuBarVisible = menuBar()->isVisible();
1223             menuBar()->hide();
1224 #endif
1225             m_statusBar->hide();
1226 
1227             m_navigationContainer->hide();
1228             m_navigationToolbar->enterFullScreen();
1229 
1230             // Show main menu button since menubar is hidden
1231             m_navigationToolbar->setSuperMenuVisible(true);
1232         }
1233         else if (e->oldState() & Qt::WindowFullScreen && !(windowState() & Qt::WindowFullScreen)) {
1234             // Leave fullscreen
1235             m_statusBar->setVisible(m_statusBarVisible);
1236 #ifndef Q_OS_MACOS
1237             menuBar()->setVisible(m_menuBarVisible);
1238 #endif
1239 
1240             m_navigationContainer->show();
1241             m_navigationToolbar->setSuperMenuVisible(!m_menuBarVisible);
1242             m_navigationToolbar->leaveFullScreen();
1243             m_htmlFullScreenView = nullptr;
1244         }
1245 
1246         if (m_hideNavigationTimer) {
1247             m_hideNavigationTimer->stop();
1248         }
1249     }
1250 
1251     return QMainWindow::event(event);
1252 }
1253 
1254 void BrowserWindow::resizeEvent(QResizeEvent* event)
1255 {
1256     m_bookmarksToolbar->setMaximumWidth(width());
1257 
1258     QMainWindow::resizeEvent(event);
1259 }
1260 
1261 void BrowserWindow::keyPressEvent(QKeyEvent* event)
1262 {
1263     if (mApp->plugins()->processKeyPress(Qz::ON_BrowserWindow, this, event)) {
1264         return;
1265     }
1266 
1267     int number = -1;
1268     TabbedWebView* view = weView();
1269 
1270     switch (event->key()) {
1271     case Qt::Key_Back:
1272         if (view) {
1273             view->back();
1274             event->accept();
1275         }
1276         break;
1277 
1278     case Qt::Key_Forward:
1279         if (view) {
1280             view->forward();
1281             event->accept();
1282         }
1283         break;
1284 
1285     case Qt::Key_Stop:
1286         if (view) {
1287             view->stop();
1288             event->accept();
1289         }
1290         break;
1291 
1292     case Qt::Key_Reload:
1293     case Qt::Key_Refresh:
1294         if (view) {
1295             view->reload();
1296             event->accept();
1297         }
1298         break;
1299 
1300     case Qt::Key_HomePage:
1301         goHome();
1302         event->accept();
1303         break;
1304 
1305     case Qt::Key_Favorites:
1306         mApp->browsingLibrary()->showBookmarks(this);
1307         event->accept();
1308         break;
1309 
1310     case Qt::Key_Search:
1311         searchOnPage();
1312         event->accept();
1313         break;
1314 
1315     case Qt::Key_F6:
1316     case Qt::Key_OpenUrl:
1317         openLocation();
1318         event->accept();
1319         break;
1320 
1321     case Qt::Key_History:
1322         showHistoryManager();
1323         event->accept();
1324         break;
1325 
1326     case Qt::Key_AddFavorite:
1327         bookmarkPage();
1328         event->accept();
1329         break;
1330 
1331     case Qt::Key_Tools:
1332         action(QSL("Standard/Preferences"))->trigger();
1333         event->accept();
1334         break;
1335 
1336     case Qt::Key_Tab:
1337         if (event->modifiers() == Qt::ControlModifier) {
1338             static_cast<QObject*>(m_tabWidget)->event(event);
1339         }
1340         break;
1341 
1342     case Qt::Key_Backtab:
1343         if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
1344             static_cast<QObject*>(m_tabWidget)->event(event);
1345         }
1346         break;
1347 
1348     case Qt::Key_PageDown:
1349         if (event->modifiers() == Qt::ControlModifier) {
1350             m_tabWidget->nextTab();
1351             event->accept();
1352         }
1353         break;
1354 
1355     case Qt::Key_PageUp:
1356         if (event->modifiers() == Qt::ControlModifier) {
1357             m_tabWidget->previousTab();
1358             event->accept();
1359         }
1360         break;
1361 
1362     case Qt::Key_Equal:
1363         if (view && event->modifiers() == Qt::ControlModifier) {
1364             view->zoomIn();
1365             event->accept();
1366         }
1367         break;
1368 
1369     case Qt::Key_I:
1370         if (event->modifiers() == Qt::ControlModifier) {
1371             action(QSL("Tools/SiteInfo"))->trigger();
1372             event->accept();
1373         }
1374         break;
1375 
1376     case Qt::Key_U:
1377         if (event->modifiers() == Qt::ControlModifier) {
1378             action(QSL("View/PageSource"))->trigger();
1379             event->accept();
1380         }
1381         break;
1382 
1383     case Qt::Key_F:
1384         if (event->modifiers() == Qt::ControlModifier) {
1385             action(QSL("Edit/Find"))->trigger();
1386             event->accept();
1387         }
1388         break;
1389 
1390     case Qt::Key_Slash:
1391         if (m_useSingleKeyShortcuts) {
1392             action(QSL("Edit/Find"))->trigger();
1393             event->accept();
1394         }
1395         break;
1396 
1397     case Qt::Key_1:
1398         number = 1;
1399         break;
1400     case Qt::Key_2:
1401         number = 2;
1402         break;
1403     case Qt::Key_3:
1404         number = 3;
1405         break;
1406     case Qt::Key_4:
1407         number = 4;
1408         break;
1409     case Qt::Key_5:
1410         number = 5;
1411         break;
1412     case Qt::Key_6:
1413         number = 6;
1414         break;
1415     case Qt::Key_7:
1416         number = 7;
1417         break;
1418     case Qt::Key_8:
1419         number = 8;
1420         break;
1421     case Qt::Key_9:
1422         number = 9;
1423         break;
1424 
1425     default:
1426         break;
1427     }
1428 
1429     if (number != -1) {
1430         if (event->modifiers() & Qt::AltModifier && m_useTabNumberShortcuts) {
1431             if (number == 9) {
1432                 number = m_tabWidget->count();
1433             }
1434             m_tabWidget->setCurrentIndex(number - 1);
1435             event->accept();
1436             return;
1437         }
1438         if (event->modifiers() & Qt::ControlModifier && m_useSpeedDialNumberShortcuts) {
1439             const QUrl url = mApp->plugins()->speedDial()->urlForShortcut(number - 1);
1440             if (url.isValid()) {
1441                 loadAddress(url);
1442                 event->accept();
1443                 return;
1444             }
1445         }
1446         if (event->modifiers() == Qt::NoModifier && m_useSingleKeyShortcuts) {
1447             if (number == 1)
1448                 m_tabWidget->previousTab();
1449             if (number == 2)
1450                 m_tabWidget->nextTab();
1451         }
1452     }
1453 
1454     QMainWindow::keyPressEvent(event);
1455 }
1456 
1457 void BrowserWindow::keyReleaseEvent(QKeyEvent* event)
1458 {
1459     if (mApp->plugins()->processKeyRelease(Qz::ON_BrowserWindow, this, event)) {
1460         return;
1461     }
1462 
1463     switch (event->key()) {
1464     case Qt::Key_F:
1465         if (event->modifiers() == Qt::ControlModifier) {
1466             action(QSL("Edit/Find"))->trigger();
1467             event->accept();
1468         }
1469         break;
1470 
1471     default:
1472         break;
1473     }
1474 
1475     QMainWindow::keyReleaseEvent(event);
1476 }
1477 
1478 void BrowserWindow::closeEvent(QCloseEvent* event)
1479 {
1480     if (mApp->isClosing()) {
1481         saveSettings();
1482         return;
1483     }
1484 
1485     Settings settings;
1486     bool askOnClose = settings.value(QSL("Browser-Tabs-Settings/AskOnClosing"), true).toBool();
1487 
1488     if ((mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) && mApp->windowCount() == 1) {
1489         askOnClose = false;
1490     }
1491 
1492     if (askOnClose && m_tabWidget->normalTabsCount() > 1) {
1493         CheckBoxDialog dialog(QMessageBox::Yes | QMessageBox::No, this);
1494         dialog.setDefaultButton(QMessageBox::No);
1495         //~ singular There is still %n open tab and your session won't be stored.\nAre you sure you want to close this window?
1496         //~ plural There are still %n open tabs and your session won't be stored.\nAre you sure you want to close this window?
1497         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()));
1498         dialog.setCheckBoxText(tr("Don't ask again"));
1499         dialog.setWindowTitle(tr("There are still open tabs"));
1500         dialog.setIcon(QMessageBox::Warning);
1501 
1502         if (dialog.exec() != QMessageBox::Yes) {
1503             event->ignore();
1504             return;
1505         }
1506 
1507         if (dialog.isChecked()) {
1508             settings.setValue(QSL("Browser-Tabs-Settings/AskOnClosing"), false);
1509         }
1510     }
1511 
1512     Q_EMIT aboutToClose();
1513 
1514     saveSettings();
1515     mApp->closedWindowsManager()->saveWindow(this);
1516 
1517     #ifndef Q_OS_MACOS
1518         if (mApp->windowCount() == 1)
1519             mApp->quitApplication();
1520     #endif
1521 
1522     event->accept();
1523 }
1524 
1525 void BrowserWindow::closeWindow()
1526 {
1527 #ifdef Q_OS_MACOS
1528     close();
1529     return;
1530 #endif
1531 
1532     if (mApp->windowCount() > 1) {
1533         close();
1534     }
1535 }
1536 
1537 void BrowserWindow::saveSettings()
1538 {
1539     if (mApp->isPrivate()) {
1540         return;
1541     }
1542 
1543     Settings settings;
1544     settings.beginGroup(QSL("Browser-View-Settings"));
1545     settings.setValue(QSL("WindowGeometry"), saveGeometry());
1546 
1547     const auto state = saveUiState();
1548     for (auto it = state.constBegin(); it != state.constEnd(); ++it) {
1549         settings.setValue(it.key(), it.value());
1550     }
1551 
1552     settings.endGroup();
1553 }
1554 
1555 void BrowserWindow::closeTab()
1556 {
1557     // Don't close pinned tabs with keyboard shortcuts (Ctrl+W, Ctrl+F4)
1558     if (weView() && !weView()->webTab()->isPinned()) {
1559         m_tabWidget->requestCloseTab();
1560     }
1561 }
1562 
1563 #ifdef QZ_WS_X11
1564 static xcb_connection_t *getXcbConnection()
1565 {
1566     const QNativeInterface::QX11Application *x11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
1567     if (x11App == nullptr)
1568         return nullptr;
1569     return x11App->connection();
1570 }
1571 
1572 int BrowserWindow::getCurrentVirtualDesktop() const
1573 {
1574     if (QGuiApplication::platformName() != QL1S("xcb"))
1575         return 0;
1576 
1577     xcb_connection_t *connection = getXcbConnection();
1578     if (connection == nullptr)
1579         return 0;
1580 
1581     xcb_intern_atom_cookie_t intern_atom;
1582     xcb_intern_atom_reply_t *atom_reply = nullptr;
1583     xcb_atom_t atom;
1584     xcb_get_property_cookie_t cookie;
1585     xcb_get_property_reply_t *reply = nullptr;
1586     uint32_t value;
1587 
1588     intern_atom = xcb_intern_atom(connection, false, qstrlen("_NET_WM_DESKTOP"), "_NET_WM_DESKTOP");
1589     atom_reply = xcb_intern_atom_reply(connection, intern_atom, nullptr);
1590 
1591     if (!atom_reply)
1592         goto error;
1593 
1594     atom = atom_reply->atom;
1595 
1596     cookie = xcb_get_property(connection, false, winId(), atom, XCB_ATOM_CARDINAL, 0, 1);
1597     reply = xcb_get_property_reply(connection, cookie, nullptr);
1598 
1599     if (!reply || reply->type != XCB_ATOM_CARDINAL || reply->value_len != 1 || reply->format != sizeof(uint32_t) * 8)
1600         goto error;
1601 
1602     value = *reinterpret_cast<uint32_t*>(xcb_get_property_value(reply));
1603 
1604     free(reply);
1605     free(atom_reply);
1606     return value;
1607 
1608 error:
1609     free(reply);
1610     free(atom_reply);
1611     return 0;
1612 }
1613 
1614 void BrowserWindow::moveToVirtualDesktop(int desktopId)
1615 {
1616     if (QGuiApplication::platformName() != QL1S("xcb"))
1617         return;
1618 
1619     // Don't move when window is already visible or it is first app window
1620     if (desktopId < 0 || isVisible() || m_windowType == Qz::BW_FirstAppWindow)
1621         return;
1622 
1623     xcb_connection_t *connection = getXcbConnection();
1624     if (connection == nullptr)
1625         return;
1626 
1627     xcb_intern_atom_cookie_t intern_atom;
1628     xcb_intern_atom_reply_t *atom_reply = nullptr;
1629     xcb_atom_t atom;
1630 
1631     intern_atom = xcb_intern_atom(connection, false, qstrlen("_NET_WM_DESKTOP"), "_NET_WM_DESKTOP");
1632     atom_reply = xcb_intern_atom_reply(connection, intern_atom, nullptr);
1633 
1634     if (!atom_reply)
1635         goto error;
1636 
1637     atom = atom_reply->atom;
1638 
1639     xcb_change_property(connection, XCB_PROP_MODE_REPLACE, winId(), atom,
1640                         XCB_ATOM_CARDINAL, 32, 1, (const void*) &desktopId);
1641 
1642 error:
1643     free(atom_reply);
1644 }
1645 #endif