diff --git a/src/lib/navigation/navigationbar.cpp b/src/lib/navigation/navigationbar.cpp index b5a8c057..8ffccac8 100644 --- a/src/lib/navigation/navigationbar.cpp +++ b/src/lib/navigation/navigationbar.cpp @@ -1,680 +1,682 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "navigationbar.h" #include "toolbutton.h" #include "browserwindow.h" #include "mainapplication.h" #include "iconprovider.h" #include "websearchbar.h" #include "reloadstopbutton.h" #include "enhancedmenu.h" #include "tabwidget.h" #include "tabbedwebview.h" #include "webpage.h" #include "qzsettings.h" #include "qztools.h" #include "abstractbuttoninterface.h" #include "navigationbartoolbutton.h" #include "navigationbarconfigdialog.h" #include #include #include #include #include #include #include static QString titleForUrl(QString title, const QUrl &url) { if (title.isEmpty()) { title = url.toString(QUrl::RemoveFragment); } if (title.isEmpty()) { return NavigationBar::tr("Empty Page"); } return QzTools::truncatedText(title, 40); } static QIcon iconForPage(const QUrl &url, const QIcon &sIcon) { QIcon icon; icon.addPixmap(IconProvider::iconForUrl(url).pixmap(16)); icon.addPixmap(sIcon.pixmap(16), QIcon::Active); return icon; } NavigationBar::NavigationBar(BrowserWindow* window) : QWidget(window) , m_window(window) { setObjectName(QSL("navigationbar")); m_layout = new QHBoxLayout(this); m_layout->setMargin(style()->pixelMetric(QStyle::PM_ToolBarItemMargin, 0, this) + style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, 0, this)); m_layout->setSpacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing, 0, this)); setLayout(m_layout); m_buttonBack = new ToolButton(this); m_buttonBack->setObjectName("navigation-button-back"); m_buttonBack->setToolTip(tr("Back")); m_buttonBack->setToolButtonStyle(Qt::ToolButtonIconOnly); m_buttonBack->setToolbarButtonLook(true); m_buttonBack->setShowMenuOnRightClick(true); m_buttonBack->setAutoRaise(true); m_buttonBack->setEnabled(false); m_buttonBack->setFocusPolicy(Qt::NoFocus); m_buttonForward = new ToolButton(this); m_buttonForward->setObjectName("navigation-button-next"); m_buttonForward->setToolTip(tr("Forward")); m_buttonForward->setToolButtonStyle(Qt::ToolButtonIconOnly); m_buttonForward->setToolbarButtonLook(true); m_buttonForward->setShowMenuOnRightClick(true); m_buttonForward->setAutoRaise(true); m_buttonForward->setEnabled(false); m_buttonForward->setFocusPolicy(Qt::NoFocus); QHBoxLayout* backNextLayout = new QHBoxLayout(); backNextLayout->setContentsMargins(0, 0, 0, 0); backNextLayout->setSpacing(0); backNextLayout->addWidget(m_buttonBack); backNextLayout->addWidget(m_buttonForward); QWidget *backNextWidget = new QWidget(this); backNextWidget->setLayout(backNextLayout); m_reloadStop = new ReloadStopButton(this); ToolButton *buttonHome = new ToolButton(this); buttonHome->setObjectName("navigation-button-home"); buttonHome->setToolTip(tr("Home")); buttonHome->setToolButtonStyle(Qt::ToolButtonIconOnly); buttonHome->setToolbarButtonLook(true); buttonHome->setAutoRaise(true); buttonHome->setFocusPolicy(Qt::NoFocus); ToolButton *buttonAddTab = new ToolButton(this); buttonAddTab->setObjectName("navigation-button-addtab"); buttonAddTab->setToolTip(tr("New Tab")); buttonAddTab->setToolButtonStyle(Qt::ToolButtonIconOnly); buttonAddTab->setToolbarButtonLook(true); buttonAddTab->setAutoRaise(true); buttonAddTab->setFocusPolicy(Qt::NoFocus); m_menuBack = new Menu(this); m_menuBack->setCloseOnMiddleClick(true); m_buttonBack->setMenu(m_menuBack); connect(m_buttonBack, SIGNAL(aboutToShowMenu()), this, SLOT(aboutToShowHistoryBackMenu())); m_menuForward = new Menu(this); m_menuForward->setCloseOnMiddleClick(true); m_buttonForward->setMenu(m_menuForward); connect(m_buttonForward, SIGNAL(aboutToShowMenu()), this, SLOT(aboutToShowHistoryNextMenu())); ToolButton *buttonTools = new ToolButton(this); buttonTools->setObjectName("navigation-button-tools"); buttonTools->setPopupMode(QToolButton::InstantPopup); buttonTools->setToolbarButtonLook(true); buttonTools->setToolTip(tr("Tools")); buttonTools->setAutoRaise(true); buttonTools->setFocusPolicy(Qt::NoFocus); buttonTools->setShowMenuInside(true); m_menuTools = new Menu(this); buttonTools->setMenu(m_menuTools); connect(buttonTools, &ToolButton::aboutToShowMenu, this, &NavigationBar::aboutToShowToolsMenu); m_supMenu = new ToolButton(this); m_supMenu->setObjectName("navigation-button-supermenu"); m_supMenu->setPopupMode(QToolButton::InstantPopup); m_supMenu->setToolbarButtonLook(true); m_supMenu->setToolTip(tr("Main Menu")); m_supMenu->setAutoRaise(true); m_supMenu->setFocusPolicy(Qt::NoFocus); m_supMenu->setMenu(m_window->superMenu()); m_supMenu->setShowMenuInside(true); m_searchLine = new WebSearchBar(m_window); m_navigationSplitter = new QSplitter(this); m_navigationSplitter->addWidget(m_window->tabWidget()->locationBars()); m_navigationSplitter->addWidget(m_searchLine); m_navigationSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); m_navigationSplitter->setCollapsible(0, false); m_exitFullscreen = new ToolButton(this); m_exitFullscreen->setObjectName("navigation-button-exitfullscreen"); m_exitFullscreen->setToolTip(tr("Exit Fullscreen")); m_exitFullscreen->setToolButtonStyle(Qt::ToolButtonIconOnly); m_exitFullscreen->setToolbarButtonLook(true); m_exitFullscreen->setFocusPolicy(Qt::NoFocus); m_exitFullscreen->setAutoRaise(true); m_exitFullscreen->setVisible(false); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); connect(m_buttonBack, SIGNAL(clicked()), this, SLOT(goBack())); connect(m_buttonBack, SIGNAL(middleMouseClicked()), this, SLOT(goBackInNewTab())); connect(m_buttonBack, SIGNAL(controlClicked()), this, SLOT(goBackInNewTab())); connect(m_buttonForward, SIGNAL(clicked()), this, SLOT(goForward())); connect(m_buttonForward, SIGNAL(middleMouseClicked()), this, SLOT(goForwardInNewTab())); connect(m_buttonForward, SIGNAL(controlClicked()), this, SLOT(goForwardInNewTab())); connect(m_reloadStop, SIGNAL(stopClicked()), this, SLOT(stop())); connect(m_reloadStop, SIGNAL(reloadClicked()), this, SLOT(reload())); connect(buttonHome, SIGNAL(clicked()), m_window, SLOT(goHome())); connect(buttonHome, SIGNAL(middleMouseClicked()), m_window, SLOT(goHomeInNewTab())); connect(buttonHome, SIGNAL(controlClicked()), m_window, SLOT(goHomeInNewTab())); connect(buttonAddTab, SIGNAL(clicked()), m_window, SLOT(addTab())); connect(buttonAddTab, SIGNAL(middleMouseClicked()), m_window->tabWidget(), SLOT(addTabFromClipboard())); connect(m_exitFullscreen, SIGNAL(clicked(bool)), m_window, SLOT(toggleFullScreen())); addWidget(backNextWidget, QSL("button-backforward"), tr("Back and Forward buttons")); addWidget(m_reloadStop, QSL("button-reloadstop"), tr("Reload button")); addWidget(buttonHome, QSL("button-home"), tr("Home button")); addWidget(buttonAddTab, QSL("button-addtab"), tr("Add tab button")); addWidget(m_navigationSplitter, QSL("locationbar"), tr("Address and Search bar")); addWidget(buttonTools, QSL("button-tools"), tr("Tools button")); addWidget(m_exitFullscreen, QSL("button-exitfullscreen"), tr("Exit Fullscreen button")); loadSettings(); } NavigationBar::~NavigationBar() { setCurrentView(nullptr); } void NavigationBar::setSplitterSizes(int locationBar, int websearchBar) { QList sizes; if (locationBar == 0) { int splitterWidth = m_navigationSplitter->width(); sizes << (int)((double)splitterWidth * .80) << (int)((double)splitterWidth * .20); } else { sizes << locationBar << websearchBar; } m_navigationSplitter->setSizes(sizes); } void NavigationBar::setCurrentView(TabbedWebView *view) { for (const WidgetData &data : qAsConst(m_widgets)) { if (data.button) { data.button->setWebView(view); } } if (!view) { return; } auto updateButton = [](ToolButton *button, QAction *action) { button->setEnabled(action->isEnabled()); }; auto updateBackButton = std::bind(updateButton, m_buttonBack, view->pageAction(QWebEnginePage::Back)); auto updateForwardButton = std::bind(updateButton, m_buttonForward, view->pageAction(QWebEnginePage::Forward)); updateBackButton(); updateForwardButton(); disconnect(m_backConnection); disconnect(m_forwardConnection); m_backConnection = connect(view->pageAction(QWebEnginePage::Back), &QAction::changed, this, updateBackButton); m_forwardConnection = connect(view->pageAction(QWebEnginePage::Forward), &QAction::changed, this, updateForwardButton); } void NavigationBar::showReloadButton() { m_reloadStop->showReloadButton(); } void NavigationBar::showStopButton() { m_reloadStop->showStopButton(); } void NavigationBar::enterFullScreen() { if (m_layout->indexOf(m_exitFullscreen) != -1) { m_exitFullscreen->show(); } } void NavigationBar::leaveFullScreen() { if (m_layout->indexOf(m_exitFullscreen) != -1) { m_exitFullscreen->hide(); } } void NavigationBar::setSuperMenuVisible(bool visible) { m_supMenu->setVisible(visible); } int NavigationBar::layoutMargin() const { return m_layout->margin(); } void NavigationBar::setLayoutMargin(int margin) { m_layout->setMargin(margin); } int NavigationBar::layoutSpacing() const { return m_layout->spacing(); } void NavigationBar::setLayoutSpacing(int spacing) { m_layout->setSpacing(spacing); } void NavigationBar::addWidget(QWidget *widget, const QString &id, const QString &name) { if (!widget || id.isEmpty() || name.isEmpty()) { return; } WidgetData data; data.id = id; data.name = name; data.widget = widget; m_widgets[id] = data; reloadLayout(); } void NavigationBar::removeWidget(const QString &id) { if (!m_widgets.contains(id)) { return; } m_widgets.remove(id); reloadLayout(); } void NavigationBar::addToolButton(AbstractButtonInterface *button) { if (!button || !button->isValid()) { return; } NavigationBarToolButton *toolButton = new NavigationBarToolButton(button, this); toolButton->setProperty("button-id", button->id()); connect(toolButton, &NavigationBarToolButton::visibilityChangeRequested, this, [=]() { if (m_layout->indexOf(toolButton) != -1) { toolButton->updateVisibility(); } }); WidgetData data; data.id = button->id(); data.name = button->name(); data.widget = toolButton; data.button = button; m_widgets[data.id] = data; data.button->setWebView(m_window->weView()); reloadLayout(); } void NavigationBar::removeToolButton(AbstractButtonInterface *button) { if (!button || !m_widgets.contains(button->id())) { return; } delete m_widgets.take(button->id()).widget; } void NavigationBar::aboutToShowHistoryBackMenu() { if (!m_menuBack || !m_window->weView()) { return; } m_menuBack->clear(); QWebEngineHistory* history = m_window->weView()->history(); int curindex = history->currentItemIndex(); int count = 0; for (int i = curindex - 1; i >= 0; i--) { QWebEngineHistoryItem item = history->itemAt(i); if (item.isValid()) { QString title = titleForUrl(item.title(), item.url()); const QIcon icon = iconForPage(item.url(), IconProvider::standardIcon(QStyle::SP_ArrowBack)); Action* act = new Action(icon, title); act->setData(i); connect(act, SIGNAL(triggered()), this, SLOT(loadHistoryIndex())); connect(act, SIGNAL(ctrlTriggered()), this, SLOT(loadHistoryIndexInNewTab())); m_menuBack->addAction(act); } count++; if (count == 20) { break; } } m_menuBack->addSeparator(); m_menuBack->addAction(QIcon::fromTheme(QSL("edit-clear")), tr("Clear history"), this, SLOT(clearHistory())); } void NavigationBar::aboutToShowHistoryNextMenu() { if (!m_menuForward || !m_window->weView()) { return; } m_menuForward->clear(); QWebEngineHistory* history = m_window->weView()->history(); int curindex = history->currentItemIndex(); int count = 0; for (int i = curindex + 1; i < history->count(); i++) { QWebEngineHistoryItem item = history->itemAt(i); if (item.isValid()) { QString title = titleForUrl(item.title(), item.url()); const QIcon icon = iconForPage(item.url(), IconProvider::standardIcon(QStyle::SP_ArrowForward)); Action* act = new Action(icon, title); act->setData(i); connect(act, SIGNAL(triggered()), this, SLOT(loadHistoryIndex())); connect(act, SIGNAL(ctrlTriggered()), this, SLOT(loadHistoryIndexInNewTab())); m_menuForward->addAction(act); } count++; if (count == 20) { break; } } m_menuForward->addSeparator(); m_menuForward->addAction(QIcon::fromTheme(QSL("edit-clear")), tr("Clear history"), this, SLOT(clearHistory())); } void NavigationBar::aboutToShowToolsMenu() { m_menuTools->clear(); m_window->createToolbarsMenu(m_menuTools->addMenu(tr("Toolbars"))); m_window->createSidebarsMenu(m_menuTools->addMenu(tr("Sidebar"))); m_menuTools->addSeparator(); for (const WidgetData &data : qAsConst(m_widgets)) { AbstractButtonInterface *button = data.button; if (button && (!button->isVisible() || !m_layoutIds.contains(data.id))) { QString title = button->title(); if (!button->badgeText().isEmpty()) { title.append(QSL(" (%1)").arg(button->badgeText())); } m_menuTools->addAction(button->icon(), title, this, &NavigationBar::toolActionActivated)->setData(data.id); } } m_menuTools->addSeparator(); m_menuTools->addAction(IconProvider::settingsIcon(), tr("Configure Toolbar"), this, SLOT(openConfigurationDialog())); } void NavigationBar::clearHistory() { QWebEngineHistory* history = m_window->weView()->page()->history(); history->clear(); } void NavigationBar::contextMenuRequested(const QPoint &pos) { QMenu menu; m_window->createToolbarsMenu(&menu); menu.addSeparator(); menu.addAction(IconProvider::settingsIcon(), tr("Configure Toolbar"), this, SLOT(openConfigurationDialog())); menu.exec(mapToGlobal(pos)); } void NavigationBar::openConfigurationDialog() { NavigationBarConfigDialog *dialog = new NavigationBarConfigDialog(this); dialog->show(); } void NavigationBar::toolActionActivated() { QAction *act = qobject_cast(sender()); if (!act) { return; } const QString id = act->data().toString(); if (!m_widgets.contains(id)) { return; } WidgetData data = m_widgets.value(id); if (!data.button) { return; } ToolButton *buttonTools = qobject_cast(m_widgets.value(QSL("button-tools")).widget); if (!buttonTools) { return; } AbstractButtonInterface::ClickController *c = new AbstractButtonInterface::ClickController; c->visualParent = buttonTools; c->popupPosition = [=](const QSize &size) { QPoint pos = buttonTools->mapToGlobal(buttonTools->rect().bottomRight()); if (QApplication::isRightToLeft()) { pos.setX(pos.x() - buttonTools->rect().width()); } else { pos.setX(pos.x() - size.width()); } c->popupOpened = true; return pos; }; c->popupClosed = [=]() { buttonTools->setDown(false); delete c; }; emit data.button->clicked(c); if (c->popupOpened) { buttonTools->setDown(true); } else { c->popupClosed(); } } void NavigationBar::loadSettings() { const QStringList defaultIds = { QSL("button-backforward"), QSL("button-reloadstop"), QSL("button-home"), QSL("locationbar"), QSL("button-downloads"), QSL("adblock-icon"), QSL("button-tools") }; Settings settings; settings.beginGroup(QSL("NavigationBar")); m_layoutIds = settings.value(QSL("Layout"), defaultIds).toStringList(); m_searchLine->setVisible(settings.value(QSL("ShowSearchBar"), true).toBool()); settings.endGroup(); m_layoutIds.removeDuplicates(); m_layoutIds.removeAll(QString()); if (!m_layoutIds.contains(QSL("locationbar"))) { m_layoutIds.append(QSL("locationbar")); } reloadLayout(); } void NavigationBar::reloadLayout() { if (m_widgets.isEmpty()) { return; } setUpdatesEnabled(false); // Clear layout while (m_layout->count() != 0) { QLayoutItem *item = m_layout->takeAt(0); if (!item) { continue; } QWidget *widget = item->widget(); if (!widget) { continue; } widget->setParent(nullptr); } // Hide all widgets for (const WidgetData &data : m_widgets) { data.widget->hide(); } // Add widgets to layout for (const QString &id : qAsConst(m_layoutIds)) { const WidgetData data = m_widgets.value(id); if (data.widget) { m_layout->addWidget(data.widget); NavigationBarToolButton *button = qobject_cast(data.widget); if (button) { button->updateVisibility(); } else { data.widget->show(); } } } m_layout->addWidget(m_supMenu); // Make sure search bar is visible if (m_searchLine->isVisible() && m_navigationSplitter->sizes().at(1) == 0) { const int locationBarSize = m_navigationSplitter->sizes().at(0); setSplitterSizes(locationBarSize - 50, 50); } if (m_window->isFullScreen()) { enterFullScreen(); } else { leaveFullScreen(); } setUpdatesEnabled(true); } void NavigationBar::loadHistoryIndex() { QWebEngineHistory* history = m_window->weView()->page()->history(); if (QAction* action = qobject_cast(sender())) { loadHistoryItem(history->itemAt(action->data().toInt())); } } void NavigationBar::loadHistoryIndexInNewTab(int index) { if (QAction* action = qobject_cast(sender())) { index = action->data().toInt(); } if (index == -1) { return; } QWebEngineHistory* history = m_window->weView()->page()->history(); loadHistoryItemInNewTab(history->itemAt(index)); } void NavigationBar::stop() { m_window->action(QSL("View/Stop"))->trigger(); } void NavigationBar::reload() { m_window->action(QSL("View/Reload"))->trigger(); } void NavigationBar::goBack() { - QWebEngineHistory* history = m_window->weView()->page()->history(); - history->back(); + auto view = m_window->weView(); + view->setFocus(); + view->back(); } void NavigationBar::goBackInNewTab() { QWebEngineHistory* history = m_window->weView()->page()->history(); if (!history->canGoBack()) { return; } loadHistoryItemInNewTab(history->backItem()); } void NavigationBar::goForward() { - QWebEngineHistory* history = m_window->weView()->page()->history(); - history->forward(); + auto view = m_window->weView(); + view->setFocus(); + view->forward(); } void NavigationBar::goForwardInNewTab() { QWebEngineHistory* history = m_window->weView()->page()->history(); if (!history->canGoForward()) { return; } loadHistoryItemInNewTab(history->forwardItem()); } void NavigationBar::loadHistoryItem(const QWebEngineHistoryItem &item) { m_window->weView()->page()->history()->goToItem(item); } void NavigationBar::loadHistoryItemInNewTab(const QWebEngineHistoryItem &item) { TabWidget* tabWidget = m_window->tabWidget(); int tabIndex = tabWidget->duplicateTab(tabWidget->currentIndex()); QWebEngineHistory* history = m_window->weView(tabIndex)->page()->history(); history->goToItem(item); if (qzSettings->newTabPosition == Qz::NT_SelectedTab) { tabWidget->setCurrentIndex(tabIndex); } } diff --git a/src/lib/tabwidget/tabwidget.cpp b/src/lib/tabwidget/tabwidget.cpp index 53ee98f6..4561b186 100644 --- a/src/lib/tabwidget/tabwidget.cpp +++ b/src/lib/tabwidget/tabwidget.cpp @@ -1,879 +1,881 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "tabwidget.h" #include "tabbar.h" #include "tabbedwebview.h" #include "webpage.h" #include "browserwindow.h" #include "mainapplication.h" #include "webtab.h" #include "clickablelabel.h" #include "closedtabsmanager.h" #include "locationbar.h" #include "settings.h" #include "datapaths.h" #include "qzsettings.h" #include "qztools.h" #include "tabicon.h" #include "pluginproxy.h" #include #include #include #include #include #include #include AddTabButton::AddTabButton(TabWidget* tabWidget, TabBar* tabBar) : ToolButton(tabBar) , m_tabBar(tabBar) , m_tabWidget(tabWidget) { setObjectName("tabwidget-button-addtab"); setAutoRaise(true); setFocusPolicy(Qt::NoFocus); setAcceptDrops(true); setToolTip(TabWidget::tr("New Tab")); } void AddTabButton::wheelEvent(QWheelEvent* event) { m_tabBar->wheelEvent(event); } void AddTabButton::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::MiddleButton && rect().contains(event->pos())) { m_tabWidget->addTabFromClipboard(); } ToolButton::mouseReleaseEvent(event); } void MenuTabs::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::MiddleButton) { QAction* action = actionAt(event->pos()); if (action && action->isEnabled()) { WebTab* tab = qobject_cast(qvariant_cast(action->data())); if (tab) { emit closeTab(tab->tabIndex()); action->setEnabled(false); event->accept(); } } } QMenu::mouseReleaseEvent(event); } TabWidget::TabWidget(BrowserWindow *window, QWidget *parent) : TabStackedWidget(parent) , m_window(window) , m_locationBars(new QStackedWidget) , m_closedTabsManager(new ClosedTabsManager) { setObjectName(QSL("tabwidget")); m_tabBar = new TabBar(m_window, this); setTabBar(m_tabBar); connect(this, &TabWidget::changed, mApp, &MainApplication::changeOccurred); connect(this, &TabStackedWidget::pinStateChanged, this, &TabWidget::changed); connect(m_tabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(requestCloseTab(int))); connect(m_tabBar, &TabBar::tabMoved, this, &TabWidget::tabWasMoved); connect(m_tabBar, SIGNAL(moveAddTabButton(int)), this, SLOT(moveAddTabButton(int))); connect(mApp, SIGNAL(settingsReloaded()), this, SLOT(loadSettings())); m_menuTabs = new MenuTabs(this); connect(m_menuTabs, SIGNAL(closeTab(int)), this, SLOT(requestCloseTab(int))); m_menuClosedTabs = new QMenu(this); // AddTab button displayed next to last tab m_buttonAddTab = new AddTabButton(this, m_tabBar); m_buttonAddTab->setProperty("outside-tabbar", false); connect(m_buttonAddTab, SIGNAL(clicked()), m_window, SLOT(addTab())); // AddTab button displayed outside tabbar (as corner widget) m_buttonAddTab2 = new AddTabButton(this, m_tabBar); m_buttonAddTab2->setProperty("outside-tabbar", true); m_buttonAddTab2->hide(); connect(m_buttonAddTab2, SIGNAL(clicked()), m_window, SLOT(addTab())); // ClosedTabs button displayed as a permanent corner widget m_buttonClosedTabs = new ToolButton(m_tabBar); m_buttonClosedTabs->setObjectName("tabwidget-button-closedtabs"); m_buttonClosedTabs->setMenu(m_menuClosedTabs); m_buttonClosedTabs->setPopupMode(QToolButton::InstantPopup); m_buttonClosedTabs->setToolTip(tr("Closed tabs")); m_buttonClosedTabs->setAutoRaise(true); m_buttonClosedTabs->setFocusPolicy(Qt::NoFocus); m_buttonClosedTabs->setShowMenuInside(true); connect(m_buttonClosedTabs, SIGNAL(aboutToShowMenu()), this, SLOT(aboutToShowClosedTabsMenu())); // ListTabs button is showed only when tabbar overflows m_buttonListTabs = new ToolButton(m_tabBar); m_buttonListTabs->setObjectName("tabwidget-button-opentabs"); m_buttonListTabs->setMenu(m_menuTabs); m_buttonListTabs->setPopupMode(QToolButton::InstantPopup); m_buttonListTabs->setToolTip(tr("List of tabs")); m_buttonListTabs->setAutoRaise(true); m_buttonListTabs->setFocusPolicy(Qt::NoFocus); m_buttonListTabs->setShowMenuInside(true); m_buttonListTabs->hide(); connect(m_buttonListTabs, SIGNAL(aboutToShowMenu()), this, SLOT(aboutToShowTabsMenu())); m_tabBar->addCornerWidget(m_buttonAddTab2, Qt::TopRightCorner); m_tabBar->addCornerWidget(m_buttonClosedTabs, Qt::TopRightCorner); m_tabBar->addCornerWidget(m_buttonListTabs, Qt::TopRightCorner); connect(m_tabBar, SIGNAL(overFlowChanged(bool)), this, SLOT(tabBarOverFlowChanged(bool))); loadSettings(); } BrowserWindow *TabWidget::browserWindow() const { return m_window; } void TabWidget::loadSettings() { Settings settings; settings.beginGroup("Browser-Tabs-Settings"); m_dontCloseWithOneTab = settings.value("dontCloseWithOneTab", false).toBool(); m_showClosedTabsButton = settings.value("showClosedTabsButton", false).toBool(); m_newTabAfterActive = settings.value("newTabAfterActive", true).toBool(); m_newEmptyTabAfterActive = settings.value("newEmptyTabAfterActive", false).toBool(); settings.endGroup(); settings.beginGroup("Web-URL-Settings"); m_urlOnNewTab = settings.value("newTabUrl", "falkon:speeddial").toUrl(); settings.endGroup(); m_tabBar->loadSettings(); updateClosedTabsButton(); } WebTab* TabWidget::weTab() const { return weTab(currentIndex()); } WebTab* TabWidget::weTab(int index) const { return qobject_cast(widget(index)); } TabIcon* TabWidget::tabIcon(int index) const { return weTab(index)->tabIcon(); } bool TabWidget::validIndex(int index) const { return index >= 0 && index < count(); } void TabWidget::updateClosedTabsButton() { m_buttonClosedTabs->setVisible(m_showClosedTabsButton && canRestoreTab()); } void TabWidget::keyPressEvent(QKeyEvent *event) { if (mApp->plugins()->processKeyPress(Qz::ON_TabWidget, this, event)) { return; } TabStackedWidget::keyPressEvent(event); } void TabWidget::keyReleaseEvent(QKeyEvent *event) { if (mApp->plugins()->processKeyRelease(Qz::ON_TabWidget, this, event)) { return; } TabStackedWidget::keyReleaseEvent(event); } bool TabWidget::isCurrentTabFresh() const { return m_currentTabFresh; } void TabWidget::setCurrentTabFresh(bool currentTabFresh) { m_currentTabFresh = currentTabFresh; } void TabWidget::tabBarOverFlowChanged(bool overflowed) { // Show buttons inside tabbar m_buttonAddTab->setVisible(!overflowed); // Show buttons displayed outside tabbar (corner widgets) m_buttonAddTab2->setVisible(overflowed); m_buttonListTabs->setVisible(overflowed); } void TabWidget::moveAddTabButton(int posX) { int posY = (m_tabBar->height() - m_buttonAddTab->height()) / 2; if (QApplication::layoutDirection() == Qt::RightToLeft) { posX = qMax(posX - m_buttonAddTab->width(), 0); } else { posX = qMin(posX, m_tabBar->width() - m_buttonAddTab->width()); } m_buttonAddTab->move(posX, posY); } void TabWidget::aboutToShowTabsMenu() { m_menuTabs->clear(); for (int i = 0; i < count(); i++) { WebTab* tab = weTab(i); if (!tab || tab->isPinned()) { continue; } QAction* action = new QAction(this); action->setIcon(tab->icon()); if (i == currentIndex()) { QFont f = action->font(); f.setBold(true); action->setFont(f); } QString title = tab->title(); title.replace(QLatin1Char('&'), QLatin1String("&&")); action->setText(QzTools::truncatedText(title, 40)); action->setData(QVariant::fromValue(qobject_cast(tab))); connect(action, SIGNAL(triggered()), this, SLOT(actionChangeIndex())); m_menuTabs->addAction(action); } } void TabWidget::aboutToShowClosedTabsMenu() { m_menuClosedTabs->clear(); const auto closedTabs = closedTabsManager()->closedTabs(); for (int i = 0; i < closedTabs.count(); ++i) { const ClosedTabsManager::Tab tab = closedTabs.at(i); const QString title = QzTools::truncatedText(tab.tabState.title, 40); m_menuClosedTabs->addAction(tab.tabState.icon, title, this, SLOT(restoreClosedTab()))->setData(i); } if (m_menuClosedTabs->isEmpty()) { m_menuClosedTabs->addAction(tr("Empty"))->setEnabled(false); } else { m_menuClosedTabs->addSeparator(); m_menuClosedTabs->addAction(tr("Restore All Closed Tabs"), this, SLOT(restoreAllClosedTabs())); m_menuClosedTabs->addAction(QIcon::fromTheme(QSL("edit-clear")), tr("Clear list"), this, SLOT(clearClosedTabsList())); } } void TabWidget::actionChangeIndex() { if (QAction* action = qobject_cast(sender())) { WebTab* tab = qobject_cast(qvariant_cast(action->data())); if (tab) { m_tabBar->ensureVisible(tab->tabIndex()); setCurrentIndex(tab->tabIndex()); } } } int TabWidget::addView(const LoadRequest &req, const Qz::NewTabPositionFlags &openFlags, bool selectLine, bool pinned) { return addView(req, QString(), openFlags, selectLine, -1, pinned); } int TabWidget::addView(const LoadRequest &req, const QString &title, const Qz::NewTabPositionFlags &openFlags, bool selectLine, int position, bool pinned) { QUrl url = req.url(); m_currentTabFresh = false; if (url.isEmpty() && !(openFlags & Qz::NT_CleanTab)) { url = m_urlOnNewTab; } bool openAfterActive = m_newTabAfterActive && !(openFlags & Qz::NT_TabAtTheEnd); if (openFlags == Qz::NT_SelectedNewEmptyTab && m_newEmptyTabAfterActive) { openAfterActive = true; } if (openAfterActive && position == -1) { // If we are opening newBgTab from pinned tab, make sure it won't be // opened between other pinned tabs if (openFlags & Qz::NT_NotSelectedTab && m_lastBackgroundTab) { position = m_lastBackgroundTab->tabIndex() + 1; } else { position = qMax(currentIndex() + 1, m_tabBar->pinnedTabsCount()); } } WebTab* webTab = new WebTab(m_window); webTab->setPinned(pinned); webTab->locationBar()->showUrl(url); m_locationBars->addWidget(webTab->locationBar()); int index = insertTab(position == -1 ? count() : position, webTab, QString(), pinned); webTab->attach(m_window); if (!title.isEmpty()) { m_tabBar->setTabText(index, title); } if (openFlags & Qz::NT_SelectedTab) { setCurrentIndex(index); } else { m_lastBackgroundTab = webTab; } connect(webTab->webView(), SIGNAL(wantsCloseTab(int)), this, SLOT(closeTab(int))); connect(webTab->webView(), SIGNAL(urlChanged(QUrl)), this, SIGNAL(changed())); connect(webTab->webView(), SIGNAL(ipChanged(QString)), m_window->ipLabel(), SLOT(setText(QString))); connect(webTab->webView(), &WebView::urlChanged, this, [this](const QUrl &url) { if (url != m_urlOnNewTab) m_currentTabFresh = false; }); if (url.isValid() && url != req.url()) { LoadRequest r(req); r.setUrl(url); webTab->webView()->load(r); } else if (req.url().isValid()) { webTab->webView()->load(req); } if (selectLine && m_window->locationBar()->text().isEmpty()) { m_window->locationBar()->setFocus(); } // Make sure user notice opening new background tabs if (!(openFlags & Qz::NT_SelectedTab)) { m_tabBar->ensureVisible(index); } emit changed(); emit tabInserted(index); return index; } int TabWidget::addView(WebTab *tab, const Qz::NewTabPositionFlags &openFlags) { return insertView(count() + 1, tab, openFlags); } int TabWidget::insertView(int index, WebTab *tab, const Qz::NewTabPositionFlags &openFlags) { m_locationBars->addWidget(tab->locationBar()); int newIndex = insertTab(index, tab, QString(), tab->isPinned()); tab->attach(m_window); if (openFlags.testFlag(Qz::NT_SelectedTab)) { setCurrentIndex(newIndex); } else { m_lastBackgroundTab = tab; } connect(tab->webView(), SIGNAL(wantsCloseTab(int)), this, SLOT(closeTab(int))); connect(tab->webView(), SIGNAL(urlChanged(QUrl)), this, SIGNAL(changed())); connect(tab->webView(), SIGNAL(ipChanged(QString)), m_window->ipLabel(), SLOT(setText(QString))); // Make sure user notice opening new background tabs if (!(openFlags & Qz::NT_SelectedTab)) { m_tabBar->ensureVisible(index); } emit changed(); emit tabInserted(newIndex); return newIndex; } void TabWidget::addTabFromClipboard() { QString selectionClipboard = QApplication::clipboard()->text(QClipboard::Selection); QUrl guessedUrl = QUrl::fromUserInput(selectionClipboard); if (!guessedUrl.isEmpty()) { addView(guessedUrl, Qz::NT_SelectedNewEmptyTab); } } void TabWidget::closeTab(int index) { if (index == -1) index = currentIndex(); WebTab *webTab = weTab(index); if (!webTab || !validIndex(index)) return; // This is already handled in requestCloseTab if (count() <= 1) { requestCloseTab(index); return; } m_closedTabsManager->saveTab(webTab); TabbedWebView *webView = webTab->webView(); m_locationBars->removeWidget(webView->webTab()->locationBar()); disconnect(webView, SIGNAL(wantsCloseTab(int)), this, SLOT(closeTab(int))); disconnect(webView, SIGNAL(urlChanged(QUrl)), this, SIGNAL(changed())); disconnect(webView, SIGNAL(ipChanged(QString)), m_window->ipLabel(), SLOT(setText(QString))); m_lastBackgroundTab = nullptr; webTab->detach(); webTab->deleteLater(); updateClosedTabsButton(); emit changed(); emit tabRemoved(index); } void TabWidget::requestCloseTab(int index) { if (index == -1) index = currentIndex(); WebTab *webTab = weTab(index); if (!webTab || !validIndex(index)) return; TabbedWebView *webView = webTab->webView(); // This would close last tab, so we close the window instead if (count() <= 1) { // If we are not closing window upon closing last tab, let's just load new-tab-url if (m_dontCloseWithOneTab) { // We don't want to accumulate more than one closed tab, if user tries // to close the last tab multiple times if (webView->url() != m_urlOnNewTab) { m_closedTabsManager->saveTab(webTab); } webView->zoomReset(); webView->load(m_urlOnNewTab); return; } m_window->close(); return; } webView->triggerPageAction(QWebEnginePage::RequestClose); } void TabWidget::currentTabChanged(int index) { if (!validIndex(index)) return; m_lastBackgroundTab = nullptr; m_currentTabFresh = false; WebTab* webTab = weTab(index); webTab->tabActivated(); LocationBar* locBar = webTab->locationBar(); if (locBar && m_locationBars->indexOf(locBar) != -1) { m_locationBars->setCurrentWidget(locBar); } m_window->currentTabChanged(); emit changed(); } void TabWidget::tabWasMoved(int before, int after) { m_lastBackgroundTab = nullptr; emit changed(); if (!m_blockTabMovedSignal) { emit tabMoved(before, after); } } void TabWidget::setCurrentIndex(int index) { TabStackedWidget::setCurrentIndex(index); } void TabWidget::nextTab() { setCurrentIndex((currentIndex() + 1) % count()); } void TabWidget::previousTab() { setCurrentIndex(currentIndex() == 0 ? count() - 1 : currentIndex() - 1); } int TabWidget::normalTabsCount() const { return m_tabBar->normalTabsCount(); } int TabWidget::pinnedTabsCount() const { return m_tabBar->pinnedTabsCount(); } void TabWidget::reloadTab(int index) { if (!validIndex(index)) { return; } weTab(index)->reload(); } WebTab *TabWidget::webTab(int index) const { return index < 0 ? weTab() : weTab(index); } int TabWidget::extraReservedWidth() const { return m_buttonAddTab->width(); } TabBar* TabWidget::tabBar() const { return m_tabBar; } ClosedTabsManager* TabWidget::closedTabsManager() const { return m_closedTabsManager; } void TabWidget::reloadAllTabs() { for (int i = 0; i < count(); i++) { reloadTab(i); } } void TabWidget::stopTab(int index) { if (!validIndex(index)) { return; } weTab(index)->stop(); } void TabWidget::closeAllButCurrent(int index) { if (!validIndex(index)) { return; } WebTab* akt = weTab(index); foreach (WebTab* tab, allTabs(false)) { int tabIndex = tab->tabIndex(); if (akt == widget(tabIndex)) { continue; } requestCloseTab(tabIndex); } } void TabWidget::closeToRight(int index) { if (!validIndex(index)) { return; } foreach (WebTab* tab, allTabs(false)) { int tabIndex = tab->tabIndex(); if (index >= tabIndex) { continue; } requestCloseTab(tabIndex); } } void TabWidget::closeToLeft(int index) { if (!validIndex(index)) { return; } foreach (WebTab* tab, allTabs(false)) { int tabIndex = tab->tabIndex(); if (index <= tabIndex) { continue; } requestCloseTab(tabIndex); } } void TabWidget::moveTab(int from, int to) { if (!validIndex(to) || from == to) { return; } WebTab *tab = webTab(from); if (!tab) { return; } m_blockTabMovedSignal = true; // (Un)pin tab when needed if ((tab->isPinned() && to >= pinnedTabsCount()) || (!tab->isPinned() && to < pinnedTabsCount())) { tab->togglePinned(); } TabStackedWidget::moveTab(tab->tabIndex(), to); m_blockTabMovedSignal = false; emit tabMoved(from, to); } int TabWidget::pinUnPinTab(int index, const QString &title) { const int newIndex = TabStackedWidget::pinUnPinTab(index, title); if (index != newIndex && !m_blockTabMovedSignal) { emit tabMoved(index, newIndex); } return newIndex; } void TabWidget::detachTab(WebTab* tab) { Q_ASSERT(tab); if (count() == 1 && mApp->windowCount() == 1) { return; } m_locationBars->removeWidget(tab->locationBar()); disconnect(tab->webView(), SIGNAL(wantsCloseTab(int)), this, SLOT(closeTab(int))); disconnect(tab->webView(), SIGNAL(urlChanged(QUrl)), this, SIGNAL(changed())); disconnect(tab->webView(), SIGNAL(ipChanged(QString)), m_window->ipLabel(), SLOT(setText(QString))); const int index = tab->tabIndex(); tab->detach(); tab->setPinned(false); emit tabRemoved(index); if (count() == 0) { m_window->close(); } } void TabWidget::detachTab(int index) { WebTab* tab = weTab(index); Q_ASSERT(tab); if (count() == 1 && mApp->windowCount() == 1) { return; } detachTab(tab); BrowserWindow* window = mApp->createWindow(Qz::BW_NewWindow); window->setStartTab(tab); } int TabWidget::duplicateTab(int index) { if (!validIndex(index)) { return -1; } WebTab* webTab = weTab(index); int id = addView(QUrl(), webTab->title(), Qz::NT_CleanSelectedTab); weTab(id)->p_restoreTab(webTab->url(), webTab->historyData(), webTab->zoomLevel()); return id; } void TabWidget::loadTab(int index) { if (!validIndex(index)) { return; } weTab(index)->tabActivated(); } void TabWidget::unloadTab(int index) { if (!validIndex(index)) { return; } weTab(index)->unload(); } void TabWidget::restoreClosedTab(QObject* obj) { if (!obj) { obj = sender(); } if (!m_closedTabsManager->isClosedTabAvailable()) { return; } ClosedTabsManager::Tab tab; QAction* action = qobject_cast(obj); if (action && action->data().toInt() != 0) { tab = m_closedTabsManager->takeTabAt(action->data().toInt()); } else { tab = m_closedTabsManager->takeLastClosedTab(); } if (tab.position < 0) { return; } int index = addView(QUrl(), tab.tabState.title, Qz::NT_CleanSelectedTab, false, tab.position); WebTab* webTab = weTab(index); + webTab->setParentTab(tab.parentTab); webTab->p_restoreTab(tab.tabState); updateClosedTabsButton(); } void TabWidget::restoreAllClosedTabs() { if (!m_closedTabsManager->isClosedTabAvailable()) { return; } const auto closedTabs = m_closedTabsManager->closedTabs(); for (const ClosedTabsManager::Tab &tab : closedTabs) { int index = addView(QUrl(), tab.tabState.title, Qz::NT_CleanSelectedTab); WebTab* webTab = weTab(index); + webTab->setParentTab(tab.parentTab); webTab->p_restoreTab(tab.tabState); } clearClosedTabsList(); } void TabWidget::clearClosedTabsList() { m_closedTabsManager->clearClosedTabs(); updateClosedTabsButton(); } bool TabWidget::canRestoreTab() const { return m_closedTabsManager->isClosedTabAvailable(); } QStackedWidget* TabWidget::locationBars() const { return m_locationBars; } ToolButton* TabWidget::buttonClosedTabs() const { return m_buttonClosedTabs; } AddTabButton* TabWidget::buttonAddTab() const { return m_buttonAddTab; } QList TabWidget::allTabs(bool withPinned) { QList allTabs; for (int i = 0; i < count(); i++) { WebTab* tab = weTab(i); if (!tab || (!withPinned && tab->isPinned())) { continue; } allTabs.append(tab); } return allTabs; } bool TabWidget::restoreState(const QVector &tabs, int currentTab) { if (tabs.isEmpty()) { return false; } QVector>> childTabs; for (int i = 0; i < tabs.size(); ++i) { WebTab::SavedTab tab = tabs.at(i); WebTab *webTab = weTab(addView(QUrl(), Qz::NT_CleanSelectedTab, false, tab.isPinned)); webTab->restoreTab(tab); if (!tab.childTabs.isEmpty()) { childTabs.append({webTab, tab.childTabs}); } } for (const auto p : qAsConst(childTabs)) { const auto indices = p.second; for (int index : indices) { WebTab *t = weTab(index); if (t) { p.first->addChildTab(t); } } } setCurrentIndex(currentTab); QTimer::singleShot(0, m_tabBar, SLOT(ensureVisible(int,int))); weTab()->tabActivated(); return true; } TabWidget::~TabWidget() { delete m_closedTabsManager; } diff --git a/src/lib/tools/closedtabsmanager.cpp b/src/lib/tools/closedtabsmanager.cpp index 0bef7594..21f5a9f6 100644 --- a/src/lib/tools/closedtabsmanager.cpp +++ b/src/lib/tools/closedtabsmanager.cpp @@ -1,76 +1,77 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "closedtabsmanager.h" #include "mainapplication.h" #include "qztools.h" #include ClosedTabsManager::ClosedTabsManager() { } void ClosedTabsManager::saveTab(WebTab *tab) { if (mApp->isPrivate()) { return; } // Don't save empty tab if (tab->url().isEmpty() && tab->history()->items().count() == 0) { return; } Tab closedTab; closedTab.position = tab->tabIndex(); + closedTab.parentTab = tab->parentTab(); closedTab.tabState = WebTab::SavedTab(tab); m_closedTabs.prepend(closedTab); } bool ClosedTabsManager::isClosedTabAvailable() const { return !m_closedTabs.isEmpty(); } ClosedTabsManager::Tab ClosedTabsManager::takeLastClosedTab() { Tab tab; if (!m_closedTabs.isEmpty()) { tab = m_closedTabs.takeFirst(); } return tab; } ClosedTabsManager::Tab ClosedTabsManager::takeTabAt(int index) { Tab tab; if (QzTools::containsIndex(m_closedTabs, index)) { tab = m_closedTabs.takeAt(index); } return tab; } QVector ClosedTabsManager::closedTabs() const { return m_closedTabs; } void ClosedTabsManager::clearClosedTabs() { m_closedTabs.clear(); } diff --git a/src/lib/tools/closedtabsmanager.h b/src/lib/tools/closedtabsmanager.h index 3fbcb00d..34d88463 100644 --- a/src/lib/tools/closedtabsmanager.h +++ b/src/lib/tools/closedtabsmanager.h @@ -1,60 +1,62 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #ifndef CLOSEDTABSMANAGER_H #define CLOSEDTABSMANAGER_H #include +#include #include "webtab.h" #include "qzcommon.h" class WebTab; class FALKON_EXPORT ClosedTabsManager { public: struct Tab { int position = -1; + QPointer parentTab; WebTab::SavedTab tabState; bool isValid() const { return position > -1; } }; explicit ClosedTabsManager(); void saveTab(WebTab *tab); bool isClosedTabAvailable() const; // Takes tab that was most recently closed Tab takeLastClosedTab(); // Takes tab at given index Tab takeTabAt(int index); QVector closedTabs() const; void clearClosedTabs(); private: QVector m_closedTabs; }; // Hint to Qt to use std::realloc on item moving Q_DECLARE_TYPEINFO(ClosedTabsManager::Tab, Q_MOVABLE_TYPE); #endif // CLOSEDTABSMANAGER_H