1 /* ============================================================
2 * Falkon - Qt web browser
3 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 * ============================================================ */
18 #include "browserwindow.h"
19 #include "tabwidget.h"
20 #include "tabbar.h"
21 #include "webpage.h"
22 #include "tabbedwebview.h"
23 #include "lineedit.h"
24 #include "history.h"
25 #include "locationbar.h"
26 #include "websearchbar.h"
27 #include "pluginproxy.h"
28 #include "sidebar.h"
29 #include "cookiejar.h"
30 #include "cookiemanager.h"
31 #include "bookmarkstoolbar.h"
32 #include "clearprivatedata.h"
33 #include "autofill.h"
34 #include "mainapplication.h"
35 #include "checkboxdialog.h"
36 #include "clickablelabel.h"
37 #include "docktitlebarwidget.h"
38 #include "iconprovider.h"
39 #include "progressbar.h"
40 #include "closedwindowsmanager.h"
41 #include "statusbar.h"
42 #include "browsinglibrary.h"
43 #include "navigationbar.h"
44 #include "bookmarksimport/bookmarksimportdialog.h"
45 #include "qztools.h"
46 #include "reloadstopbutton.h"
47 #include "enhancedmenu.h"
48 #include "navigationcontainer.h"
49 #include "settings.h"
50 #include "qzsettings.h"
51 #include "speeddial.h"
52 #include "menubar.h"
53 #include "bookmarkstools.h"
54 #include "bookmarksmenu.h"
55 #include "historymenu.h"
56 #include "mainmenu.h"
57 #include "downloadsbutton.h"
58 #include "tabmodel.h"
59 #include "tabmrumodel.h"
60 
61 #include <algorithm>
62 
63 #include <QKeyEvent>
64 #include <QSplitter>
65 #include <QMenuBar>
66 #include <QTimer>
67 #include <QShortcut>
68 #include <QStackedWidget>
69 #include <QTextCodec>
70 #include <QFileDialog>
71 #include <QDesktopServices>
72 #include <QWebEngineHistory>
73 #include <QWebEngineSettings>
74 #include <QMessageBox>
75 #include <QDesktopWidget>
76 #include <QToolTip>
77 #include <QScrollArea>
78 #include <QCollator>
79 #include <QTemporaryFile>
80 
81 #ifdef QZ_WS_X11
82 #include <QX11Info>
83 #include <xcb/xcb.h>
84 #include <xcb/xcb_atom.h>
85 #endif
86 
87 static const int savedWindowVersion = 2;
88 
SavedWindow()89 BrowserWindow::SavedWindow::SavedWindow()
90 {
91 }
92 
SavedWindow(BrowserWindow * window)93 BrowserWindow::SavedWindow::SavedWindow(BrowserWindow *window)
94 {
95     windowState = window->isFullScreen() ? QByteArray() : window->saveState();
96     windowGeometry = window->saveGeometry();
97     windowUiState = window->saveUiState();
98 #ifdef QZ_WS_X11
99     virtualDesktop = window->getCurrentVirtualDesktop();
100 #endif
101 
102     const int tabsCount = window->tabCount();
103     tabs.reserve(tabsCount);
104     for (int i = 0; i < tabsCount; ++i) {
105         TabbedWebView *webView = window->weView(i);
106         if (!webView) {
107             continue;
108         }
109         WebTab* webTab = webView->webTab();
110         if (!webTab) {
111             continue;
112         }
113         WebTab::SavedTab tab(webTab);
114         if (!tab.isValid()) {
115             continue;
116         }
117         if (webTab->isCurrentTab()) {
118             currentTab = tabs.size();
119         }
120         tabs.append(tab);
121     }
122 }
123 
isValid() const124 bool BrowserWindow::SavedWindow::isValid() const
125 {
126     for (const WebTab::SavedTab &tab : qAsConst(tabs)) {
127         if (!tab.isValid()) {
128             return false;
129         }
130     }
131     return currentTab > -1;
132 }
133 
clear()134 void BrowserWindow::SavedWindow::clear()
135 {
136     windowState.clear();
137     windowGeometry.clear();
138     virtualDesktop = -1;
139     currentTab = -1;
140     tabs.clear();
141 }
142 
operator <<(QDataStream & stream,const BrowserWindow::SavedWindow & window)143 QDataStream &operator<<(QDataStream &stream, const BrowserWindow::SavedWindow &window)
144 {
145     stream << savedWindowVersion;
146     stream << window.windowState;
147     stream << window.windowGeometry;
148     stream << window.virtualDesktop;
149     stream << window.currentTab;
150     stream << window.tabs.count();
151 
152     for (int i = 0; i < window.tabs.count(); ++i) {
153         stream << window.tabs.at(i);
154     }
155 
156     stream << window.windowUiState;
157 
158     return stream;
159 }
160 
operator >>(QDataStream & stream,BrowserWindow::SavedWindow & window)161 QDataStream &operator>>(QDataStream &stream, BrowserWindow::SavedWindow &window)
162 {
163     int version;
164     stream >> version;
165 
166     if (version < 1) {
167         return stream;
168     }
169 
170     stream >> window.windowState;
171     stream >> window.windowGeometry;
172     stream >> window.virtualDesktop;
173     stream >> window.currentTab;
174 
175     int tabsCount = -1;
176     stream >> tabsCount;
177     window.tabs.reserve(tabsCount);
178 
179     for (int i = 0; i < tabsCount; ++i) {
180         WebTab::SavedTab tab;
181         stream >> tab;
182         window.tabs.append(tab);
183     }
184 
185     if (version >= 2) {
186         stream >> window.windowUiState;
187     }
188 
189     return stream;
190 }
191 
BrowserWindow(Qz::BrowserWindowType type,const QUrl & startUrl)192 BrowserWindow::BrowserWindow(Qz::BrowserWindowType type, const QUrl &startUrl)
193     : QMainWindow(nullptr)
194     , m_startUrl(startUrl)
195     , m_windowType(type)
196     , m_startTab(nullptr)
197     , m_startPage(nullptr)
198     , m_sideBarManager(new SideBarManager(this))
199     , m_hideNavigationTimer(nullptr)
200 {
201     setAttribute(Qt::WA_DeleteOnClose);
202     setAttribute(Qt::WA_DontCreateNativeAncestors);
203 
204     setObjectName(QSL("mainwindow"));
205     setWindowTitle(tr("Falkon"));
206     setProperty("private", mApp->isPrivate());
207 
208     setupUi();
209     setupMenu();
210 
211     m_hideNavigationTimer = new QTimer(this);
212     m_hideNavigationTimer->setInterval(1000);
213     m_hideNavigationTimer->setSingleShot(true);
214     connect(m_hideNavigationTimer, &QTimer::timeout, this, &BrowserWindow::hideNavigationSlot);
215 
216     connect(mApp, &MainApplication::settingsReloaded, this, &BrowserWindow::loadSettings);
217 
218     QTimer::singleShot(0, this, &BrowserWindow::postLaunch);
219 
220     if (mApp->isPrivate()) {
221         QzTools::setWmClass(QSL("Falkon Browser (Private Window)"), this);
222     }
223     else {
224         QzTools::setWmClass(QSL("Falkon Browser"), this);
225     }
226 }
227 
~BrowserWindow()228 BrowserWindow::~BrowserWindow()
229 {
230     mApp->plugins()->emitMainWindowDeleted(this);
231 
232     for (const QPointer<QWidget> &pointer : qAsConst(m_deleteOnCloseWidgets)) {
233         if (pointer) {
234             pointer->deleteLater();
235         }
236     }
237 }
238 
setStartTab(WebTab * tab)239 void BrowserWindow::setStartTab(WebTab* tab)
240 {
241     m_startTab = tab;
242 }
243 
setStartPage(WebPage * page)244 void BrowserWindow::setStartPage(WebPage *page)
245 {
246     m_startPage = page;
247 }
248 
postLaunch()249 void BrowserWindow::postLaunch()
250 {
251     loadSettings();
252 
253     bool addTab = true;
254     QUrl startUrl;
255 
256     switch (mApp->afterLaunch()) {
257     case MainApplication::OpenBlankPage:
258         startUrl = QUrl();
259         break;
260 
261     case MainApplication::OpenSpeedDial:
262         startUrl = QUrl(QSL("falkon:speeddial"));
263         break;
264 
265     case MainApplication::OpenHomePage:
266     case MainApplication::RestoreSession:
267     case MainApplication::SelectSession:
268         startUrl = m_homepage;
269         break;
270 
271     default:
272         break;
273     }
274 
275     if (!mApp->isTestModeEnabled()) {
276         show();
277     }
278 
279     switch (m_windowType) {
280     case Qz::BW_FirstAppWindow:
281         if (mApp->isStartingAfterCrash()) {
282             addTab = false;
283             startUrl.clear();
284             m_tabWidget->addView(QUrl(QSL("falkon:restore")), Qz::NT_CleanSelectedTabAtTheEnd);
285         }
286         else if (mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) {
287             addTab = m_tabWidget->count() <= 0;
288         }
289         break;
290 
291     case Qz::BW_NewWindow:
292     case Qz::BW_MacFirstWindow:
293         addTab = true;
294         break;
295 
296     case Qz::BW_OtherRestoredWindow:
297         addTab = false;
298         break;
299     }
300 
301     if (!m_startUrl.isEmpty()) {
302         startUrl = m_startUrl;
303         addTab = true;
304     }
305 
306     if (m_startTab) {
307         addTab = false;
308         m_tabWidget->addView(m_startTab, Qz::NT_SelectedTab);
309     }
310 
311     if (m_startPage) {
312         addTab = false;
313         m_tabWidget->addView(QUrl());
314         weView()->setPage(m_startPage);
315     }
316 
317     if (addTab) {
318         m_tabWidget->addView(startUrl, Qz::NT_CleanSelectedTabAtTheEnd);
319 
320         if (startUrl.isEmpty() || startUrl.toString() == QLatin1String("falkon:speeddial")) {
321             locationBar()->setFocus();
322         }
323     }
324 
325     // Something went really wrong .. add one tab
326     if (m_tabWidget->count() <= 0) {
327         m_tabWidget->addView(m_homepage, Qz::NT_SelectedTabAtTheEnd);
328     }
329 
330     mApp->plugins()->emitMainWindowCreated(this);
331     emit startingCompleted();
332 
333     raise();
334     activateWindow();
335     updateStartupFocus();
336 }
337 
setupUi()338 void BrowserWindow::setupUi()
339 {
340     Settings settings;
341     settings.beginGroup(QSL("Browser-View-Settings"));
342     const QByteArray windowGeometry = settings.value(QSL("WindowGeometry")).toByteArray();
343 
344     const QStringList keys = {
345         QSL("LocationBarWidth"),
346         QSL("WebSearchBarWidth"),
347         QSL("SideBarWidth"),
348         QSL("WebViewWidth"),
349         QSL("SideBar")
350     };
351     QHash<QString, QVariant> uiState;
352     for (const QString &key : keys) {
353         if (settings.contains(key)) {
354             uiState[key] = settings.value(key);
355         }
356     }
357     settings.endGroup();
358 
359     QWidget* widget = new QWidget(this);
360     widget->setCursor(Qt::ArrowCursor);
361     setCentralWidget(widget);
362 
363     m_mainLayout = new QVBoxLayout(widget);
364     m_mainLayout->setContentsMargins(0, 0, 0, 0);
365     m_mainLayout->setSpacing(0);
366     m_mainSplitter = new QSplitter(this);
367     m_mainSplitter->setObjectName(QSL("sidebar-splitter"));
368     m_tabWidget = new TabWidget(this);
369     m_superMenu = new QMenu(this);
370     m_navigationToolbar = new NavigationBar(this);
371     m_bookmarksToolbar = new BookmarksToolbar(this);
372 
373     m_tabModel = new TabModel(this, this);
374     m_tabMruModel = new TabMruModel(this, this);
375     m_tabMruModel->setSourceModel(m_tabModel);
376 
377     m_navigationContainer = new NavigationContainer(this);
378     m_navigationContainer->addWidget(m_navigationToolbar);
379     m_navigationContainer->addWidget(m_bookmarksToolbar);
380     m_navigationContainer->setTabBar(m_tabWidget->tabBar());
381 
382     m_mainSplitter->addWidget(m_tabWidget);
383     m_mainSplitter->setCollapsible(0, false);
384 
385     m_mainLayout->addWidget(m_navigationContainer);
386     m_mainLayout->addWidget(m_mainSplitter);
387 
388     m_statusBar = new StatusBar(this);
389     m_statusBar->setObjectName(QSL("mainwindow-statusbar"));
390     m_statusBar->setCursor(Qt::ArrowCursor);
391     setStatusBar(m_statusBar);
392     m_progressBar = new ProgressBar(m_statusBar);
393     m_ipLabel = new QLabel(this);
394     m_ipLabel->setObjectName(QSL("statusbar-ip-label"));
395     m_ipLabel->setToolTip(tr("IP Address of current page"));
396 
397     m_statusBar->addPermanentWidget(m_progressBar);
398     m_statusBar->addPermanentWidget(m_ipLabel);
399 
400     DownloadsButton *downloadsButton = new DownloadsButton(this);
401     m_statusBar->addButton(downloadsButton);
402     m_navigationToolbar->addToolButton(downloadsButton);
403 
404     QDesktopWidget* desktop = mApp->desktop();
405     int windowWidth = desktop->availableGeometry().width() / 1.3;
406     int windowHeight = desktop->availableGeometry().height() / 1.3;
407 
408     // Let the WM decides where to put new browser window
409     if (m_windowType != Qz::BW_FirstAppWindow && m_windowType != Qz::BW_MacFirstWindow && mApp->getWindow()) {
410 #ifdef Q_WS_WIN
411         // Windows WM places every new window in the middle of screen .. for some reason
412         QPoint p = mApp->getWindow()->geometry().topLeft();
413         p.setX(p.x() + 30);
414         p.setY(p.y() + 30);
415 
416         if (!desktop->availableGeometry(mApp->getWindow()).contains(p)) {
417             p.setX(desktop->availableGeometry(mApp->getWindow()).x() + 30);
418             p.setY(desktop->availableGeometry(mApp->getWindow()).y() + 30);
419         }
420 
421         setGeometry(QRect(p, mApp->getWindow()->size()));
422 #else
423         resize(mApp->getWindow()->size());
424 #endif
425     } else if (!restoreGeometry(windowGeometry)) {
426 #ifdef Q_WS_WIN
427         setGeometry(QRect(desktop->availableGeometry(mApp->getWindow()).x() + 30,
428                           desktop->availableGeometry(mApp->getWindow()).y() + 30, windowWidth, windowHeight));
429 #else
430         resize(windowWidth, windowHeight);
431 #endif
432     }
433 
434     restoreUiState(uiState);
435 
436     // Set some sane minimum width
437     setMinimumWidth(300);
438 }
439 
setupMenu()440 void BrowserWindow::setupMenu()
441 {
442 #ifdef Q_OS_MACOS
443     static MainMenu* macMainMenu = 0;
444 
445     if (!macMainMenu) {
446         macMainMenu = new MainMenu(this, 0);
447         macMainMenu->initMenuBar(new QMenuBar(0));
448         connect(mApp, SIGNAL(activeWindowChanged(BrowserWindow*)), macMainMenu, SLOT(setWindow(BrowserWindow*)));
449     }
450     else {
451         macMainMenu->setWindow(this);
452     }
453 
454     m_mainMenu = macMainMenu;
455 #else
456     setMenuBar(new MenuBar(this));
457 
458     m_mainMenu = new MainMenu(this, this);
459     m_mainMenu->initMenuBar(menuBar());
460 #endif
461     m_mainMenu->initSuperMenu(m_superMenu);
462 
463     // Setup other shortcuts
464     QShortcut* reloadBypassCacheAction = new QShortcut(QKeySequence(QSL("Ctrl+F5")), this);
465     QShortcut* reloadBypassCacheAction2 = new QShortcut(QKeySequence(QSL("Ctrl+Shift+R")), this);
466     connect(reloadBypassCacheAction, &QShortcut::activated, this, &BrowserWindow::reloadBypassCache);
467     connect(reloadBypassCacheAction2, &QShortcut::activated, this, &BrowserWindow::reloadBypassCache);
468 
469     QShortcut* closeTabAction = new QShortcut(QKeySequence(QSL("Ctrl+W")), this);
470     QShortcut* closeTabAction2 = new QShortcut(QKeySequence(QSL("Ctrl+F4")), this);
471     connect(closeTabAction, &QShortcut::activated, this, &BrowserWindow::closeTab);
472     connect(closeTabAction2, &QShortcut::activated, this, &BrowserWindow::closeTab);
473 
474     QShortcut* reloadAction = new QShortcut(QKeySequence(QSL("Ctrl+R")), this);
475     connect(reloadAction, &QShortcut::activated, this, &BrowserWindow::reload);
476 
477     QShortcut* openLocationAction = new QShortcut(QKeySequence(QSL("Alt+D")), this);
478     connect(openLocationAction, &QShortcut::activated, this, &BrowserWindow::openLocation);
479 
480     QShortcut* inspectorAction = new QShortcut(QKeySequence(QSL("F12")), this);
481     connect(inspectorAction, &QShortcut::activated, this, &BrowserWindow::toggleWebInspector);
482 
483     QShortcut* restoreClosedWindow = new QShortcut(QKeySequence(QSL("Ctrl+Shift+N")), this);
484     connect(restoreClosedWindow, &QShortcut::activated, mApp->closedWindowsManager(), &ClosedWindowsManager::restoreClosedWindow);
485 }
486 
updateStartupFocus()487 void BrowserWindow::updateStartupFocus()
488 {
489     QTimer::singleShot(500, this, [this]() {
490         // Scroll to current tab
491         tabWidget()->tabBar()->ensureVisible();
492         // Update focus
493         if (!m_startPage && LocationBar::convertUrlToText(weView()->page()->requestedUrl()).isEmpty())
494             locationBar()->setFocus();
495         else
496             weView()->setFocus();
497     });
498 }
499 
createEncodingAction(const QString & codecName,const QString & activeCodecName,QMenu * menu)500 QAction* BrowserWindow::createEncodingAction(const QString &codecName,
501                                              const QString &activeCodecName, QMenu* menu)
502 {
503     QAction* action = new QAction(codecName, menu);
504     action->setData(codecName);
505     action->setCheckable(true);
506     connect(action, &QAction::triggered, this, &BrowserWindow::changeEncoding);
507     if (activeCodecName.compare(codecName, Qt::CaseInsensitive) == 0) {
508         action->setChecked(true);
509     }
510     return action;
511 }
512 
createEncodingSubMenu(const QString & name,QStringList & codecNames,QMenu * menu)513 void BrowserWindow::createEncodingSubMenu(const QString &name, QStringList &codecNames, QMenu* menu)
514 {
515     if (codecNames.isEmpty()) {
516         return;
517     }
518 
519     QCollator collator;
520     collator.setNumericMode(true);
521     std::sort(codecNames.begin(), codecNames.end(), [collator](const QString &a, const QString &b) {
522         return collator.compare(a, b) < 0;
523     });
524 
525     QMenu* subMenu = new QMenu(name, menu);
526     const QString activeCodecName = mApp->webSettings()->defaultTextEncoding();
527 
528     QActionGroup *group = new QActionGroup(subMenu);
529 
530     for (const QString &codecName : qAsConst(codecNames)) {
531         QAction *act = createEncodingAction(codecName, activeCodecName, subMenu);
532         group->addAction(act);
533         subMenu->addAction(act);
534     }
535 
536     menu->addMenu(subMenu);
537 }
538 
saveUiState()539 QHash<QString, QVariant> BrowserWindow::saveUiState()
540 {
541     saveSideBarSettings();
542 
543     QHash<QString, QVariant> state;
544     state[QSL("LocationBarWidth")] = m_navigationToolbar->splitter()->sizes().at(0);
545     state[QSL("WebSearchBarWidth")] = m_navigationToolbar->splitter()->sizes().at(1);
546     state[QSL("SideBarWidth")] = m_sideBarWidth;
547     state[QSL("WebViewWidth")] = m_webViewWidth;
548     state[QSL("SideBar")] = m_sideBarManager->activeSideBar();
549     return state;
550 }
551 
restoreUiState(const QHash<QString,QVariant> & state)552 void BrowserWindow::restoreUiState(const QHash<QString, QVariant> &state)
553 {
554     const int locationBarWidth = state.value(QSL("LocationBarWidth"), 480).toInt();
555     const int websearchBarWidth = state.value(QSL("WebSearchBarWidth"), 140).toInt();
556     m_navigationToolbar->setSplitterSizes(locationBarWidth, websearchBarWidth);
557 
558     m_sideBarWidth = state.value(QSL("SideBarWidth"), 250).toInt();
559     m_webViewWidth = state.value(QSL("WebViewWidth"), 2000).toInt();
560     if (m_sideBar) {
561         m_mainSplitter->setSizes({m_sideBarWidth, m_webViewWidth});
562     }
563 
564     const QString activeSideBar = state.value(QSL("SideBar")).toString();
565     if (activeSideBar.isEmpty() && m_sideBar) {
566         m_sideBar->close();
567     } else {
568         m_sideBarManager->showSideBar(activeSideBar, false);
569     }
570 }
571 
loadSettings()572 void BrowserWindow::loadSettings()
573 {
574     Settings settings;
575 
576     //Url settings
577     settings.beginGroup(QSL("Web-URL-Settings"));
578     m_homepage = settings.value(QSL("homepage"), QSL("falkon:start")).toUrl();
579     settings.endGroup();
580 
581     //Browser Window settings
582     settings.beginGroup(QSL("Browser-View-Settings"));
583     bool showStatusBar = settings.value(QSL("showStatusBar"), false).toBool();
584     bool showBookmarksToolbar = settings.value(QSL("showBookmarksToolbar"), false).toBool();
585     bool showNavigationToolbar = settings.value(QSL("showNavigationToolbar"), true).toBool();
586     bool showMenuBar = settings.value(QSL("showMenubar"), false).toBool();
587 
588     // Make sure both menubar and navigationbar are not hidden
589     // Fixes #781
590     if (!showNavigationToolbar) {
591         showMenuBar = true;
592         settings.setValue(QSL("showMenubar"), true);
593     }
594 
595     settings.endGroup();
596 
597     settings.beginGroup(QSL("Shortcuts"));
598     m_useTabNumberShortcuts = settings.value(QSL("useTabNumberShortcuts"), true).toBool();
599     m_useSpeedDialNumberShortcuts = settings.value(QSL("useSpeedDialNumberShortcuts"), true).toBool();
600     m_useSingleKeyShortcuts = settings.value(QSL("useSingleKeyShortcuts"), false).toBool();
601     settings.endGroup();
602 
603     settings.beginGroup(QSL("Web-Browser-Settings"));
604     QAction *quitAction = m_mainMenu->action(QSL("Standard/Quit"));
605     if (settings.value(QSL("closeAppWithCtrlQ"), true).toBool()) {
606         quitAction->setShortcut(QzTools::actionShortcut(QKeySequence::Quit, QKeySequence(QSL("Ctrl+Q"))));
607     } else {
608         quitAction->setShortcut(QKeySequence());
609     }
610     settings.endGroup();
611 
612     m_statusBarVisible = showStatusBar;
613     statusBar()->setVisible(!isFullScreen() && showStatusBar);
614     m_bookmarksToolbar->setVisible(showBookmarksToolbar);
615     m_navigationToolbar->setVisible(showNavigationToolbar);
616 
617 #ifndef Q_OS_MACOS
618     m_menuBarVisible = showMenuBar;
619     menuBar()->setVisible(!isFullScreen() && showMenuBar);
620 #endif
621 
622     m_navigationToolbar->setSuperMenuVisible(isFullScreen() || !showMenuBar);
623 }
624 
goForward()625 void BrowserWindow::goForward()
626 {
627     weView()->forward();
628 }
629 
reload()630 void BrowserWindow::reload()
631 {
632     weView()->reload();
633 }
634 
reloadBypassCache()635 void BrowserWindow::reloadBypassCache()
636 {
637     weView()->reloadBypassCache();
638 }
639 
goBack()640 void BrowserWindow::goBack()
641 {
642     weView()->back();
643 }
644 
tabCount() const645 int BrowserWindow::tabCount() const
646 {
647     return m_tabWidget->count();
648 }
649 
weView() const650 TabbedWebView* BrowserWindow::weView() const
651 {
652     return weView(m_tabWidget->currentIndex());
653 }
654 
weView(int index) const655 TabbedWebView* BrowserWindow::weView(int index) const
656 {
657     WebTab* webTab = qobject_cast<WebTab*>(m_tabWidget->widget(index));
658     if (!webTab) {
659         return nullptr;
660     }
661 
662     return webTab->webView();
663 }
664 
locationBar() const665 LocationBar* BrowserWindow::locationBar() const
666 {
667     return qobject_cast<LocationBar*>(m_tabWidget->locationBars()->currentWidget());
668 }
669 
tabWidget() const670 TabWidget* BrowserWindow::tabWidget() const
671 {
672     return m_tabWidget;
673 }
674 
bookmarksToolbar() const675 BookmarksToolbar* BrowserWindow::bookmarksToolbar() const
676 {
677     return m_bookmarksToolbar;
678 }
679 
statusBar() const680 StatusBar* BrowserWindow::statusBar() const
681 {
682     return m_statusBar;
683 }
684 
navigationBar() const685 NavigationBar* BrowserWindow::navigationBar() const
686 {
687     return m_navigationToolbar;
688 }
689 
sideBarManager() const690 SideBarManager* BrowserWindow::sideBarManager() const
691 {
692     return m_sideBarManager;
693 }
694 
ipLabel() const695 QLabel* BrowserWindow::ipLabel() const
696 {
697     return m_ipLabel;
698 }
699 
superMenu() const700 QMenu* BrowserWindow::superMenu() const
701 {
702     return m_superMenu;
703 }
704 
homepageUrl() const705 QUrl BrowserWindow::homepageUrl() const
706 {
707     return m_homepage;
708 }
709 
windowType() const710 Qz::BrowserWindowType BrowserWindow::windowType() const
711 {
712     return m_windowType;
713 }
714 
action(const QString & name) const715 QAction* BrowserWindow::action(const QString &name) const
716 {
717     return m_mainMenu->action(name);
718 }
719 
tabModel() const720 TabModel *BrowserWindow::tabModel() const
721 {
722     return m_tabModel;
723 }
724 
tabMruModel() const725 TabMruModel *BrowserWindow::tabMruModel() const
726 {
727     return m_tabMruModel;
728 }
729 
setWindowTitle(const QString & t)730 void BrowserWindow::setWindowTitle(const QString &t)
731 {
732     QString title = t;
733 
734     if (mApp->isPrivate()) {
735         title.append(tr(" (Private Browsing)"));
736     }
737 
738     QMainWindow::setWindowTitle(title);
739 }
740 
changeEncoding()741 void BrowserWindow::changeEncoding()
742 {
743     if (QAction* action = qobject_cast<QAction*>(sender())) {
744         const QString encoding = action->data().toString();
745         mApp->webSettings()->setDefaultTextEncoding(encoding);
746 
747         Settings settings;
748         settings.setValue(QSL("Web-Browser-Settings/DefaultEncoding"), encoding);
749 
750         weView()->reload();
751     }
752 }
753 
printPage()754 void BrowserWindow::printPage()
755 {
756     weView()->printPage();
757 }
758 
bookmarkPage()759 void BrowserWindow::bookmarkPage()
760 {
761     TabbedWebView* view = weView();
762     BookmarksTools::addBookmarkDialog(this, view->url(), view->title());
763 }
764 
bookmarkAllTabs()765 void BrowserWindow::bookmarkAllTabs()
766 {
767     BookmarksTools::bookmarkAllTabsDialog(this, m_tabWidget);
768 }
769 
addBookmark(const QUrl & url,const QString & title)770 void BrowserWindow::addBookmark(const QUrl &url, const QString &title)
771 {
772     BookmarksTools::addBookmarkDialog(this, url, title);
773 }
774 
goHome()775 void BrowserWindow::goHome()
776 {
777     loadAddress(m_homepage);
778 }
779 
goHomeInNewTab()780 void BrowserWindow::goHomeInNewTab()
781 {
782     m_tabWidget->addView(m_homepage, Qz::NT_SelectedTab);
783 }
784 
loadActionUrl(QObject * obj)785 void BrowserWindow::loadActionUrl(QObject* obj)
786 {
787     if (!obj) {
788         obj = sender();
789     }
790 
791     if (QAction* action = qobject_cast<QAction*>(obj)) {
792         loadAddress(action->data().toUrl());
793     }
794 }
795 
loadActionUrlInNewTab(QObject * obj)796 void BrowserWindow::loadActionUrlInNewTab(QObject* obj)
797 {
798     if (!obj) {
799         obj = sender();
800     }
801 
802     if (QAction* action = qobject_cast<QAction*>(obj)) {
803         m_tabWidget->addView(action->data().toUrl(), Qz::NT_SelectedTabAtTheEnd);
804     }
805 }
806 
loadAddress(const QUrl & url)807 void BrowserWindow::loadAddress(const QUrl &url)
808 {
809     if (weView()->webTab()->isPinned()) {
810         int index = m_tabWidget->addView(url, qzSettings->newTabPosition);
811         weView(index)->setFocus();
812     } else {
813         weView()->setFocus();
814         weView()->load(LoadRequest(url));
815     }
816 }
817 
showHistoryManager()818 void BrowserWindow::showHistoryManager()
819 {
820     mApp->browsingLibrary()->showHistory(this);
821 }
822 
showSource(WebView * view)823 void BrowserWindow::showSource(WebView *view)
824 {
825     if (!view)
826         view = weView();
827     view->showSource();
828 }
829 
addSideBar()830 SideBar* BrowserWindow::addSideBar()
831 {
832     if (m_sideBar) {
833         return m_sideBar.data();
834     }
835 
836     m_sideBar = new SideBar(m_sideBarManager, this);
837 
838     m_mainSplitter->insertWidget(0, m_sideBar.data());
839     m_mainSplitter->setCollapsible(0, false);
840     m_mainSplitter->setSizes({m_sideBarWidth, m_webViewWidth});
841 
842     return m_sideBar.data();
843 }
844 
saveSideBarSettings()845 void BrowserWindow::saveSideBarSettings()
846 {
847     if (m_sideBar) {
848         // That +1 is important here, without it, the sidebar width would
849         // decrease by 1 pixel every close
850         m_sideBarWidth = m_mainSplitter->sizes().at(0) + 1;
851         m_webViewWidth = width() - m_sideBarWidth;
852     }
853 
854     Settings().setValue(QSL("Browser-View-Settings/SideBar"), m_sideBarManager->activeSideBar());
855 }
856 
toggleShowMenubar()857 void BrowserWindow::toggleShowMenubar()
858 {
859 #ifdef Q_OS_MACOS
860     // We use one shared global menubar on Mac that can't be hidden
861     return;
862 #endif
863 
864     setUpdatesEnabled(false);
865 
866     menuBar()->setVisible(!menuBar()->isVisible());
867     m_navigationToolbar->setSuperMenuVisible(!menuBar()->isVisible());
868 
869     setUpdatesEnabled(true);
870 
871     Settings().setValue(QSL("Browser-View-Settings/showMenubar"), menuBar()->isVisible());
872 
873     // Make sure we show Navigation Toolbar when Menu Bar is hidden
874     if (!m_navigationToolbar->isVisible() && !menuBar()->isVisible()) {
875         toggleShowNavigationToolbar();
876     }
877 }
878 
toggleShowStatusBar()879 void BrowserWindow::toggleShowStatusBar()
880 {
881     setUpdatesEnabled(false);
882 
883     m_statusBar->setVisible(!m_statusBar->isVisible());
884 
885     setUpdatesEnabled(true);
886 
887     Settings().setValue(QSL("Browser-View-Settings/showStatusBar"), m_statusBar->isVisible());
888 
889 }
890 
toggleShowBookmarksToolbar()891 void BrowserWindow::toggleShowBookmarksToolbar()
892 {
893     setUpdatesEnabled(false);
894 
895     m_bookmarksToolbar->setVisible(!m_bookmarksToolbar->isVisible());
896 
897     setUpdatesEnabled(true);
898 
899     Settings().setValue(QSL("Browser-View-Settings/showBookmarksToolbar"), m_bookmarksToolbar->isVisible());
900     Settings().setValue(QSL("Browser-View-Settings/instantBookmarksToolbar"), false);
901 }
902 
toggleShowNavigationToolbar()903 void BrowserWindow::toggleShowNavigationToolbar()
904 {
905     setUpdatesEnabled(false);
906 
907     m_navigationToolbar->setVisible(!m_navigationToolbar->isVisible());
908 
909     setUpdatesEnabled(true);
910 
911     Settings().setValue(QSL("Browser-View-Settings/showNavigationToolbar"), m_navigationToolbar->isVisible());
912 
913 #ifndef Q_OS_MACOS
914     // Make sure we show Menu Bar when Navigation Toolbar is hidden
915     if (!m_navigationToolbar->isVisible() && !menuBar()->isVisible()) {
916         toggleShowMenubar();
917     }
918 #endif
919 }
920 
toggleTabsOnTop(bool enable)921 void BrowserWindow::toggleTabsOnTop(bool enable)
922 {
923     qzSettings->tabsOnTop = enable;
924     m_navigationContainer->toggleTabsOnTop(enable);
925 }
926 
toggleFullScreen()927 void BrowserWindow::toggleFullScreen()
928 {
929     if (m_htmlFullScreenView) {
930         weView()->triggerPageAction(QWebEnginePage::ExitFullScreen);
931         return;
932     }
933 
934     if (isFullScreen()) {
935         setWindowState(windowState() & ~Qt::WindowFullScreen);
936     } else {
937         setWindowState(windowState() | Qt::WindowFullScreen);
938     }
939 }
940 
requestHtmlFullScreen(TabbedWebView * view,bool enable)941 void BrowserWindow::requestHtmlFullScreen(TabbedWebView *view, bool enable)
942 {
943     if (enable) {
944         setWindowState(windowState() | Qt::WindowFullScreen);
945     } else {
946         setWindowState(windowState() & ~Qt::WindowFullScreen);
947     }
948 
949     if (m_sideBar)
950         m_sideBar.data()->setHidden(enable);
951 
952     m_htmlFullScreenView = enable ? view : nullptr;
953 }
954 
showWebInspector()955 void BrowserWindow::showWebInspector()
956 {
957     if (weView() && weView()->webTab()) {
958         weView()->webTab()->showWebInspector();
959     }
960 }
961 
toggleWebInspector()962 void BrowserWindow::toggleWebInspector()
963 {
964     if (weView() && weView()->webTab()) {
965         weView()->webTab()->toggleWebInspector();
966     }
967 }
968 
currentTabChanged()969 void BrowserWindow::currentTabChanged()
970 {
971     TabbedWebView* view = weView();
972     m_navigationToolbar->setCurrentView(view);
973 
974     if (!view) {
975         return;
976     }
977 
978     const QString title = view->webTab()->title(/*allowEmpty*/true);
979     if (title.isEmpty()) {
980         setWindowTitle(tr("Falkon"));
981     } else {
982         setWindowTitle(tr("%1 - Falkon").arg(title));
983     }
984     m_ipLabel->setText(view->getIp());
985     view->setFocus();
986 
987     updateLoadingActions();
988 
989     // Setting correct tab order (LocationBar -> WebSearchBar -> WebView)
990     setTabOrder(locationBar(), m_navigationToolbar->webSearchBar());
991     setTabOrder(m_navigationToolbar->webSearchBar(), view);
992 }
993 
updateLoadingActions()994 void BrowserWindow::updateLoadingActions()
995 {
996     TabbedWebView* view = weView();
997     if (!view) {
998         return;
999     }
1000 
1001     bool isLoading = view->isLoading();
1002 
1003     m_ipLabel->setVisible(!isLoading);
1004     m_progressBar->setVisible(isLoading);
1005 
1006     action(QSL("View/Stop"))->setEnabled(isLoading);
1007     action(QSL("View/Reload"))->setEnabled(!isLoading);
1008 
1009     if (isLoading) {
1010         m_progressBar->setValue(view->loadingProgress());
1011         m_navigationToolbar->showStopButton();
1012     }
1013     else {
1014         m_navigationToolbar->showReloadButton();
1015     }
1016 }
1017 
addDeleteOnCloseWidget(QWidget * widget)1018 void BrowserWindow::addDeleteOnCloseWidget(QWidget* widget)
1019 {
1020     if (!m_deleteOnCloseWidgets.contains(widget)) {
1021         m_deleteOnCloseWidgets.append(widget);
1022     }
1023 }
1024 
restoreWindow(const SavedWindow & window)1025 void BrowserWindow::restoreWindow(const SavedWindow &window)
1026 {
1027     restoreState(window.windowState);
1028     restoreGeometry(window.windowGeometry);
1029     restoreUiState(window.windowUiState);
1030 #ifdef QZ_WS_X11
1031     moveToVirtualDesktop(window.virtualDesktop);
1032 #endif
1033     if (!mApp->isTestModeEnabled()) {
1034         show(); // Window has to be visible before adding QWebEngineView's
1035     }
1036     m_tabWidget->restoreState(window.tabs, window.currentTab);
1037     updateStartupFocus();
1038 }
1039 
createToolbarsMenu(QMenu * menu)1040 void BrowserWindow::createToolbarsMenu(QMenu* menu)
1041 {
1042     removeActions(menu->actions());
1043     menu->clear();
1044 
1045     QAction* action;
1046 
1047 #ifndef Q_OS_MACOS
1048     action = menu->addAction(tr("&Menu Bar"), this, &BrowserWindow::toggleShowMenubar);
1049     action->setCheckable(true);
1050     action->setChecked(menuBar()->isVisible());
1051 #endif
1052 
1053     action = menu->addAction(tr("&Navigation Toolbar"), this, &BrowserWindow::toggleShowNavigationToolbar);
1054     action->setCheckable(true);
1055     action->setChecked(m_navigationToolbar->isVisible());
1056 
1057     action = menu->addAction(tr("&Bookmarks Toolbar"), this, &BrowserWindow::toggleShowBookmarksToolbar);
1058     action->setCheckable(true);
1059     action->setChecked(Settings().value(QSL("Browser-View-Settings/showBookmarksToolbar")).toBool());
1060 
1061     menu->addSeparator();
1062 
1063     action = menu->addAction(tr("&Tabs on Top"), this, SLOT(toggleTabsOnTop(bool)));
1064     action->setCheckable(true);
1065     action->setChecked(qzSettings->tabsOnTop);
1066 
1067     addActions(menu->actions());
1068 }
1069 
createSidebarsMenu(QMenu * menu)1070 void BrowserWindow::createSidebarsMenu(QMenu* menu)
1071 {
1072     m_sideBarManager->createMenu(menu);
1073 }
1074 
createEncodingMenu(QMenu * menu)1075 void BrowserWindow::createEncodingMenu(QMenu* menu)
1076 {
1077     const QString activeCodecName = mApp->webSettings()->defaultTextEncoding();
1078 
1079     QStringList isoCodecs;
1080     QStringList utfCodecs;
1081     QStringList windowsCodecs;
1082     QStringList isciiCodecs;
1083     QStringList ibmCodecs;
1084     QStringList otherCodecs;
1085     QStringList allCodecs;
1086 
1087     const auto mibs = QTextCodec::availableMibs();
1088     for (const int mib : mibs) {
1089         const QString codecName = QString::fromUtf8(QTextCodec::codecForMib(mib)->name());
1090 
1091         if (!allCodecs.contains(codecName))
1092             allCodecs.append(codecName);
1093         else
1094             continue;
1095 
1096         if (codecName.startsWith(QLatin1String("ISO")))
1097             isoCodecs.append(codecName);
1098         else if (codecName.startsWith(QLatin1String("UTF")))
1099             utfCodecs.append(codecName);
1100         else if (codecName.startsWith(QLatin1String("windows")))
1101             windowsCodecs.append(codecName);
1102         else if (codecName.startsWith(QLatin1String("Iscii")))
1103             isciiCodecs.append(codecName);
1104         else if (codecName.startsWith(QLatin1String("IBM")))
1105             ibmCodecs.append(codecName);
1106         else
1107             otherCodecs.append(codecName);
1108     }
1109 
1110     if (!menu->isEmpty())
1111         menu->addSeparator();
1112 
1113     createEncodingSubMenu(QSL("ISO"), isoCodecs, menu);
1114     createEncodingSubMenu(QSL("UTF"), utfCodecs, menu);
1115     createEncodingSubMenu(QSL("Windows"), windowsCodecs, menu);
1116     createEncodingSubMenu(QSL("Iscii"), isciiCodecs, menu);
1117     createEncodingSubMenu(QSL("IBM"), ibmCodecs, menu);
1118     createEncodingSubMenu(tr("Other"), otherCodecs, menu);
1119 }
1120 
removeActions(const QList<QAction * > & actions)1121 void BrowserWindow::removeActions(const QList<QAction *> &actions)
1122 {
1123     for (QAction *action : actions) {
1124         removeAction(action);
1125     }
1126 }
1127 
addTab()1128 void BrowserWindow::addTab()
1129 {
1130     m_tabWidget->addView(QUrl(), Qz::NT_SelectedNewEmptyTab, true);
1131     m_tabWidget->setCurrentTabFresh(true);
1132 
1133     if (isFullScreen())
1134         showNavigationWithFullScreen();
1135 }
1136 
webSearch()1137 void BrowserWindow::webSearch()
1138 {
1139     m_navigationToolbar->webSearchBar()->setFocus();
1140     m_navigationToolbar->webSearchBar()->selectAll();
1141 }
1142 
searchOnPage()1143 void BrowserWindow::searchOnPage()
1144 {
1145     if (weView() && weView()->webTab()) {
1146         const QString searchText = weView()->page()->selectedText();
1147         if (!searchText.contains(QL1C('\n'))) {
1148             weView()->webTab()->showSearchToolBar(searchText);
1149         } else {
1150             weView()->webTab()->showSearchToolBar();
1151         }
1152     }
1153 }
1154 
openFile()1155 void BrowserWindow::openFile()
1156 {
1157     const QString fileTypes = QSL("%1(*.html *.htm *.shtml *.shtm *.xhtml);;"
1158                                       "%2(*.png *.jpg *.jpeg *.bmp *.gif *.svg *.tiff);;"
1159                                       "%3(*.txt);;"
1160                                       "%4(*.*)").arg(tr("HTML files"), tr("Image files"), tr("Text files"), tr("All files"));
1161 
1162     const QString filePath = QzTools::getOpenFileName(QSL("MainWindow-openFile"), this, tr("Open file..."), QDir::homePath(), fileTypes);
1163 
1164     if (!filePath.isEmpty()) {
1165         loadAddress(QUrl::fromLocalFile(filePath));
1166     }
1167 }
1168 
openLocation()1169 void BrowserWindow::openLocation()
1170 {
1171     if (isFullScreen()) {
1172         showNavigationWithFullScreen();
1173     }
1174 
1175     locationBar()->setFocus();
1176     locationBar()->selectAll();
1177 }
1178 
fullScreenNavigationVisible() const1179 bool BrowserWindow::fullScreenNavigationVisible() const
1180 {
1181     return m_navigationContainer->isVisible();
1182 }
1183 
showNavigationWithFullScreen()1184 void BrowserWindow::showNavigationWithFullScreen()
1185 {
1186     if (m_htmlFullScreenView)
1187         return;
1188 
1189     if (m_hideNavigationTimer->isActive()) {
1190         m_hideNavigationTimer->stop();
1191     }
1192 
1193     m_navigationContainer->show();
1194 }
1195 
hideNavigationWithFullScreen()1196 void BrowserWindow::hideNavigationWithFullScreen()
1197 {
1198     if (m_tabWidget->isCurrentTabFresh())
1199         return;
1200 
1201     if (!m_hideNavigationTimer->isActive()) {
1202         m_hideNavigationTimer->start();
1203     }
1204 }
1205 
hideNavigationSlot()1206 void BrowserWindow::hideNavigationSlot()
1207 {
1208     TabbedWebView* view = weView();
1209     bool mouseInView = view && view->underMouse();
1210 
1211     if (isFullScreen() && mouseInView) {
1212         m_navigationContainer->hide();
1213     }
1214 }
1215 
event(QEvent * event)1216 bool BrowserWindow::event(QEvent *event)
1217 {
1218     if (event->type() == QEvent::WindowStateChange) {
1219         QWindowStateChangeEvent *e = static_cast<QWindowStateChangeEvent*>(event);
1220         if (!(e->oldState() & Qt::WindowFullScreen) && windowState() & Qt::WindowFullScreen) {
1221             // Enter fullscreen
1222             m_statusBarVisible = m_statusBar->isVisible();
1223 #ifndef Q_OS_MACOS
1224             m_menuBarVisible = menuBar()->isVisible();
1225             menuBar()->hide();
1226 #endif
1227             m_statusBar->hide();
1228 
1229             m_navigationContainer->hide();
1230             m_navigationToolbar->enterFullScreen();
1231 
1232             // Show main menu button since menubar is hidden
1233             m_navigationToolbar->setSuperMenuVisible(true);
1234         }
1235         else if (e->oldState() & Qt::WindowFullScreen && !(windowState() & Qt::WindowFullScreen)) {
1236             // Leave fullscreen
1237             m_statusBar->setVisible(m_statusBarVisible);
1238 #ifndef Q_OS_MACOS
1239             menuBar()->setVisible(m_menuBarVisible);
1240 #endif
1241 
1242             m_navigationContainer->show();
1243             m_navigationToolbar->setSuperMenuVisible(!m_menuBarVisible);
1244             m_navigationToolbar->leaveFullScreen();
1245             m_htmlFullScreenView = nullptr;
1246         }
1247 
1248         if (m_hideNavigationTimer) {
1249             m_hideNavigationTimer->stop();
1250         }
1251     }
1252 
1253     return QMainWindow::event(event);
1254 }
1255 
resizeEvent(QResizeEvent * event)1256 void BrowserWindow::resizeEvent(QResizeEvent* event)
1257 {
1258     m_bookmarksToolbar->setMaximumWidth(width());
1259 
1260     QMainWindow::resizeEvent(event);
1261 }
1262 
keyPressEvent(QKeyEvent * event)1263 void BrowserWindow::keyPressEvent(QKeyEvent* event)
1264 {
1265     if (mApp->plugins()->processKeyPress(Qz::ON_BrowserWindow, this, event)) {
1266         return;
1267     }
1268 
1269     int number = -1;
1270     TabbedWebView* view = weView();
1271 
1272     switch (event->key()) {
1273     case Qt::Key_Back:
1274         if (view) {
1275             view->back();
1276             event->accept();
1277         }
1278         break;
1279 
1280     case Qt::Key_Forward:
1281         if (view) {
1282             view->forward();
1283             event->accept();
1284         }
1285         break;
1286 
1287     case Qt::Key_Stop:
1288         if (view) {
1289             view->stop();
1290             event->accept();
1291         }
1292         break;
1293 
1294     case Qt::Key_Reload:
1295     case Qt::Key_Refresh:
1296         if (view) {
1297             view->reload();
1298             event->accept();
1299         }
1300         break;
1301 
1302     case Qt::Key_HomePage:
1303         goHome();
1304         event->accept();
1305         break;
1306 
1307     case Qt::Key_Favorites:
1308         mApp->browsingLibrary()->showBookmarks(this);
1309         event->accept();
1310         break;
1311 
1312     case Qt::Key_Search:
1313         searchOnPage();
1314         event->accept();
1315         break;
1316 
1317     case Qt::Key_F6:
1318     case Qt::Key_OpenUrl:
1319         openLocation();
1320         event->accept();
1321         break;
1322 
1323     case Qt::Key_History:
1324         showHistoryManager();
1325         event->accept();
1326         break;
1327 
1328     case Qt::Key_AddFavorite:
1329         bookmarkPage();
1330         event->accept();
1331         break;
1332 
1333     case Qt::Key_News:
1334         action(QSL("Tools/RssReader"))->trigger();
1335         event->accept();
1336         break;
1337 
1338     case Qt::Key_Tools:
1339         action(QSL("Standard/Preferences"))->trigger();
1340         event->accept();
1341         break;
1342 
1343     case Qt::Key_Tab:
1344         if (event->modifiers() == Qt::ControlModifier) {
1345             static_cast<QObject*>(m_tabWidget)->event(event);
1346         }
1347         break;
1348 
1349     case Qt::Key_Backtab:
1350         if (event->modifiers() == (Qt::ControlModifier + Qt::ShiftModifier)) {
1351             static_cast<QObject*>(m_tabWidget)->event(event);
1352         }
1353         break;
1354 
1355     case Qt::Key_PageDown:
1356         if (event->modifiers() == Qt::ControlModifier) {
1357             m_tabWidget->nextTab();
1358             event->accept();
1359         }
1360         break;
1361 
1362     case Qt::Key_PageUp:
1363         if (event->modifiers() == Qt::ControlModifier) {
1364             m_tabWidget->previousTab();
1365             event->accept();
1366         }
1367         break;
1368 
1369     case Qt::Key_Equal:
1370         if (view && event->modifiers() == Qt::ControlModifier) {
1371             view->zoomIn();
1372             event->accept();
1373         }
1374         break;
1375 
1376     case Qt::Key_I:
1377         if (event->modifiers() == Qt::ControlModifier) {
1378             action(QSL("Tools/SiteInfo"))->trigger();
1379             event->accept();
1380         }
1381         break;
1382 
1383     case Qt::Key_U:
1384         if (event->modifiers() == Qt::ControlModifier) {
1385             action(QSL("View/PageSource"))->trigger();
1386             event->accept();
1387         }
1388         break;
1389 
1390     case Qt::Key_F:
1391         if (event->modifiers() == Qt::ControlModifier) {
1392             action(QSL("Edit/Find"))->trigger();
1393             event->accept();
1394         }
1395         break;
1396 
1397     case Qt::Key_Slash:
1398         if (m_useSingleKeyShortcuts) {
1399             action(QSL("Edit/Find"))->trigger();
1400             event->accept();
1401         }
1402         break;
1403 
1404     case Qt::Key_1:
1405         number = 1;
1406         break;
1407     case Qt::Key_2:
1408         number = 2;
1409         break;
1410     case Qt::Key_3:
1411         number = 3;
1412         break;
1413     case Qt::Key_4:
1414         number = 4;
1415         break;
1416     case Qt::Key_5:
1417         number = 5;
1418         break;
1419     case Qt::Key_6:
1420         number = 6;
1421         break;
1422     case Qt::Key_7:
1423         number = 7;
1424         break;
1425     case Qt::Key_8:
1426         number = 8;
1427         break;
1428     case Qt::Key_9:
1429         number = 9;
1430         break;
1431 
1432     default:
1433         break;
1434     }
1435 
1436     if (number != -1) {
1437         if (event->modifiers() & Qt::AltModifier && m_useTabNumberShortcuts) {
1438             if (number == 9) {
1439                 number = m_tabWidget->count();
1440             }
1441             m_tabWidget->setCurrentIndex(number - 1);
1442             event->accept();
1443             return;
1444         }
1445         if (event->modifiers() & Qt::ControlModifier && m_useSpeedDialNumberShortcuts) {
1446             const QUrl url = mApp->plugins()->speedDial()->urlForShortcut(number - 1);
1447             if (url.isValid()) {
1448                 loadAddress(url);
1449                 event->accept();
1450                 return;
1451             }
1452         }
1453         if (event->modifiers() == Qt::NoModifier && m_useSingleKeyShortcuts) {
1454             if (number == 1)
1455                 m_tabWidget->previousTab();
1456             if (number == 2)
1457                 m_tabWidget->nextTab();
1458         }
1459     }
1460 
1461     QMainWindow::keyPressEvent(event);
1462 }
1463 
keyReleaseEvent(QKeyEvent * event)1464 void BrowserWindow::keyReleaseEvent(QKeyEvent* event)
1465 {
1466     if (mApp->plugins()->processKeyRelease(Qz::ON_BrowserWindow, this, event)) {
1467         return;
1468     }
1469 
1470     switch (event->key()) {
1471     case Qt::Key_F:
1472         if (event->modifiers() == Qt::ControlModifier) {
1473             action(QSL("Edit/Find"))->trigger();
1474             event->accept();
1475         }
1476         break;
1477 
1478     default:
1479         break;
1480     }
1481 
1482     QMainWindow::keyReleaseEvent(event);
1483 }
1484 
closeEvent(QCloseEvent * event)1485 void BrowserWindow::closeEvent(QCloseEvent* event)
1486 {
1487     if (mApp->isClosing()) {
1488         saveSettings();
1489         return;
1490     }
1491 
1492     Settings settings;
1493     bool askOnClose = settings.value(QSL("Browser-Tabs-Settings/AskOnClosing"), true).toBool();
1494 
1495     if ((mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) && mApp->windowCount() == 1) {
1496         askOnClose = false;
1497     }
1498 
1499     if (askOnClose && m_tabWidget->normalTabsCount() > 1) {
1500         CheckBoxDialog dialog(QMessageBox::Yes | QMessageBox::No, this);
1501         dialog.setDefaultButton(QMessageBox::No);
1502         //~ singular There is still %n open tab and your session won't be stored.\nAre you sure you want to close this window?
1503         //~ plural There are still %n open tabs and your session won't be stored.\nAre you sure you want to close this window?
1504         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()));
1505         dialog.setCheckBoxText(tr("Don't ask again"));
1506         dialog.setWindowTitle(tr("There are still open tabs"));
1507         dialog.setIcon(QMessageBox::Warning);
1508 
1509         if (dialog.exec() != QMessageBox::Yes) {
1510             event->ignore();
1511             return;
1512         }
1513 
1514         if (dialog.isChecked()) {
1515             settings.setValue(QSL("Browser-Tabs-Settings/AskOnClosing"), false);
1516         }
1517     }
1518 
1519     emit aboutToClose();
1520 
1521     saveSettings();
1522     mApp->closedWindowsManager()->saveWindow(this);
1523 
1524     #ifndef Q_OS_MACOS
1525         if (mApp->windowCount() == 1)
1526             mApp->quitApplication();
1527     #endif
1528 
1529     event->accept();
1530 }
1531 
closeWindow()1532 void BrowserWindow::closeWindow()
1533 {
1534 #ifdef Q_OS_MACOS
1535     close();
1536     return;
1537 #endif
1538 
1539     if (mApp->windowCount() > 1) {
1540         close();
1541     }
1542 }
1543 
saveSettings()1544 void BrowserWindow::saveSettings()
1545 {
1546     if (mApp->isPrivate()) {
1547         return;
1548     }
1549 
1550     Settings settings;
1551     settings.beginGroup(QSL("Browser-View-Settings"));
1552     settings.setValue(QSL("WindowGeometry"), saveGeometry());
1553 
1554     const auto state = saveUiState();
1555     for (auto it = state.constBegin(); it != state.constEnd(); ++it) {
1556         settings.setValue(it.key(), it.value());
1557     }
1558 
1559     settings.endGroup();
1560 }
1561 
closeTab()1562 void BrowserWindow::closeTab()
1563 {
1564     // Don't close pinned tabs with keyboard shortcuts (Ctrl+W, Ctrl+F4)
1565     if (weView() && !weView()->webTab()->isPinned()) {
1566         m_tabWidget->requestCloseTab();
1567     }
1568 }
1569 
1570 #ifdef QZ_WS_X11
getCurrentVirtualDesktop() const1571 int BrowserWindow::getCurrentVirtualDesktop() const
1572 {
1573     if (QGuiApplication::platformName() != QL1S("xcb"))
1574         return 0;
1575 
1576     xcb_intern_atom_cookie_t intern_atom;
1577     xcb_intern_atom_reply_t *atom_reply = 0;
1578     xcb_atom_t atom;
1579     xcb_get_property_cookie_t cookie;
1580     xcb_get_property_reply_t *reply = 0;
1581     uint32_t value;
1582 
1583     intern_atom = xcb_intern_atom(QX11Info::connection(), false, qstrlen("_NET_WM_DESKTOP"), "_NET_WM_DESKTOP");
1584     atom_reply = xcb_intern_atom_reply(QX11Info::connection(), intern_atom, 0);
1585 
1586     if (!atom_reply)
1587         goto error;
1588 
1589     atom = atom_reply->atom;
1590 
1591     cookie = xcb_get_property(QX11Info::connection(), false, winId(), atom, XCB_ATOM_CARDINAL, 0, 1);
1592     reply = xcb_get_property_reply(QX11Info::connection(), cookie, 0);
1593 
1594     if (!reply || reply->type != XCB_ATOM_CARDINAL || reply->value_len != 1 || reply->format != sizeof(uint32_t) * 8)
1595         goto error;
1596 
1597     value = *reinterpret_cast<uint32_t*>(xcb_get_property_value(reply));
1598 
1599     free(reply);
1600     free(atom_reply);
1601     return value;
1602 
1603 error:
1604     free(reply);
1605     free(atom_reply);
1606     return 0;
1607 }
1608 
moveToVirtualDesktop(int desktopId)1609 void BrowserWindow::moveToVirtualDesktop(int desktopId)
1610 {
1611     if (QGuiApplication::platformName() != QL1S("xcb"))
1612         return;
1613 
1614     // Don't move when window is already visible or it is first app window
1615     if (desktopId < 0 || isVisible() || m_windowType == Qz::BW_FirstAppWindow)
1616         return;
1617 
1618     xcb_intern_atom_cookie_t intern_atom;
1619     xcb_intern_atom_reply_t *atom_reply = 0;
1620     xcb_atom_t atom;
1621 
1622     intern_atom = xcb_intern_atom(QX11Info::connection(), false, qstrlen("_NET_WM_DESKTOP"), "_NET_WM_DESKTOP");
1623     atom_reply = xcb_intern_atom_reply(QX11Info::connection(), intern_atom, 0);
1624 
1625     if (!atom_reply)
1626         goto error;
1627 
1628     atom = atom_reply->atom;
1629 
1630     xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, winId(), atom,
1631                         XCB_ATOM_CARDINAL, 32, 1, (const void*) &desktopId);
1632 
1633 error:
1634     free(atom_reply);
1635 }
1636 #endif
1637