diff --git a/src/plugins/TabManager/tabmanagerdelegate.cpp b/src/plugins/TabManager/tabmanagerdelegate.cpp index a0d05971..4b8c6133 100644 --- a/src/plugins/TabManager/tabmanagerdelegate.cpp +++ b/src/plugins/TabManager/tabmanagerdelegate.cpp @@ -1,285 +1,280 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2016-2017 S. Razi Alavizadeh * Copyright (C) 2017 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 "tabmanagerdelegate.h" #include "tabmanagerwidget.h" #include #include #include TabManagerDelegate::TabManagerDelegate(QObject* parent) : QStyledItemDelegate(parent) { } // most of codes taken from QCommonStyle::drawControl() and add our custom text drawer void TabManagerDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget* w = opt.widget; const QStyle* style = w ? w->style() : QApplication::style(); const Qt::LayoutDirection direction = w ? w->layoutDirection() : QApplication::layoutDirection(); const bool isActiveOrCaption = index.data(TabItem::ActiveOrCaptionRole).toBool(); const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text; QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) { cg = QPalette::Inactive; } #ifdef Q_OS_WIN opt.palette.setColor(QPalette::All, QPalette::HighlightedText, opt.palette.color(QPalette::Active, QPalette::Text)); opt.palette.setColor(QPalette::All, QPalette::Highlight, opt.palette.base().color().darker(108)); #endif QPalette textPalette = opt.palette; textPalette.setCurrentColorGroup(cg); painter->save(); painter->setClipRect(opt.rect); QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, w); QRect iconRect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, w); QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, w); // draw the background style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, w); // draw close button if (index.column() == 1) { if (opt.state & QStyle::State_MouseOver) { static const int buttonSize = 16; static const QPixmap closeTabButton(":tabmanager/data/closetab.png"); static const QPixmap addTabButton(":tabmanager/data/addtab.png"); const QRect rect(opt.rect.right() - buttonSize, (opt.rect.height() - buttonSize) / 2 + opt.rect.y(), buttonSize, buttonSize); painter->drawPixmap(style->visualRect(direction, opt.rect, rect), (index.parent().isValid() ? closeTabButton : addTabButton)); } painter->restore(); return; } // draw the check mark if (opt.features & QStyleOptionViewItem::HasCheckIndicator) { QStyleOptionViewItem opt2(opt); opt2.rect = checkRect; opt2.state = opt2.state & ~QStyle::State_HasFocus; switch (opt.checkState) { case Qt::Unchecked: opt2.state |= QStyle::State_Off; break; case Qt::PartiallyChecked: opt2.state |= QStyle::State_NoChange; break; case Qt::Checked: opt2.state |= QStyle::State_On; break; } style->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt2, painter, w); } // draw the icon QIcon::Mode mode = QIcon::Normal; if (!(opt.state & QStyle::State_Enabled)) mode = QIcon::Disabled; else if (opt.state & QStyle::State_Selected) mode = QIcon::Selected; QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; opt.icon.paint(painter, iconRect, opt.decorationAlignment, mode, state); // draw the text if (!opt.text.isEmpty()) { const QString filterText = property("filterText").toString(); - QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled - ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - if (opt.state & QStyle::State_Selected) { painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(opt.palette.color(cg, QPalette::Text)); } if (opt.state & QStyle::State_Editing) { painter->setPen(opt.palette.color(cg, QPalette::Text)); painter->drawRect(textRect.adjusted(0, 0, -1, -1)); } if (isActiveOrCaption) opt.font.setBold(true); painter->setFont(opt.font); viewItemDrawText(painter, &opt, textRect, opt.text, textPalette.color(colorRole), filterText); } painter->restore(); } static bool sizeBiggerThan(const QString &s1, const QString &s2) { return s1.size() > s2.size(); } static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth) { qreal height = 0; qreal widthUsed = 0; textLayout.beginLayout(); QTextLine line = textLayout.createLine(); if (line.isValid()) { line.setLineWidth(lineWidth); line.setPosition(QPointF(0, height)); height += line.height(); widthUsed = qMax(widthUsed, line.naturalTextWidth()); textLayout.endLayout(); } return QSizeF(widthUsed, height); } // most of codes taken from QCommonStylePrivate::viewItemDrawText() // added highlighting and simplified for single-line textlayouts void TabManagerDelegate::viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect, const QString &text, const QColor &color, const QString &searchText) const { if (text.isEmpty()) { return; } const QWidget* widget = option->widget; const bool isRtlLayout = widget ? widget->isRightToLeft() : QApplication::isRightToLeft(); const QStyle* proxyStyle = widget ? widget->style()->proxy() : QApplication::style()->proxy(); const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1; QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding const QFontMetrics fontMetrics(p->font()); QString elidedText = fontMetrics.elidedText(text, option->textElideMode, textRect.width()); QTextOption textOption; textOption.setWrapMode(QTextOption::NoWrap); textOption.setTextDirection(text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight); textOption.setAlignment(Qt::AlignVCenter | (isRtlLayout ? Qt::AlignRight : Qt::AlignLeft)); QTextLayout textLayout; textLayout.setFont(p->font()); textLayout.setText(elidedText); textLayout.setTextOption(textOption); if (!searchText.isEmpty()) { QList delimiters; QStringList searchStrings = searchText.split(QLatin1Char(' '), QString::SkipEmptyParts); // Look for longer parts first std::sort(searchStrings.begin(), searchStrings.end(), sizeBiggerThan); foreach (const QString &string, searchStrings) { int delimiter = text.indexOf(string, 0, Qt::CaseInsensitive); while (delimiter != -1) { int start = delimiter; int end = delimiter + string.length(); bool alreadyContains = false; for (int i = 0; i < delimiters.count(); ++i) { int dStart = delimiters.at(i); int dEnd = delimiters.at(++i); if (dStart <= start && dEnd >= end) { alreadyContains = true; break; } } if (!alreadyContains) { delimiters.append(start); delimiters.append(end); } delimiter = text.indexOf(string, end, Qt::CaseInsensitive); } } // We need to sort delimiters to properly paint all parts that user typed std::sort(delimiters.begin(), delimiters.end()); // If we don't find any match, just paint it without any highlight if (!delimiters.isEmpty() && !(delimiters.count() % 2)) { QList highlightParts; QTextLayout::FormatRange lighterWholeLine; lighterWholeLine.start = 0; lighterWholeLine.length = elidedText.size(); QColor lighterColor = color.lighter(130); if (lighterColor == color) { lighterColor = QColor(Qt::gray).darker(180); } lighterWholeLine.format.setForeground(lighterColor); highlightParts << lighterWholeLine; while (!delimiters.isEmpty()) { QTextLayout::FormatRange highlightedPart; int start = delimiters.takeFirst(); int end = delimiters.takeFirst(); highlightedPart.start = start; highlightedPart.length = end - start; highlightedPart.format.setFontWeight(QFont::Bold); highlightedPart.format.setUnderlineStyle(QTextCharFormat::SingleUnderline); highlightedPart.format.setForeground(color); highlightParts << highlightedPart; } textLayout.setAdditionalFormats(highlightParts); } } // do layout viewItemTextLayout(textLayout, textRect.width()); if (textLayout.lineCount() <= 0) { return; } QTextLine textLine = textLayout.lineAt(0); // if elidedText after highlighting is longer // than available width then re-elide it and redo layout int diff = textLine.naturalTextWidth() - textRect.width(); if (diff > 0) { elidedText = fontMetrics.elidedText(elidedText, option->textElideMode, textRect.width() - diff); textLayout.setText(elidedText); // redo layout viewItemTextLayout(textLayout, textRect.width()); if (textLayout.lineCount() <= 0) { return; } textLine = textLayout.lineAt(0); } // draw line p->setPen(color); qreal width = qMax(textRect.width(), textLayout.lineAt(0).width()); const QRect &layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment, QSize(int(width), int(textLine.height())), textRect); const QPointF &position = layoutRect.topLeft(); textLine.draw(p, position); } diff --git a/src/plugins/TabManager/tabmanagerwidget.cpp b/src/plugins/TabManager/tabmanagerwidget.cpp index f212e4d0..90ac0387 100644 --- a/src/plugins/TabManager/tabmanagerwidget.cpp +++ b/src/plugins/TabManager/tabmanagerwidget.cpp @@ -1,797 +1,796 @@ /* ============================================================ * TabManager plugin for Falkon * Copyright (C) 2013-2017 S. Razi Alavizadeh * * 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 "tabmanagerwidget.h" #include "ui_tabmanagerwidget.h" #include "mainapplication.h" #include "browserwindow.h" #include "webtab.h" #include "webpage.h" #include "tabbedwebview.h" #include "tabwidget.h" #include "locationbar.h" #include "bookmarkstools.h" #include "bookmarkitem.h" #include "bookmarks.h" #include "tabmanagerplugin.h" #include "tldextractor/tldextractor.h" #include "tabmanagerdelegate.h" #include "tabcontextmenu.h" #include #include #include #include #include #include TLDExtractor* TabManagerWidget::s_tldExtractor = 0; TabManagerWidget::TabManagerWidget(BrowserWindow* mainClass, QWidget* parent, bool defaultWidget) : QWidget(parent) , ui(new Ui::TabManagerWidget) , m_window(mainClass) , m_webPage(0) , m_isRefreshing(false) , m_refreshBlocked(false) , m_waitForRefresh(false) , m_isDefaultWidget(defaultWidget) { if(s_tldExtractor == 0) { s_tldExtractor = TLDExtractor::instance(); s_tldExtractor->setDataSearchPaths(QStringList() << TabManagerPlugin::settingsPath()); } ui->setupUi(this); ui->treeWidget->setUniformRowHeights(true); ui->treeWidget->setColumnCount(2); ui->treeWidget->header()->hide(); ui->treeWidget->header()->setStretchLastSection(false); ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->treeWidget->header()->setSectionResizeMode(1, QHeaderView::Fixed); ui->treeWidget->header()->resizeSection(1, 16); ui->treeWidget->setExpandsOnDoubleClick(false); ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); ui->treeWidget->installEventFilter(this); ui->filterBar->installEventFilter(this); QPushButton* closeButton = new QPushButton(ui->filterBar); closeButton->setFlat(true); closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton)); ui->filterBar->addWidget(closeButton, LineEdit::RightSide); ui->filterBar->hide(); ui->treeWidget->setItemDelegate(new TabManagerDelegate(ui->treeWidget)); connect(closeButton, SIGNAL(clicked(bool)), this, SLOT(filterBarClosed())); connect(ui->filterBar, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); connect(ui->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(onItemActivated(QTreeWidgetItem*,int))); connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); } TabManagerWidget::~TabManagerWidget() { delete ui; } void TabManagerWidget::setGroupType(GroupType type) { m_groupType = type; } QString TabManagerWidget::domainFromUrl(const QUrl &url, bool useHostName) { QString appendString = QL1S(":"); QString urlString = url.toString(); if (url.scheme() == "file") { return tr("Local File System:"); } else if (url.scheme() == "falkon" || urlString.isEmpty()) { return tr("Falkon:"); } else if (url.scheme() == "ftp") { appendString.prepend(tr(" [FTP]")); } QString host = url.host(); if (host.isEmpty()) { return urlString.append(appendString); } if (useHostName || host.contains(QRegExp("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$"))) { if (host.startsWith("www.", Qt::CaseInsensitive)) { host.remove(0, 4); } return host.append(appendString); } else { const QString registeredDomain = s_tldExtractor->registrableDomain(host); if (!registeredDomain.isEmpty()) { host = registeredDomain; } return host.append(appendString); } } void TabManagerWidget::delayedRefreshTree(WebPage* p) { if (m_refreshBlocked || m_waitForRefresh) { return; } if (m_isRefreshing && !p) { return; } m_webPage = p; m_waitForRefresh = true; QTimer::singleShot(50, this, SLOT(refreshTree())); } void TabManagerWidget::refreshTree() { if (m_refreshBlocked) { return; } if (m_isRefreshing && !m_webPage) { return; } // store selected items QList selectedTabs; for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* winItem = ui->treeWidget->topLevelItem(i); if (winItem->checkState(0) == Qt::Unchecked) { continue; } for (int j = 0; j < winItem->childCount(); ++j) { TabItem* tabItem = static_cast(winItem->child(j)); if (!tabItem || tabItem->checkState(0) == Qt::Unchecked) { continue; } selectedTabs << tabItem->webTab(); } } ui->treeWidget->clear(); QTreeWidgetItem* currentTabItem = nullptr; if (m_groupType == GroupByHost) { currentTabItem = groupByDomainName(true); } else if (m_groupType == GroupByDomain) { currentTabItem = groupByDomainName(); } else { // fallback to GroupByWindow m_groupType = GroupByWindow; currentTabItem = groupByWindow(); } // restore selected items for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* winItem = ui->treeWidget->topLevelItem(i); for (int j = 0; j < winItem->childCount(); ++j) { TabItem* tabItem = static_cast(winItem->child(j)); if (tabItem && selectedTabs.contains(tabItem->webTab())) { tabItem->setCheckState(0, Qt::Checked); } } } filterChanged(m_filterText, true); ui->treeWidget->expandAll(); if (currentTabItem) ui->treeWidget->scrollToItem(currentTabItem, QAbstractItemView::EnsureVisible); m_isRefreshing = false; m_waitForRefresh = false; } void TabManagerWidget::onItemActivated(QTreeWidgetItem* item, int column) { TabItem* tabItem = static_cast(item); if (!tabItem) { return; } BrowserWindow* mainWindow = tabItem->window(); QWidget* tabWidget = tabItem->webTab(); if (column == 1) { if (item->childCount() > 0) QMetaObject::invokeMethod(mainWindow ? mainWindow : mApp->getWindow(), "addTab"); else if (tabWidget && mainWindow) mainWindow->tabWidget()->requestCloseTab(mainWindow->tabWidget()->indexOf(tabWidget)); return; } if (!mainWindow) { return; } if (mainWindow->isMinimized()) { mainWindow->showNormal(); } else { mainWindow->show(); } mainWindow->activateWindow(); mainWindow->raise(); mainWindow->weView()->setFocus(); if (tabWidget && tabWidget != mainWindow->tabWidget()->currentWidget()) { mainWindow->tabWidget()->setCurrentIndex(mainWindow->tabWidget()->indexOf(tabWidget)); } } bool TabManagerWidget::isTabSelected() { bool selected = false; for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* parentItem = ui->treeWidget->topLevelItem(i); if (parentItem->checkState(0) != Qt::Unchecked) { selected = true; break; } } return selected; } void TabManagerWidget::customContextMenuRequested(const QPoint &pos) { QMenu* menu = nullptr; TabItem* item = static_cast(ui->treeWidget->itemAt(pos)); if (item) { BrowserWindow* mainWindow = item->window(); QWidget* tabWidget = item->webTab(); if (mainWindow && tabWidget) { int index = mainWindow->tabWidget()->indexOf(tabWidget); // if items are not grouped by Window then actions "Close Other Tabs", // "Close Tabs To The Bottom" and "Close Tabs To The Top" // are ambiguous and should be hidden. menu = new TabContextMenu(index, Qt::Vertical, mainWindow, mainWindow->tabWidget(), m_groupType == GroupByWindow); menu->addSeparator(); } } if (!menu) menu = new QMenu; menu->setAttribute(Qt::WA_DeleteOnClose); QAction* action; QMenu groupTypeSubmenu(tr("Group by")); action = groupTypeSubmenu.addAction(tr("&Window"), this, SLOT(changeGroupType())); action->setData(GroupByWindow); action->setCheckable(true); action->setChecked(m_groupType == GroupByWindow); action = groupTypeSubmenu.addAction(tr("&Domain"), this, SLOT(changeGroupType())); action->setData(GroupByDomain); action->setCheckable(true); action->setChecked(m_groupType == GroupByDomain); action = groupTypeSubmenu.addAction(tr("&Host"), this, SLOT(changeGroupType())); action->setData(GroupByHost); action->setCheckable(true); action->setChecked(m_groupType == GroupByHost); menu->addMenu(&groupTypeSubmenu); if (m_isDefaultWidget) { menu->addAction(QIcon(":/tabmanager/data/side-by-side.png"), tr("&Show side by side"), this, SIGNAL(showSideBySide()))->setObjectName("sideBySide"); } menu->addSeparator(); if (isTabSelected()) { menu->addAction(QIcon(":/tabmanager/data/tab-detach.png"), tr("&Detach checked tabs"), this, SLOT(processActions()))->setObjectName("detachSelection"); menu->addAction(QIcon(":/tabmanager/data/tab-bookmark.png"), tr("Book&mark checked tabs"), this, SLOT(processActions()))->setObjectName("bookmarkSelection"); menu->addAction(QIcon(":/tabmanager/data/tab-close.png"), tr("&Close checked tabs"), this, SLOT(processActions()))->setObjectName("closeSelection"); } menu->exec(ui->treeWidget->viewport()->mapToGlobal(pos)); } void TabManagerWidget::filterChanged(const QString &filter, bool force) { if (force || filter != m_filterText) { m_filterText = filter.simplified(); ui->treeWidget->itemDelegate()->setProperty("filterText", m_filterText); if (m_filterText.isEmpty()) { for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* parentItem = ui->treeWidget->topLevelItem(i); for (int j = 0; j < parentItem->childCount(); ++j) { QTreeWidgetItem* childItem = parentItem->child(j); childItem->setHidden(false); } parentItem->setHidden(false); parentItem->setExpanded(true); } return; } const QRegularExpression filterRegExp(filter.simplified().replace(QChar(' '), QLatin1String(".*")) .append(QLatin1String(".*")).prepend(QLatin1String(".*")), QRegularExpression::CaseInsensitiveOption); for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* parentItem = ui->treeWidget->topLevelItem(i); int visibleChildCount = 0; for (int j = 0; j < parentItem->childCount(); ++j) { TabItem* childItem = static_cast(parentItem->child(j)); if (!childItem) { continue; } if (childItem->text(0).contains(filterRegExp) || childItem->webTab()->url().toString().simplified().contains(filterRegExp)) { ++visibleChildCount; childItem->setHidden(false); } else { childItem->setHidden(true); } } if (visibleChildCount == 0) { parentItem->setHidden(true); } else { parentItem->setHidden(false); parentItem->setExpanded(true); } } } } void TabManagerWidget::filterBarClosed() { ui->filterBar->clear(); ui->filterBar->hide(); ui->treeWidget->setFocusProxy(0); ui->treeWidget->setFocus(); } bool TabManagerWidget::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); const QString text = keyEvent->text().simplified(); if (obj == ui->treeWidget) { // switch to tab/window on enter if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { onItemActivated(ui->treeWidget->currentItem(), 0); return QObject::eventFilter(obj, event); } if (!text.isEmpty() || ((keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() == Qt::Key_F)) { ui->filterBar->show(); ui->treeWidget->setFocusProxy(ui->filterBar); ui->filterBar->setFocus(); if (!text.isEmpty() && text.at(0).isPrint()) { ui->filterBar->setText(ui->filterBar->text() + text); } return true; } } else if (obj == ui->filterBar) { bool isNavigationOrActionKey = keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down || keyEvent->key() == Qt::Key_PageDown || keyEvent->key() == Qt::Key_PageUp || keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return; // send scroll or action press key to treeWidget if (isNavigationOrActionKey) { QKeyEvent ev(QKeyEvent::KeyPress, keyEvent->key(), keyEvent->modifiers()); QApplication::sendEvent(ui->treeWidget, &ev); return false; } } } if (obj == ui->treeWidget && (event->type() == QEvent::Resize || event->type() == QEvent::Show)) ui->treeWidget->setColumnHidden(1, ui->treeWidget->viewport()->width() < 150); return QObject::eventFilter(obj, event); } void TabManagerWidget::processActions() { if (!sender()) { return; } m_refreshBlocked = true; QHash selectedTabs; const QString &command = sender()->objectName(); for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* winItem = ui->treeWidget->topLevelItem(i); if (winItem->checkState(0) == Qt::Unchecked) { continue; } for (int j = 0; j < winItem->childCount(); ++j) { TabItem* tabItem = static_cast(winItem->child(j)); if (!tabItem || tabItem->checkState(0) == Qt::Unchecked) { continue; } BrowserWindow* mainWindow = tabItem->window(); WebTab* webTab = tabItem->webTab(); // current supported actions are not applied to pinned tabs if (webTab->isPinned()) { tabItem->setCheckState(0, Qt::Unchecked); continue; } if (command == "closeSelection") { if (webTab->url().toString() == "falkon:restore") { continue; } selectedTabs.insertMulti(mainWindow, webTab); } else if (command == "detachSelection" || command == "bookmarkSelection") { selectedTabs.insertMulti(mainWindow, webTab); } } winItem->setCheckState(0, Qt::Unchecked); } if (!selectedTabs.isEmpty()) { if (command == "closeSelection") { closeSelectedTabs(selectedTabs); } else if (command == "detachSelection") { detachSelectedTabs(selectedTabs); } else if (command == "bookmarkSelection") { bookmarkSelectedTabs(selectedTabs); } } m_refreshBlocked = false; delayedRefreshTree(); } void TabManagerWidget::changeGroupType() { QAction* action = qobject_cast(sender()); if (action) { int type = action->data().toInt(); if (m_groupType != GroupType(type)) { m_groupType = GroupType(type); delayedRefreshTree(); emit groupTypeChanged(m_groupType); } } } void TabManagerWidget::closeSelectedTabs(const QHash &tabsHash) { if (tabsHash.isEmpty()) { return; } const QList &windows = tabsHash.uniqueKeys(); foreach (BrowserWindow* mainWindow, windows) { QList tabs = tabsHash.values(mainWindow); foreach (WebTab* webTab, tabs) { mainWindow->tabWidget()->requestCloseTab(webTab->tabIndex()); } } } void TabManagerWidget::detachSelectedTabs(const QHash &tabsHash) { // TODO: use TabWidget::detachTab() if (tabsHash.isEmpty() || (tabsHash.uniqueKeys().size() == 1 && tabsHash.size() == tabsHash.keys().at(0)->tabWidget()->count())) { return; } BrowserWindow* newWindow = mApp->createWindow(Qz::BW_OtherRestoredWindow); newWindow->move(mApp->desktop()->availableGeometry(this).topLeft() + QPoint(30, 30)); const QList &windows = tabsHash.uniqueKeys(); foreach (BrowserWindow* mainWindow, windows) { const QList &tabs = tabsHash.values(mainWindow); foreach (WebTab* webTab, tabs) { mainWindow->tabWidget()->locationBars()->removeWidget(webTab->locationBar()); disconnect(webTab->webView(), SIGNAL(wantsCloseTab(int)), mainWindow->tabWidget(), SLOT(closeTab(int))); disconnect(webTab->webView(), SIGNAL(changed()), mainWindow->tabWidget(), SIGNAL(changed())); disconnect(webTab->webView(), SIGNAL(ipChanged(QString)), mainWindow->ipLabel(), SLOT(setText(QString))); webTab->detach(); if (mainWindow && mainWindow->tabWidget()->count() == 0) { mainWindow->close(); mainWindow = 0; } newWindow->tabWidget()->addView(webTab); } } } bool TabManagerWidget::bookmarkSelectedTabs(const QHash &tabsHash) { QDialog* dialog = new QDialog(getWindow(), Qt::WindowStaysOnTopHint | Qt::MSWindowsFixedSizeDialogHint); QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, dialog); QLabel* label = new QLabel(dialog); BookmarksFoldersButton* folderButton = new BookmarksFoldersButton(dialog); QDialogButtonBox* box = new QDialogButtonBox(dialog); box->addButton(QDialogButtonBox::Ok); box->addButton(QDialogButtonBox::Cancel); QObject::connect(box, SIGNAL(rejected()), dialog, SLOT(reject())); QObject::connect(box, SIGNAL(accepted()), dialog, SLOT(accept())); layout->addWidget(label); layout->addWidget(folderButton); layout->addWidget(box); label->setText(tr("Choose folder for bookmarks:")); dialog->setWindowTitle(tr("Bookmark Selected Tabs")); QSize size = dialog->size(); size.setWidth(350); dialog->resize(size); dialog->exec(); if (dialog->result() == QDialog::Rejected) { return false; } foreach (WebTab* tab, tabsHash) { if (!tab->url().isEmpty()) { BookmarkItem* bookmark = new BookmarkItem(BookmarkItem::Url); bookmark->setTitle(tab->title()); bookmark->setUrl(tab->url()); mApp->bookmarks()->addBookmark(folderButton->selectedFolder(), bookmark); } } delete dialog; return true; } QTreeWidgetItem* TabManagerWidget::groupByDomainName(bool useHostName) { QTreeWidgetItem* currentTabItem = nullptr; QList windows = mApp->windows(); int currentWindowIdx = windows.indexOf(getWindow()); if (currentWindowIdx == -1) { // getWindow() instance is closing return nullptr; } - windows.move(currentWindowIdx, 0); QMap tabsGroupedByDomain; for (int win = 0; win < windows.count(); ++win) { BrowserWindow* mainWin = windows.at(win); QList tabs = mainWin->tabWidget()->allTabs(); for (int tab = 0; tab < tabs.count(); ++tab) { WebTab* webTab = tabs.at(tab); if (webTab->webView() && m_webPage == webTab->webView()->page()) { m_webPage = 0; continue; } QString domain = domainFromUrl(webTab->url(), useHostName); if (!tabsGroupedByDomain.contains(domain)) { TabItem* groupItem = new TabItem(ui->treeWidget, 0, false); groupItem->setTitle(domain); groupItem->setIsActiveOrCaption(true); tabsGroupedByDomain.insert(domain, groupItem); } QTreeWidgetItem* groupItem = tabsGroupedByDomain.value(domain); TabItem* tabItem = new TabItem(ui->treeWidget, groupItem); tabItem->setBrowserWindow(mainWin); tabItem->setWebTab(webTab); if (webTab == mainWin->weView()->webTab()) { tabItem->setIsActiveOrCaption(true); if (mainWin == getWindow()) currentTabItem = tabItem; } tabItem->updateIcon(); tabItem->setTitle(webTab->title()); } } ui->treeWidget->insertTopLevelItems(0, tabsGroupedByDomain.values()); return currentTabItem; } QTreeWidgetItem* TabManagerWidget::groupByWindow() { QTreeWidgetItem* currentTabItem = nullptr; QList windows = mApp->windows(); int currentWindowIdx = windows.indexOf(getWindow()); if (currentWindowIdx == -1) { return nullptr; } m_isRefreshing = true; if (!m_isDefaultWidget) { windows.move(currentWindowIdx, 0); currentWindowIdx = 0; } for (int win = 0; win < windows.count(); ++win) { BrowserWindow* mainWin = windows.at(win); TabItem* winItem = new TabItem(ui->treeWidget); winItem->setBrowserWindow(mainWin); winItem->setText(0, tr("Window %1").arg(QString::number(win + 1))); winItem->setToolTip(0, tr("Double click to switch")); winItem->setIsActiveOrCaption(win == currentWindowIdx); QList tabs = mainWin->tabWidget()->allTabs(); for (int tab = 0; tab < tabs.count(); ++tab) { WebTab* webTab = tabs.at(tab); if (webTab->webView() && m_webPage == webTab->webView()->page()) { m_webPage = 0; continue; } TabItem* tabItem = new TabItem(ui->treeWidget, winItem); tabItem->setBrowserWindow(mainWin); tabItem->setWebTab(webTab); if (webTab == mainWin->weView()->webTab()) { tabItem->setIsActiveOrCaption(true); if (mainWin == getWindow()) currentTabItem = tabItem; } tabItem->updateIcon(); tabItem->setTitle(webTab->title()); } } return currentTabItem; } BrowserWindow* TabManagerWidget::getWindow() { if (m_isDefaultWidget || !m_window) { return mApp->getWindow(); } else { return m_window.data(); } } TabItem::TabItem(QTreeWidget* treeWidget, QTreeWidgetItem* parent, bool addToTree) : QObject() , QTreeWidgetItem(addToTree ? (parent ? parent : treeWidget->invisibleRootItem()) : 0, 1) , m_treeWidget(treeWidget) , m_window(0) , m_webTab(0) { setFlags(flags() | (parent ? Qt::ItemIsUserCheckable : Qt::ItemIsUserCheckable | Qt::ItemIsTristate)); setCheckState(0, Qt::Unchecked); } BrowserWindow* TabItem::window() const { return m_window; } void TabItem::setBrowserWindow(BrowserWindow* window) { m_window = window; } WebTab* TabItem::webTab() const { return m_webTab; } void TabItem::setWebTab(WebTab* webTab) { m_webTab = webTab; connect(m_webTab->webView()->page(), SIGNAL(audioMutedChanged(bool)), this, SLOT(updateIcon())); connect(m_webTab->webView()->page(), SIGNAL(loadFinished(bool)), this, SLOT(updateIcon())); connect(m_webTab->webView()->page(), SIGNAL(loadStarted()), this, SLOT(updateIcon())); connect(m_webTab->webView(), SIGNAL(titleChanged(QString)), this, SLOT(setTitle(QString))); connect(m_webTab->webView(), SIGNAL(iconChanged(QIcon)), this, SLOT(updateIcon())); } void TabItem::updateIcon() { if (!m_webTab) return; if (!m_webTab->isLoading()) { if (!m_webTab->isPinned()) { if (m_webTab->isMuted()) { setIcon(0, QIcon::fromTheme(QSL("audio-volume-muted"), QIcon(QSL(":icons/other/audiomuted.svg")))); } else if (!m_webTab->isMuted() && m_webTab->webView()->page()->recentlyAudible()) { setIcon(0, QIcon::fromTheme(QSL("audio-volume-high"), QIcon(QSL(":icons/other/audioplaying.svg")))); } else { setIcon(0, m_webTab->icon()); } } else { setIcon(0, QIcon(":tabmanager/data/tab-pinned.png")); } } else { setIcon(0, QIcon(":tabmanager/data/tab-loading.png")); } } void TabItem::setTitle(const QString &title) { setText(0, title); setToolTip(0, title); } void TabItem::setIsActiveOrCaption(bool yes) { setData(0, ActiveOrCaptionRole, yes ? QVariant(true) : QVariant()); }