diff --git a/src/articleviewerwidget.cpp b/src/articleviewerwidget.cpp index 6a85bba6..74b243a5 100644 --- a/src/articleviewerwidget.cpp +++ b/src/articleviewerwidget.cpp @@ -1,420 +1,413 @@ /* This file is part of Akregator. Copyright (C) 2004 Teemu Rytilahti 2005 Frank Osterfeld 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "articleviewerwidget.h" #include "akregatorconfig.h" #include "aboutdata.h" #include "actionmanager.h" #include "actions.h" #include "article.h" #include "articleformatter.h" #include "articlejobs.h" #include "articlematcher.h" #include "feed.h" #include "folder.h" #include "treenode.h" #include "utils.h" #include "openurlrequest.h" #include "akregator_debug.h" #include "akregator-version.h" #include -#include -#include -#include -#include -#include -#include #include #include #include #include #include #include #include #include -#include #include using namespace Akregator; using namespace Akregator::Filters; ArticleViewerWidget::ArticleViewerWidget(const QString &grantleeDirectory, KActionCollection *ac, QWidget *parent) : QWidget(parent) , m_node(nullptr) , m_viewMode(NormalView) , m_articleViewerWidgetNg(new Akregator::ArticleViewerWebEngineWidgetNg(ac, this)) , m_grantleeDirectory(grantleeDirectory) { QGridLayout *layout = new QGridLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_articleViewerWidgetNg); m_articleHtmlWriter = new Akregator::ArticleHtmlWebEngineWriter(m_articleViewerWidgetNg->articleViewerNg(), this); connect(m_articleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::signalOpenUrlRequest, this, &ArticleViewerWidget::signalOpenUrlRequest); connect(m_articleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::showStatusBarMessage, this, &ArticleViewerWidget::showStatusBarMessage); } ArticleViewerWidget::~ArticleViewerWidget() { } QSharedPointer ArticleViewerWidget::normalViewFormatter() { if (!m_normalViewFormatter.data()) { m_normalViewFormatter = QSharedPointer(new DefaultNormalViewFormatter(m_grantleeDirectory, m_articleViewerWidgetNg->articleViewerNg())); } return m_normalViewFormatter; } QSharedPointer ArticleViewerWidget::combinedViewFormatter() { if (!m_combinedViewFormatter.data()) { m_combinedViewFormatter = QSharedPointer(new DefaultCombinedViewFormatter(m_grantleeDirectory, m_articleViewerWidgetNg->articleViewerNg())); } return m_combinedViewFormatter; } void ArticleViewerWidget::slotZoomChangeInFrame(qreal value) { m_articleViewerWidgetNg->articleViewerNg()->setZoomFactor(value); } void ArticleViewerWidget::slotCopy() { m_articleViewerWidgetNg->articleViewerNg()->slotCopy(); } void ArticleViewerWidget::slotSelectionChanged() { ActionManager::getInstance()->action(QStringLiteral("viewer_copy"))->setEnabled(!m_articleViewerWidgetNg->articleViewerNg()->selectedText().isEmpty()); } void ArticleViewerWidget::slotPrint() { m_articleViewerWidgetNg->slotPrint(); } void ArticleViewerWidget::slotPrintPreview() { m_articleViewerWidgetNg->slotPrintPreview(); } void ArticleViewerWidget::connectToNode(TreeNode *node) { if (node) { if (m_viewMode == CombinedView) { connect(node, &TreeNode::signalChanged, this, &ArticleViewerWidget::slotUpdateCombinedView); connect(node, &TreeNode::signalArticlesAdded, this, &ArticleViewerWidget::slotArticlesAdded); connect(node, &TreeNode::signalArticlesRemoved, this, &ArticleViewerWidget::slotArticlesRemoved); connect(node, &TreeNode::signalArticlesUpdated, this, &ArticleViewerWidget::slotArticlesUpdated); } else if (m_viewMode == SummaryView) { connect(node, &TreeNode::signalChanged, this, &ArticleViewerWidget::slotShowSummary); } connect(node, &TreeNode::signalDestroyed, this, &ArticleViewerWidget::slotClear); } } void ArticleViewerWidget::disconnectFromNode(TreeNode *node) { if (node) { node->disconnect(this); } } void ArticleViewerWidget::renderContent(const QString &text) { m_currentText = text; reload(); } void ArticleViewerWidget::beginWriting() { m_articleHtmlWriter->begin(); } void ArticleViewerWidget::endWriting() { m_articleHtmlWriter->end(); } void ArticleViewerWidget::slotShowSummary(TreeNode *node) { m_viewMode = SummaryView; if (!node) { slotClear(); return; } if (node != m_node) { disconnectFromNode(m_node); connectToNode(node); m_node = node; } const QString summary = normalViewFormatter()->formatSummary(node); m_link.clear(); renderContent(summary); setArticleActionsEnabled(false); } void ArticleViewerWidget::showArticle(const Akregator::Article &article) { if (article.isNull() || article.isDeleted()) { slotClear(); return; } const QUrl xmlUrl = QUrl(article.feed()->xmlUrl()); qCDebug(AKREGATOR_LOG) << "showing Article - xmlUrl:" << xmlUrl; m_articleHtmlWriter->setBaseUrl(xmlUrl); m_viewMode = NormalView; disconnectFromNode(m_node); m_article = article; m_node = nullptr; m_link = article.link(); if (article.feed()->loadLinkedWebsite()) { openUrl(article.link()); } else { renderContent(normalViewFormatter()->formatArticles(QVector() << article, ArticleFormatter::ShowIcon)); } setArticleActionsEnabled(true); } bool ArticleViewerWidget::openUrl(const QUrl &url) { if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite()) { m_articleViewerWidgetNg->articleViewerNg()->load(url); } else { reload(); } return true; } void ArticleViewerWidget::setFilters(const std::vector< QSharedPointer > &filters) { if (filters == m_filters) { return; } m_filters = filters; slotUpdateCombinedView(); } void ArticleViewerWidget::slotUpdateCombinedView() { if (m_viewMode != CombinedView) { return; } if (!m_node) { return slotClear(); } m_articleViewerWidgetNg->saveCurrentPosition(); QString text; int num = 0; QElapsedTimer spent; spent.start(); const std::vector< QSharedPointer >::const_iterator filterEnd = m_filters.cend(); QVector
articles; for (const Article &i : qAsConst(m_articles)) { if (i.isDeleted()) { continue; } auto func = [i](const QSharedPointer &matcher) -> bool { return !matcher->matches(i); }; if (std::find_if(m_filters.cbegin(), filterEnd, func) != filterEnd) { continue; } articles << i; ++num; } text = combinedViewFormatter()->formatArticles(articles, ArticleFormatter::NoIcon); qCDebug(AKREGATOR_LOG) << "Combined view rendering: (" << num << " articles):" << "generating HTML:" << spent.elapsed() << "ms"; renderContent(text); qCDebug(AKREGATOR_LOG) << "HTML rendering:" << spent.elapsed() << "ms"; } void ArticleViewerWidget::slotArticlesUpdated(TreeNode * /*node*/, const QVector
& /*list*/) { if (m_viewMode == CombinedView) { //TODO slotUpdateCombinedView(); } } void ArticleViewerWidget::slotArticlesAdded(TreeNode * /*node*/, const QVector
&list) { if (m_viewMode == CombinedView) { //TODO sort list, then merge m_articles << list; std::sort(m_articles.begin(), m_articles.end()); slotUpdateCombinedView(); } } void ArticleViewerWidget::slotArticlesRemoved(TreeNode * /*node*/, const QVector
&list) { Q_UNUSED(list) if (m_viewMode == CombinedView) { //TODO slotUpdateCombinedView(); } } void ArticleViewerWidget::slotClear() { disconnectFromNode(m_node); m_node = nullptr; m_article = Article(); m_articles.clear(); renderContent(QString()); } void ArticleViewerWidget::showNode(TreeNode *node) { m_viewMode = CombinedView; if (node != m_node) { disconnectFromNode(m_node); } connectToNode(node); m_articles.clear(); m_article = Article(); m_node = node; delete m_listJob; m_listJob = node->createListJob(); connect(m_listJob.data(), &ArticleListJob::finished, this, &ArticleViewerWidget::slotArticlesListed); m_listJob->start(); slotUpdateCombinedView(); } qreal ArticleViewerWidget::zoomFactor() const { return m_articleViewerWidgetNg->articleViewerNg()->zoomFactor(); } void ArticleViewerWidget::slotArticlesListed(KJob *job) { Q_ASSERT(job); Q_ASSERT(job == m_listJob); TreeNode *node = m_listJob->node(); if (job->error() || !node) { if (!node) { qCWarning(AKREGATOR_LOG) << "Node to be listed is already deleted"; } else { qCWarning(AKREGATOR_LOG) << job->errorText(); } slotUpdateCombinedView(); return; } m_articles = m_listJob->articles(); std::sort(m_articles.begin(), m_articles.end()); if (node && !m_articles.isEmpty()) { m_link = m_articles.first().link(); } else { m_link = QUrl(); } slotUpdateCombinedView(); } void ArticleViewerWidget::keyPressEvent(QKeyEvent *e) { e->ignore(); } void ArticleViewerWidget::updateAfterConfigChanged() { switch (m_viewMode) { case NormalView: if (!m_article.isNull()) { renderContent(normalViewFormatter()->formatArticles(QVector() << m_article, ArticleFormatter::ShowIcon)); } break; case CombinedView: slotUpdateCombinedView(); break; case SummaryView: slotShowSummary(m_node); break; } } void ArticleViewerWidget::reload() { beginWriting(); m_articleHtmlWriter->queue(m_currentText); endWriting(); } QSize ArticleViewerWidget::sizeHint() const { // Increase height a bit so that we can (roughly) read 25 lines of text QSize sh = QWidget::sizeHint(); sh.setHeight(qMax(sh.height(), 25 * fontMetrics().height())); return sh; } void ArticleViewerWidget::displayAboutPage() { m_articleViewerWidgetNg->articleViewerNg()->showAboutPage(); } void ArticleViewerWidget::setArticleActionsEnabled(bool enabled) { ActionManager::getInstance()->setArticleActionsEnabled(enabled); } Akregator::ArticleViewerWebEngineWidgetNg *ArticleViewerWidget::articleViewerWidgetNg() const { return m_articleViewerWidgetNg; } diff --git a/src/tabwidget.cpp b/src/tabwidget.cpp index 7e9d979c..012c5564 100644 --- a/src/tabwidget.cpp +++ b/src/tabwidget.cpp @@ -1,519 +1,518 @@ /* This file is part of Akregator. Copyright (C) 2004 Sashmit Bhaduri 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "tabwidget.h" #include #include #include #include #include #include #include #include #include "akregator_debug.h" #include #include -#include #include #include #include #include "actionmanager.h" #include "akregatorconfig.h" #include "frame.h" #include "framemanager.h" #include "kernel.h" #include "openurlrequest.h" #include "utils/temporaryvalue.h" using namespace Akregator; class Q_DECL_HIDDEN TabWidget::Private { private: TabWidget *const q; public: explicit Private(TabWidget *qq) : q(qq) { } QHash frames; QHash framesById; int currentMaxLength = 30; QWidget *currentItem = nullptr; QToolButton *tabsClose = nullptr; QWidget *selectedWidget() const { return (currentItem && q->indexOf(currentItem) != -1) ? currentItem : q->currentWidget(); } int tabBarWidthForMaxChars(int maxLength); void setTitle(const QString &title, QWidget *sender); void updateTabBarVisibility(); Frame *currentFrame(); }; void TabWidget::Private::updateTabBarVisibility() { const bool tabBarIsHidden = ((q->count() <= 1) && !Settings::alwaysShowTabBar()); if (tabBarIsHidden) { q->tabBar()->hide(); } else { q->tabBar()->show(); } if (q->count() >= 1 && Settings::closeButtonOnTabs()) { q->tabBar()->tabButton(0, QTabBar::RightSide)->hide(); } } TabWidget::TabWidget(QWidget *parent) : QTabWidget(parent) , d(new Private(this)) { setMinimumSize(250, 150); setMovable(false); setDocumentMode(true); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &TabWidget::customContextMenuRequested, this, &TabWidget::slotTabContextMenuRequest); connect(this, &TabWidget::currentChanged, this, &TabWidget::slotTabChanged); connect(this, &QTabWidget::tabCloseRequested, this, &TabWidget::slotCloseRequest); setTabsClosable(Settings::closeButtonOnTabs()); d->tabsClose = new QToolButton(this); connect(d->tabsClose, &QToolButton::clicked, this, &TabWidget::slotRemoveCurrentFrame); d->tabsClose->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); d->tabsClose->setEnabled(false); d->tabsClose->adjustSize(); d->tabsClose->setToolTip(i18n("Close the current tab")); #ifndef QT_NO_ACCESSIBILITY d->tabsClose->setAccessibleName(i18n("Close tab")); #endif setCornerWidget(d->tabsClose, Qt::TopRightCorner); d->updateTabBarVisibility(); } TabWidget::~TabWidget() { delete d; } void TabWidget::slotTabContextMenuRequest(const QPoint &pos) { QTabBar *bar = tabBar(); if (count() <= 1) { return; } const int indexBar = bar->tabAt(bar->mapFrom(this, pos)); if (indexBar == -1) { return; } QMenu menu(this); const int countTab = (count() > 1); QAction *detachTab = menu.addAction(i18nc("@action:inmenu", "Detach Tab")); detachTab->setEnabled((indexBar != 0) && countTab); detachTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-detach"))); menu.addSeparator(); QAction *closeTab = menu.addAction(i18nc("@action:inmenu", "Close Tab")); closeTab->setEnabled((indexBar != 0) && countTab); closeTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); QAction *allOther = menu.addAction(i18nc("@action:inmenu", "Close All Other Tabs")); allOther->setEnabled(countTab); allOther->setIcon(QIcon::fromTheme(QStringLiteral("tab-close-other"))); QAction *allTab = menu.addAction(i18nc("@action:inmenu", "Close All Tabs")); allTab->setEnabled(countTab); allTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); QAction *action = menu.exec(mapToGlobal(pos)); if (action == allOther) { // Close all other tabs slotCloseAllTabExcept(indexBar); } else if (action == closeTab) { slotCloseRequest(indexBar); } else if (action == allTab) { slotCloseAllTab(); } else if (action == detachTab) { slotDetachTab(indexBar); } } void TabWidget::closeAllTabExcept(int index) { //Don't close first tab for (int i = count() - 1; i > 0; --i) { if (i == index) { continue; } slotCloseRequest(i); } } void TabWidget::slotCloseAllTabExcept(int index) { closeAllTabExcept(index); } void TabWidget::slotCloseAllTab() { closeAllTabExcept(); } void TabWidget::slotSettingsChanged() { if (tabsClosable() != Settings::closeButtonOnTabs()) { setTabsClosable(Settings::closeButtonOnTabs()); } d->updateTabBarVisibility(); } void TabWidget::slotNextTab() { setCurrentIndex((currentIndex() + 1) % count()); } void TabWidget::slotPreviousTab() { if (currentIndex() == 0) { setCurrentIndex(count() - 1); } else { setCurrentIndex(currentIndex() - 1); } } void TabWidget::slotSelectFrame(int frameId) { Frame *frame = d->framesById.value(frameId); if (frame && frame != d->currentFrame()) { setCurrentWidget(frame); frame->setFocus(); } } void TabWidget::slotAddFrame(Frame *frame) { if (!frame) { return; } d->frames.insert(frame, frame); d->framesById.insert(frame->id(), frame); addTab(frame, frame->title()); connect(frame, &Frame::signalTitleChanged, this, &TabWidget::slotSetTitle); slotSetTitle(frame, frame->title()); } Frame *TabWidget::Private::currentFrame() { QWidget *w = q->currentWidget(); Q_ASSERT(frames.value(w)); return w ? frames.value(w) : nullptr; } void TabWidget::slotZoomChanged(qreal value) { if (!d->currentFrame()) { return; } Q_EMIT signalZoomChangedInFrame(d->currentFrame()->id(), value); } void TabWidget::slotTabChanged(int index) { Frame *frame = d->frames.value(widget(index)); d->tabsClose->setEnabled(frame && frame->isRemovable()); Q_EMIT signalCurrentFrameChanged(frame ? frame->id() : -1); } void TabWidget::tabInserted(int) { d->updateTabBarVisibility(); } void TabWidget::tabRemoved(int) { d->updateTabBarVisibility(); } void TabWidget::slotRemoveCurrentFrame() { Frame *const frame = d->currentFrame(); if (frame) { Q_EMIT signalRemoveFrameRequest(frame->id()); } } void TabWidget::slotRemoveFrame(int frameId) { if (!d->framesById.contains(frameId)) { return; } Frame *f = d->framesById.value(frameId); d->frames.remove(f); d->framesById.remove(frameId); f->disconnect(this); removeTab(indexOf(f)); Q_EMIT signalRemoveFrameRequest(f->id()); if (d->currentFrame()) { d->setTitle(d->currentFrame()->title(), currentWidget()); } } // copied wholesale from KonqFrameTabs int TabWidget::Private::tabBarWidthForMaxChars(int maxLength) { int hframe; QStyleOption o; hframe = q->tabBar()->style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &o, q); QFontMetrics fm = q->tabBar()->fontMetrics(); int x = 0; for (int i = 0; i < q->count(); ++i) { Frame *f = frames.value(q->widget(i)); if (!f) { continue; // frames is out of sync, e.g. because tabInserted wasn't called yet - #185597 } QString newTitle = f->title(); if (newTitle.length() > maxLength) { newTitle = newTitle.left(maxLength - 3) + QLatin1String("..."); } int lw = fm.boundingRect(newTitle).width(); int iw = q->tabBar()->tabIcon(i).pixmap(q->tabBar()->style()->pixelMetric( QStyle::PM_SmallIconSize), QIcon::Normal ).width() + 4; x += (q->tabBar()->style()->sizeFromContents(QStyle::CT_TabBarTab, &o, QSize(lw + hframe + iw, 0), q)).width(); } return x; } void TabWidget::slotSetTitle(Frame *frame, const QString &title) { d->setTitle(title, frame); } void TabWidget::slotWebPageMutedOrAudibleChanged(Akregator::Frame *frame, bool isAudioMuted, bool wasRecentlyAudible) { Q_UNUSED(wasRecentlyAudible); const int idx = indexOf(frame); if (idx < 0) { return; } if (isAudioMuted) { setTabIcon(idx, QIcon::fromTheme(QStringLiteral("audio-volume-muted"))); } else { setTabIcon(idx, frame->icon()); } } void TabWidget::slotSetIcon(Akregator::Frame *frame, const QIcon &icon) { const int idx = indexOf(frame); if (idx < 0) { return; } frame->setIcon(icon); setTabIcon(idx, icon); } void TabWidget::Private::setTitle(const QString &title, QWidget *sender) { int senderIndex = q->indexOf(sender); q->setTabToolTip(senderIndex, QString()); int lcw = 0, rcw = 0; int tabBarHeight = q->tabBar()->sizeHint().height(); QWidget *leftCorner = q->cornerWidget(Qt::TopLeftCorner); if (leftCorner && leftCorner->isVisible()) { lcw = qMax(leftCorner->width(), tabBarHeight); } QWidget *rightCorner = q->cornerWidget(Qt::TopRightCorner); if (rightCorner && rightCorner->isVisible()) { rcw = qMax(rightCorner->width(), tabBarHeight); } int maxTabBarWidth = q->width() - lcw - rcw; int newMaxLength = 30; for (; newMaxLength > 3; newMaxLength--) { if (tabBarWidthForMaxChars(newMaxLength) < maxTabBarWidth) { break; } } QString newTitle = title; if (newTitle.length() > newMaxLength) { q->setTabToolTip(senderIndex, newTitle); newTitle = newTitle.left(newMaxLength - 3) + QLatin1String("..."); } newTitle.replace(QLatin1Char('&'), QStringLiteral("&&")); if (q->tabText(senderIndex) != newTitle) { q->setTabText(senderIndex, newTitle); } if (newMaxLength != currentMaxLength) { for (int i = 0; i < q->count(); ++i) { Frame *f = frames.value(q->widget(i)); if (!f) { continue; // frames is out of sync, e.g. because tabInserted wasn't called yet - #185597 } newTitle = f->title(); int index = q->indexOf(q->widget(i)); q->setTabToolTip(index, QString()); if (newTitle.length() > newMaxLength) { q->setTabToolTip(index, newTitle); newTitle = newTitle.left(newMaxLength - 3) + QLatin1String("..."); } newTitle.replace(QLatin1Char('&'), QStringLiteral("&&")); if (newTitle != q->tabText(index)) { q->setTabText(index, newTitle); } } currentMaxLength = newMaxLength; } } void TabWidget::slotDetachTab(int index) { QWidget *w = widget(index); Frame *frame = d->frames.value(w); if (frame && frame->url().isValid() && frame->isRemovable()) { OpenUrlRequest request; request.setUrl(frame->url()); request.setOptions(OpenUrlRequest::ExternalBrowser); Q_EMIT signalOpenUrlRequest(request); slotCloseRequest(index); } } void TabWidget::slotTextToSpeech() { Q_EMIT signalTextToSpeechInFrame(d->currentFrame()->id()); } void TabWidget::slotFindTextInHtml() { Q_EMIT signalFindTextInFrame(d->currentFrame()->id()); } void TabWidget::slotCopyLinkAddress() { Q_EMIT signalCopyLinkAsInFrame(d->currentFrame()->id()); } void TabWidget::slotSaveLinkAs() { Q_EMIT signalSaveLinkAsInFrame(d->currentFrame()->id()); } void TabWidget::slotPrintPreview() { Q_EMIT signalPrintPreviewInFrame(d->currentFrame()->id()); } void TabWidget::slotPrint() { Q_EMIT signalPrintInFrame(d->currentFrame()->id()); } void TabWidget::slotCopy() { Q_EMIT signalCopyInFrame(d->currentFrame()->id()); } void TabWidget::slotSaveImageOnDisk() { Q_EMIT signalSaveImageOnDisk(d->currentFrame()->id()); } void TabWidget::slotUnMute() { Q_EMIT signalMute(d->currentFrame()->id(), false); } void TabWidget::slotMute() { Q_EMIT signalMute(d->currentFrame()->id(), true); } void TabWidget::slotCopyImageLocation() { Q_EMIT signalCopyImageLocation(d->currentFrame()->id()); } void TabWidget::slotCloseTab() { QWidget *widget = d->selectedWidget(); Frame *frame = d->frames.value(widget); if (frame == nullptr || !frame->isRemovable()) { return; } Q_EMIT signalRemoveFrameRequest(frame->id()); } void TabWidget::slotReloadAllTabs() { for (Frame *frame : qAsConst(d->frames)) { frame->slotReload(); } } void TabWidget::slotCloseRequest(int index) { QWidget *w = widget(index); if (d->frames.value(w)) { Q_EMIT signalRemoveFrameRequest(d->frames.value(w)->id()); } } void TabWidget::slotActivateTab() { setCurrentIndex(sender()->objectName().rightRef(2).toInt() - 1); }