File indexing completed on 2024-05-12 04:58:25

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 "tabwidget.h"
0019 #include "tabbar.h"
0020 #include "tabbedwebview.h"
0021 #include "webpage.h"
0022 #include "browserwindow.h"
0023 #include "mainapplication.h"
0024 #include "clickablelabel.h"
0025 #include "closedtabsmanager.h"
0026 #include "locationbar.h"
0027 #include "settings.h"
0028 #include "datapaths.h"
0029 #include "qzsettings.h"
0030 #include "qztools.h"
0031 #include "tabicon.h"
0032 #include "pluginproxy.h"
0033 
0034 #include <QFile>
0035 #include <QTimer>
0036 #include <QMimeData>
0037 #include <QStackedWidget>
0038 #include <QMouseEvent>
0039 #include <QWebEngineHistory>
0040 #include <QClipboard>
0041 
0042 AddTabButton::AddTabButton(TabWidget* tabWidget, TabBar* tabBar)
0043     : ToolButton(tabBar)
0044     , m_tabBar(tabBar)
0045     , m_tabWidget(tabWidget)
0046 {
0047     setObjectName("tabwidget-button-addtab");
0048     setAutoRaise(true);
0049     setFocusPolicy(Qt::NoFocus);
0050     setAcceptDrops(true);
0051     setToolTip(TabWidget::tr("New Tab"));
0052 }
0053 
0054 void AddTabButton::wheelEvent(QWheelEvent* event)
0055 {
0056     m_tabBar->wheelEvent(event);
0057 }
0058 
0059 void AddTabButton::mouseReleaseEvent(QMouseEvent* event)
0060 {
0061     if (event->button() == Qt::MiddleButton && rect().contains(event->position().toPoint())) {
0062         m_tabWidget->addTabFromClipboard();
0063     }
0064 
0065     ToolButton::mouseReleaseEvent(event);
0066 }
0067 
0068 void MenuTabs::mouseReleaseEvent(QMouseEvent* event)
0069 {
0070     if (event->button() == Qt::MiddleButton) {
0071         QAction* action = actionAt(event->position().toPoint());
0072         if (action && action->isEnabled()) {
0073             auto* tab = qobject_cast<WebTab*>(qvariant_cast<QWidget*>(action->data()));
0074             if (tab) {
0075                 Q_EMIT closeTab(tab->tabIndex());
0076                 action->setEnabled(false);
0077                 event->accept();
0078             }
0079         }
0080     }
0081     QMenu::mouseReleaseEvent(event);
0082 }
0083 
0084 TabWidget::TabWidget(BrowserWindow *window, QWidget *parent)
0085     : TabStackedWidget(parent)
0086     , m_window(window)
0087     , m_locationBars(new QStackedWidget)
0088     , m_closedTabsManager(new ClosedTabsManager)
0089 {
0090     setObjectName(QSL("tabwidget"));
0091 
0092     m_tabBar = new TabBar(m_window, this);
0093     setTabBar(m_tabBar);
0094 
0095     connect(this, &TabWidget::changed, mApp, &MainApplication::changeOccurred);
0096     connect(this, &TabStackedWidget::pinStateChanged, this, &TabWidget::changed);
0097 
0098     connect(m_tabBar, &ComboTabBar::tabCloseRequested, this, &TabWidget::requestCloseTab);
0099     connect(m_tabBar, &TabBar::tabMoved, this, &TabWidget::tabWasMoved);
0100 
0101     connect(m_tabBar, &TabBar::moveAddTabButton, this, &TabWidget::moveAddTabButton);
0102 
0103     connect(mApp, &MainApplication::settingsReloaded, this, &TabWidget::loadSettings);
0104 
0105     m_menuTabs = new MenuTabs(this);
0106     connect(m_menuTabs, &MenuTabs::closeTab, this, &TabWidget::requestCloseTab);
0107 
0108     m_menuClosedTabs = new QMenu(this);
0109 
0110     // AddTab button displayed next to last tab
0111     m_buttonAddTab = new AddTabButton(this, m_tabBar);
0112     m_buttonAddTab->setProperty("outside-tabbar", false);
0113     connect(m_buttonAddTab, &QAbstractButton::clicked, m_window, &BrowserWindow::addTab);
0114 
0115     // AddTab button displayed outside tabbar (as corner widget)
0116     m_buttonAddTab2 = new AddTabButton(this, m_tabBar);
0117     m_buttonAddTab2->setProperty("outside-tabbar", true);
0118     m_buttonAddTab2->hide();
0119     connect(m_buttonAddTab2, &QAbstractButton::clicked, m_window, &BrowserWindow::addTab);
0120 
0121     // ClosedTabs button displayed as a permanent corner widget
0122     m_buttonClosedTabs = new ToolButton(m_tabBar);
0123     m_buttonClosedTabs->setObjectName("tabwidget-button-closedtabs");
0124     m_buttonClosedTabs->setMenu(m_menuClosedTabs);
0125     m_buttonClosedTabs->setPopupMode(QToolButton::InstantPopup);
0126     m_buttonClosedTabs->setToolTip(tr("Closed tabs"));
0127     m_buttonClosedTabs->setAutoRaise(true);
0128     m_buttonClosedTabs->setFocusPolicy(Qt::NoFocus);
0129     m_buttonClosedTabs->setShowMenuInside(true);
0130     connect(m_buttonClosedTabs, &ToolButton::aboutToShowMenu, this, &TabWidget::aboutToShowClosedTabsMenu);
0131 
0132     // ListTabs button is showed only when tabbar overflows
0133     m_buttonListTabs = new ToolButton(m_tabBar);
0134     m_buttonListTabs->setObjectName("tabwidget-button-opentabs");
0135     m_buttonListTabs->setMenu(m_menuTabs);
0136     m_buttonListTabs->setPopupMode(QToolButton::InstantPopup);
0137     m_buttonListTabs->setToolTip(tr("List of tabs"));
0138     m_buttonListTabs->setAutoRaise(true);
0139     m_buttonListTabs->setFocusPolicy(Qt::NoFocus);
0140     m_buttonListTabs->setShowMenuInside(true);
0141     m_buttonListTabs->hide();
0142     connect(m_buttonListTabs, &ToolButton::aboutToShowMenu, this, &TabWidget::aboutToShowTabsMenu);
0143 
0144     m_tabBar->addCornerWidget(m_buttonAddTab2, Qt::TopRightCorner);
0145     m_tabBar->addCornerWidget(m_buttonClosedTabs, Qt::TopRightCorner);
0146     m_tabBar->addCornerWidget(m_buttonListTabs, Qt::TopRightCorner);
0147     connect(m_tabBar, &ComboTabBar::overFlowChanged, this, &TabWidget::tabBarOverFlowChanged);
0148 
0149     loadSettings();
0150 }
0151 
0152 BrowserWindow *TabWidget::browserWindow() const
0153 {
0154     return m_window;
0155 }
0156 
0157 void TabWidget::loadSettings()
0158 {
0159     Settings settings;
0160     settings.beginGroup(QSL("Browser-Tabs-Settings"));
0161     m_dontCloseWithOneTab = settings.value(QSL("dontCloseWithOneTab"), false).toBool();
0162     m_showClosedTabsButton = settings.value(QSL("showClosedTabsButton"), false).toBool();
0163     m_newTabAfterActive = settings.value(QSL("newTabAfterActive"), true).toBool();
0164     m_newEmptyTabAfterActive = settings.value(QSL("newEmptyTabAfterActive"), false).toBool();
0165     settings.endGroup();
0166 
0167     settings.beginGroup(QSL("Web-URL-Settings"));
0168     m_urlOnNewTab = settings.value(QSL("newTabUrl"), QSL("falkon:speeddial")).toUrl();
0169     settings.endGroup();
0170 
0171     m_tabBar->loadSettings();
0172 
0173     updateClosedTabsButton();
0174 }
0175 
0176 WebTab* TabWidget::weTab() const
0177 {
0178     return weTab(currentIndex());
0179 }
0180 
0181 WebTab* TabWidget::weTab(int index) const
0182 {
0183     return qobject_cast<WebTab*>(widget(index));
0184 }
0185 
0186 TabIcon* TabWidget::tabIcon(int index) const
0187 {
0188     return weTab(index)->tabIcon();
0189 }
0190 
0191 bool TabWidget::validIndex(int index) const
0192 {
0193     return index >= 0 && index < count();
0194 }
0195 
0196 void TabWidget::updateClosedTabsButton()
0197 {
0198     m_buttonClosedTabs->setVisible(m_showClosedTabsButton && canRestoreTab());
0199 }
0200 
0201 void TabWidget::keyPressEvent(QKeyEvent *event)
0202 {
0203     if (mApp->plugins()->processKeyPress(Qz::ON_TabWidget, this, event)) {
0204         return;
0205     }
0206 
0207     TabStackedWidget::keyPressEvent(event);
0208 }
0209 
0210 void TabWidget::keyReleaseEvent(QKeyEvent *event)
0211 {
0212     if (mApp->plugins()->processKeyRelease(Qz::ON_TabWidget, this, event)) {
0213         return;
0214     }
0215 
0216     TabStackedWidget::keyReleaseEvent(event);
0217 }
0218 
0219 bool TabWidget::isCurrentTabFresh() const
0220 {
0221     return m_currentTabFresh;
0222 }
0223 
0224 void TabWidget::setCurrentTabFresh(bool currentTabFresh)
0225 {
0226     m_currentTabFresh = currentTabFresh;
0227 }
0228 
0229 void TabWidget::tabBarOverFlowChanged(bool overflowed)
0230 {
0231     // Show buttons inside tabbar
0232     m_buttonAddTab->setVisible(!overflowed);
0233 
0234     // Show buttons displayed outside tabbar (corner widgets)
0235     m_buttonAddTab2->setVisible(overflowed);
0236     m_buttonListTabs->setVisible(overflowed);
0237 }
0238 
0239 void TabWidget::moveAddTabButton(int posX)
0240 {
0241     int posY = (m_tabBar->height() - m_buttonAddTab->height()) / 2;
0242 
0243     if (QApplication::layoutDirection() == Qt::RightToLeft) {
0244         posX = qMax(posX - m_buttonAddTab->width(), 0);
0245     }
0246     else {
0247         posX = qMin(posX, m_tabBar->width() - m_buttonAddTab->width());
0248     }
0249 
0250     m_buttonAddTab->move(posX, posY);
0251 }
0252 
0253 void TabWidget::aboutToShowTabsMenu()
0254 {
0255     m_menuTabs->clear();
0256 
0257     for (int i = 0; i < count(); i++) {
0258         WebTab* tab = weTab(i);
0259         if (!tab || tab->isPinned()) {
0260             continue;
0261         }
0262 
0263         auto* action = new QAction(this);
0264         action->setIcon(tab->icon());
0265 
0266         if (i == currentIndex()) {
0267             QFont f = action->font();
0268             f.setBold(true);
0269             action->setFont(f);
0270         }
0271 
0272         QString title = tab->title();
0273         title.replace(QLatin1Char('&'), QLatin1String("&&"));
0274         action->setText(QzTools::truncatedText(title, 40));
0275 
0276         action->setData(QVariant::fromValue(qobject_cast<QWidget*>(tab)));
0277         connect(action, &QAction::triggered, this, &TabWidget::actionChangeIndex);
0278         m_menuTabs->addAction(action);
0279     }
0280 }
0281 
0282 void TabWidget::aboutToShowClosedTabsMenu()
0283 {
0284     m_menuClosedTabs->clear();
0285 
0286     const auto closedTabs = closedTabsManager()->closedTabs();
0287     for (int i = 0; i < closedTabs.count(); ++i) {
0288         const ClosedTabsManager::Tab tab = closedTabs.at(i);
0289         const QString title = QzTools::truncatedText(tab.tabState.title, 40);
0290         m_menuClosedTabs->addAction(tab.tabState.icon, title, this, SLOT(restoreClosedTab()))->setData(i);
0291     }
0292 
0293     if (m_menuClosedTabs->isEmpty()) {
0294         m_menuClosedTabs->addAction(tr("Empty"))->setEnabled(false);
0295     }
0296     else {
0297         m_menuClosedTabs->addSeparator();
0298         m_menuClosedTabs->addAction(tr("Restore All Closed Tabs"), this, &TabWidget::restoreAllClosedTabs);
0299         m_menuClosedTabs->addAction(QIcon::fromTheme(QSL("edit-clear")), tr("Clear list"), this, &TabWidget::clearClosedTabsList);
0300     }
0301 }
0302 
0303 void TabWidget::actionChangeIndex()
0304 {
0305     if (auto* action = qobject_cast<QAction*>(sender())) {
0306         auto* tab = qobject_cast<WebTab*>(qvariant_cast<QWidget*>(action->data()));
0307         if (tab) {
0308             m_tabBar->ensureVisible(tab->tabIndex());
0309             setCurrentIndex(tab->tabIndex());
0310         }
0311     }
0312 }
0313 
0314 int TabWidget::addView(const LoadRequest &req, const Qz::NewTabPositionFlags &openFlags, bool selectLine, bool pinned)
0315 {
0316     return addView(req, QString(), openFlags, selectLine, -1, pinned);
0317 }
0318 
0319 int TabWidget::addView(const LoadRequest &req, const QString &title, const Qz::NewTabPositionFlags &openFlags, bool selectLine, int position, bool pinned)
0320 {
0321     QUrl url = req.url();
0322     m_currentTabFresh = false;
0323 
0324     if (url.isEmpty() && !(openFlags & Qz::NT_CleanTab)) {
0325         url = m_urlOnNewTab;
0326     }
0327 
0328     bool openAfterActive = m_newTabAfterActive && !(openFlags & Qz::NT_TabAtTheEnd);
0329 
0330     if (openFlags == Qz::NT_SelectedNewEmptyTab && m_newEmptyTabAfterActive) {
0331         openAfterActive = true;
0332     }
0333 
0334     if (openAfterActive && position == -1) {
0335         // If we are opening newBgTab from pinned tab, make sure it won't be
0336         // opened between other pinned tabs
0337         if (openFlags & Qz::NT_NotSelectedTab && m_lastBackgroundTab) {
0338             position = m_lastBackgroundTab->tabIndex() + 1;
0339         }
0340         else {
0341             position = qMax(currentIndex() + 1, m_tabBar->pinnedTabsCount());
0342         }
0343     }
0344 
0345     auto* webTab = new WebTab(m_window);
0346     webTab->setPinned(pinned);
0347     webTab->locationBar()->showUrl(url);
0348     m_locationBars->addWidget(webTab->locationBar());
0349 
0350     int index = insertTab(position == -1 ? count() : position, webTab, QString(), pinned);
0351     webTab->attach(m_window);
0352 
0353     if (!title.isEmpty()) {
0354         m_tabBar->setTabText(index, title);
0355     }
0356 
0357     if (openFlags & Qz::NT_SelectedTab) {
0358         setCurrentIndex(index);
0359     } else {
0360         m_lastBackgroundTab = webTab;
0361     }
0362 
0363     connect(webTab->webView(), &TabbedWebView::wantsCloseTab, this, &TabWidget::closeTab);
0364     connect(webTab->webView(), &QWebEngineView::urlChanged, this, &TabWidget::changed);
0365     connect(webTab->webView(), &TabbedWebView::ipChanged, m_window->ipLabel(), &QLabel::setText);
0366     connect(webTab->webView(), &WebView::urlChanged, this, [this](const QUrl &url) {
0367         if (url != m_urlOnNewTab)
0368             m_currentTabFresh = false;
0369     });
0370 
0371     if (url.isValid() && url != req.url()) {
0372         LoadRequest r(req);
0373         r.setUrl(url);
0374         webTab->webView()->load(r);
0375     }
0376     else if (req.url().isValid()) {
0377         webTab->webView()->load(req);
0378     }
0379 
0380     if (selectLine && m_window->locationBar()->text().isEmpty()) {
0381         m_window->locationBar()->setFocus();
0382     }
0383 
0384     // Make sure user notice opening new background tabs
0385     if (!(openFlags & Qz::NT_SelectedTab)) {
0386         m_tabBar->ensureVisible(index);
0387     }
0388 
0389     Q_EMIT changed();
0390     Q_EMIT tabInserted(index);
0391 
0392     return index;
0393 }
0394 
0395 int TabWidget::addView(WebTab *tab, const Qz::NewTabPositionFlags &openFlags)
0396 {
0397     return insertView(count() + 1, tab, openFlags);
0398 }
0399 
0400 int TabWidget::insertView(int index, WebTab *tab, const Qz::NewTabPositionFlags &openFlags)
0401 {
0402     m_locationBars->addWidget(tab->locationBar());
0403     int newIndex = insertTab(index, tab, QString(), tab->isPinned());
0404     tab->attach(m_window);
0405 
0406     if (openFlags.testFlag(Qz::NT_SelectedTab)) {
0407         setCurrentIndex(newIndex);
0408     } else {
0409         m_lastBackgroundTab = tab;
0410     }
0411 
0412     connect(tab->webView(), &TabbedWebView::wantsCloseTab, this, &TabWidget::closeTab);
0413     connect(tab->webView(), &QWebEngineView::urlChanged, this, &TabWidget::changed);
0414     connect(tab->webView(), &TabbedWebView::ipChanged, m_window->ipLabel(), &QLabel::setText);
0415 
0416     // Make sure user notice opening new background tabs
0417     if (!(openFlags & Qz::NT_SelectedTab)) {
0418         m_tabBar->ensureVisible(index);
0419     }
0420 
0421     Q_EMIT changed();
0422     Q_EMIT tabInserted(newIndex);
0423 
0424     return newIndex;
0425 }
0426 
0427 void TabWidget::addTabFromClipboard()
0428 {
0429     QString selectionClipboard = QApplication::clipboard()->text(QClipboard::Selection);
0430     QUrl guessedUrl = QUrl::fromUserInput(selectionClipboard);
0431 
0432     if (!guessedUrl.isEmpty()) {
0433         addView(guessedUrl, Qz::NT_SelectedNewEmptyTab);
0434     }
0435 }
0436 
0437 void TabWidget::closeTab(int index)
0438 {
0439     if (index == -1)
0440         index = currentIndex();
0441 
0442     WebTab *webTab = weTab(index);
0443     if (!webTab || !validIndex(index))
0444         return;
0445 
0446     // This is already handled in requestCloseTab
0447     if (count() <= 1) {
0448         requestCloseTab(index);
0449         return;
0450     }
0451 
0452     m_closedTabsManager->saveTab(webTab);
0453 
0454     TabbedWebView *webView = webTab->webView();
0455     m_locationBars->removeWidget(webView->webTab()->locationBar());
0456     disconnect(webView, &TabbedWebView::wantsCloseTab, this, &TabWidget::closeTab);
0457     disconnect(webView, &QWebEngineView::urlChanged, this, &TabWidget::changed);
0458     disconnect(webView, &TabbedWebView::ipChanged, m_window->ipLabel(), &QLabel::setText);
0459 
0460     m_lastBackgroundTab = nullptr;
0461 
0462     webTab->detach();
0463     webTab->deleteLater();
0464 
0465     updateClosedTabsButton();
0466 
0467     Q_EMIT changed();
0468     Q_EMIT tabRemoved(index);
0469 }
0470 
0471 void TabWidget::requestCloseTab(int index)
0472 {
0473     if (index == -1)
0474         index = currentIndex();
0475 
0476     WebTab *webTab = weTab(index);
0477     if (!webTab || !validIndex(index))
0478         return;
0479 
0480     TabbedWebView *webView = webTab->webView();
0481 
0482     // This would close last tab, so we close the window instead
0483     if (count() <= 1) {
0484         // If we are not closing window upon closing last tab, let's just load new-tab-url
0485         if (m_dontCloseWithOneTab) {
0486             // We don't want to accumulate more than one closed tab, if user tries
0487             // to close the last tab multiple times
0488             if (webView->url() != m_urlOnNewTab) {
0489                 m_closedTabsManager->saveTab(webTab);
0490             }
0491             webView->zoomReset();
0492             webView->load(m_urlOnNewTab);
0493             return;
0494         }
0495         m_window->close();
0496         return;
0497     }
0498 
0499     webView->triggerPageAction(QWebEnginePage::RequestClose);
0500 }
0501 
0502 void TabWidget::currentTabChanged(int index)
0503 {
0504     if (!validIndex(index))
0505         return;
0506 
0507     m_lastBackgroundTab = nullptr;
0508     m_currentTabFresh = false;
0509 
0510     if (!m_tabBar->isRestoring()) {
0511         WebTab* webTab = weTab(index);
0512         webTab->tabActivated();
0513 
0514         LocationBar* locBar = webTab->locationBar();
0515 
0516         if (locBar && m_locationBars->indexOf(locBar) != -1) {
0517             m_locationBars->setCurrentWidget(locBar);
0518         }
0519 
0520         m_window->currentTabChanged();
0521     }
0522 
0523     Q_EMIT changed();
0524 }
0525 
0526 void TabWidget::tabWasMoved(int before, int after)
0527 {
0528     m_lastBackgroundTab = nullptr;
0529 
0530     Q_EMIT changed();
0531     if (!m_blockTabMovedSignal) {
0532         Q_EMIT tabMoved(before, after);
0533     }
0534 }
0535 
0536 void TabWidget::setCurrentIndex(int index)
0537 {
0538     TabStackedWidget::setCurrentIndex(index);
0539 }
0540 
0541 void TabWidget::nextTab()
0542 {
0543     setCurrentIndex((currentIndex() + 1) % count());
0544 }
0545 
0546 void TabWidget::previousTab()
0547 {
0548     setCurrentIndex(currentIndex() == 0 ? count() - 1 : currentIndex() - 1);
0549 }
0550 
0551 int TabWidget::normalTabsCount() const
0552 {
0553     return m_tabBar->normalTabsCount();
0554 }
0555 
0556 int TabWidget::pinnedTabsCount() const
0557 {
0558     return m_tabBar->pinnedTabsCount();
0559 }
0560 
0561 void TabWidget::reloadTab(int index)
0562 {
0563     if (!validIndex(index)) {
0564         return;
0565     }
0566 
0567     weTab(index)->reload();
0568 }
0569 
0570 WebTab *TabWidget::webTab(int index) const
0571 {
0572     return index < 0 ? weTab() : weTab(index);
0573 }
0574 
0575 int TabWidget::extraReservedWidth() const
0576 {
0577     return m_buttonAddTab->width();
0578 }
0579 
0580 TabBar* TabWidget::tabBar() const
0581 {
0582     return m_tabBar;
0583 }
0584 
0585 ClosedTabsManager* TabWidget::closedTabsManager() const
0586 {
0587     return m_closedTabsManager;
0588 }
0589 
0590 void TabWidget::reloadAllTabs()
0591 {
0592     for (int i = 0; i < count(); i++) {
0593         reloadTab(i);
0594     }
0595 }
0596 
0597 void TabWidget::stopTab(int index)
0598 {
0599     if (!validIndex(index)) {
0600         return;
0601     }
0602 
0603     weTab(index)->stop();
0604 }
0605 
0606 void TabWidget::closeAllButCurrent(int index)
0607 {
0608     if (!validIndex(index)) {
0609         return;
0610     }
0611 
0612     WebTab* akt = weTab(index);
0613 
0614     const auto tabs = allTabs(false);
0615     for (const WebTab* tab : tabs) {
0616         int tabIndex = tab->tabIndex();
0617         if (akt == widget(tabIndex)) {
0618             continue;
0619         }
0620         requestCloseTab(tabIndex);
0621     }
0622 }
0623 
0624 void TabWidget::closeToRight(int index)
0625 {
0626     if (!validIndex(index)) {
0627         return;
0628     }
0629 
0630     const auto tabs = allTabs(false);
0631     for (const WebTab* tab : tabs) {
0632         int tabIndex = tab->tabIndex();
0633         if (index >= tabIndex) {
0634             continue;
0635         }
0636         requestCloseTab(tabIndex);
0637     }
0638 }
0639 
0640 
0641 void TabWidget::closeToLeft(int index)
0642 {
0643     if (!validIndex(index)) {
0644         return;
0645     }
0646 
0647     const auto tabs = allTabs(false);
0648     for (const WebTab* tab : tabs) {
0649         int tabIndex = tab->tabIndex();
0650         if (index <= tabIndex) {
0651             continue;
0652         }
0653         requestCloseTab(tabIndex);
0654     }
0655 }
0656 
0657 void TabWidget::moveTab(int from, int to)
0658 {
0659     if (!validIndex(to) || from == to) {
0660         return;
0661     }
0662     WebTab *tab = webTab(from);
0663     if (!tab) {
0664         return;
0665     }
0666     m_blockTabMovedSignal = true;
0667     // (Un)pin tab when needed
0668     if ((tab->isPinned() && to >= pinnedTabsCount()) || (!tab->isPinned() && to < pinnedTabsCount())) {
0669         tab->togglePinned();
0670     }
0671     TabStackedWidget::moveTab(tab->tabIndex(), to);
0672     m_blockTabMovedSignal = false;
0673     Q_EMIT tabMoved(from, to);
0674 }
0675 
0676 int TabWidget::pinUnPinTab(int index, const QString &title)
0677 {
0678     const int newIndex = TabStackedWidget::pinUnPinTab(index, title);
0679     if (index != newIndex && !m_blockTabMovedSignal) {
0680         Q_EMIT tabMoved(index, newIndex);
0681     }
0682     return newIndex;
0683 }
0684 
0685 void TabWidget::detachTab(WebTab* tab)
0686 {
0687     Q_ASSERT(tab);
0688 
0689     if (count() == 1 && mApp->windowCount() == 1) {
0690         return;
0691     }
0692 
0693     m_locationBars->removeWidget(tab->locationBar());
0694     disconnect(tab->webView(), &TabbedWebView::wantsCloseTab, this, &TabWidget::closeTab);
0695     disconnect(tab->webView(), &QWebEngineView::urlChanged, this, &TabWidget::changed);
0696     disconnect(tab->webView(), &TabbedWebView::ipChanged, m_window->ipLabel(), &QLabel::setText);
0697 
0698     const int index = tab->tabIndex();
0699 
0700     tab->detach();
0701     tab->setPinned(false);
0702 
0703     Q_EMIT tabRemoved(index);
0704 
0705     if (count() == 0) {
0706         m_window->close();
0707     }
0708 }
0709 
0710 void TabWidget::detachTab(int index)
0711 {
0712     WebTab* tab = weTab(index);
0713     Q_ASSERT(tab);
0714 
0715     if (count() == 1 && mApp->windowCount() == 1) {
0716         return;
0717     }
0718 
0719     detachTab(tab);
0720 
0721     BrowserWindow* window = mApp->createWindow(Qz::BW_NewWindow);
0722     window->setStartTab(tab);
0723 }
0724 
0725 int TabWidget::duplicateTab(int index)
0726 {
0727     if (!validIndex(index)) {
0728         return -1;
0729     }
0730 
0731     WebTab* webTab = weTab(index);
0732 
0733     int id = addView(QUrl(), webTab->title(), Qz::NT_CleanSelectedTab);
0734     weTab(id)->p_restoreTab(webTab->url(), webTab->historyData(), webTab->zoomLevel());
0735     weTab(id)->setParentTab(webTab);
0736 
0737     return id;
0738 }
0739 
0740 void TabWidget::loadTab(int index)
0741 {
0742     if (!validIndex(index)) {
0743         return;
0744     }
0745 
0746     weTab(index)->tabActivated();
0747 }
0748 
0749 void TabWidget::unloadTab(int index)
0750 {
0751     if (!validIndex(index)) {
0752         return;
0753     }
0754 
0755     weTab(index)->unload();
0756 }
0757 
0758 void TabWidget::restoreClosedTab(QObject* obj)
0759 {
0760     if (!obj) {
0761         obj = sender();
0762     }
0763 
0764     if (!m_closedTabsManager->isClosedTabAvailable()) {
0765         return;
0766     }
0767 
0768     ClosedTabsManager::Tab tab;
0769 
0770     auto* action = qobject_cast<QAction*>(obj);
0771     if (action && action->data().toInt() != 0) {
0772         tab = m_closedTabsManager->takeTabAt(action->data().toInt());
0773     }
0774     else {
0775         tab = m_closedTabsManager->takeLastClosedTab();
0776     }
0777 
0778     if (tab.position < 0) {
0779         return;
0780     }
0781 
0782     int index = addView(QUrl(), tab.tabState.title, Qz::NT_CleanSelectedTab, false, tab.position);
0783     WebTab* webTab = weTab(index);
0784     webTab->setParentTab(tab.parentTab);
0785     webTab->p_restoreTab(tab.tabState);
0786 
0787     updateClosedTabsButton();
0788 }
0789 
0790 void TabWidget::restoreAllClosedTabs()
0791 {
0792     if (!m_closedTabsManager->isClosedTabAvailable()) {
0793         return;
0794     }
0795 
0796     const auto closedTabs = m_closedTabsManager->closedTabs();
0797     for (const ClosedTabsManager::Tab &tab : closedTabs) {
0798         int index = addView(QUrl(), tab.tabState.title, Qz::NT_CleanSelectedTab);
0799         WebTab* webTab = weTab(index);
0800         webTab->setParentTab(tab.parentTab);
0801         webTab->p_restoreTab(tab.tabState);
0802     }
0803 
0804     clearClosedTabsList();
0805 }
0806 
0807 void TabWidget::clearClosedTabsList()
0808 {
0809     m_closedTabsManager->clearClosedTabs();
0810     updateClosedTabsButton();
0811 }
0812 
0813 bool TabWidget::canRestoreTab() const
0814 {
0815     return m_closedTabsManager->isClosedTabAvailable();
0816 }
0817 
0818 QStackedWidget* TabWidget::locationBars() const
0819 {
0820     return m_locationBars;
0821 }
0822 
0823 ToolButton* TabWidget::buttonClosedTabs() const
0824 {
0825     return m_buttonClosedTabs;
0826 }
0827 
0828 AddTabButton* TabWidget::buttonAddTab() const
0829 {
0830     return m_buttonAddTab;
0831 }
0832 
0833 QList<WebTab*> TabWidget::allTabs(bool withPinned)
0834 {
0835     QList<WebTab*> allTabs;
0836 
0837     for (int i = 0; i < count(); i++) {
0838         WebTab* tab = weTab(i);
0839         if (!tab || (!withPinned && tab->isPinned())) {
0840             continue;
0841         }
0842         allTabs.append(tab);
0843     }
0844 
0845     return allTabs;
0846 }
0847 
0848 bool TabWidget::restoreState(const QVector<WebTab::SavedTab> &tabs, int currentTab)
0849 {
0850     if (tabs.isEmpty()) {
0851         return false;
0852     }
0853 
0854     m_tabBar->setIsRestoring(true);
0855 
0856     QVector<QPair<WebTab*, QVector<int>>> childTabs;
0857 
0858     for (int i = 0; i < tabs.size(); ++i) {
0859         WebTab::SavedTab tab = tabs.at(i);
0860         WebTab *webTab = weTab(addView(QUrl(), Qz::NT_CleanSelectedTab, false, tab.isPinned));
0861         webTab->restoreTab(tab);
0862         if (!tab.childTabs.isEmpty()) {
0863             childTabs.append({webTab, tab.childTabs});
0864         }
0865     }
0866 
0867     for (const auto &p : std::as_const(childTabs)) {
0868         const auto indices = p.second;
0869         for (int index : indices) {
0870             WebTab *t = weTab(index);
0871             if (t) {
0872                 p.first->addChildTab(t);
0873             }
0874         }
0875     }
0876 
0877     m_tabBar->setIsRestoring(false);
0878 
0879     auto const l_allTabs = allTabs();
0880     for (const WebTab* tab : l_allTabs) {
0881         m_tabBar->setTabText(tab->tabIndex(), tab->title());
0882     }
0883 
0884     setCurrentIndex(currentTab);
0885     currentTabChanged(currentTab);
0886     QTimer::singleShot(0, m_tabBar, SLOT(ensureVisible(int,int)));
0887 
0888     weTab()->tabActivated();
0889 
0890     return true;
0891 }
0892 
0893 TabWidget::~TabWidget()
0894 {
0895     delete m_closedTabsManager;
0896 }