Warning, file /network/falkon/src/lib/tabwidget/tabtreemodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "tabtreemodel.h" 0019 #include "tabmodel.h" 0020 #include "webtab.h" 0021 #include "tabwidget.h" 0022 #include "browserwindow.h" 0023 0024 #include <QTimer> 0025 #include <QMimeData> 0026 0027 class TabTreeModelItem 0028 { 0029 public: 0030 explicit TabTreeModelItem(WebTab *tab = nullptr, const QModelIndex &index = QModelIndex()); 0031 ~TabTreeModelItem(); 0032 0033 void setParent(TabTreeModelItem *item); 0034 void addChild(TabTreeModelItem *item, int index = -1); 0035 0036 WebTab *tab = nullptr; 0037 TabTreeModelItem *parent = nullptr; 0038 QVector<TabTreeModelItem*> children; 0039 QPersistentModelIndex sourceIndex; 0040 }; 0041 0042 TabTreeModelItem::TabTreeModelItem(WebTab *tab, const QModelIndex &index) 0043 : tab(tab) 0044 , sourceIndex(index) 0045 { 0046 } 0047 0048 TabTreeModelItem::~TabTreeModelItem() 0049 { 0050 for (TabTreeModelItem *child : std::as_const(children)) { 0051 delete child; 0052 } 0053 } 0054 0055 void TabTreeModelItem::setParent(TabTreeModelItem *item) 0056 { 0057 if (parent == item) { 0058 return; 0059 } 0060 if (parent) { 0061 parent->children.removeOne(this); 0062 } 0063 parent = item; 0064 if (parent) { 0065 parent->children.append(this); 0066 } 0067 } 0068 0069 void TabTreeModelItem::addChild(TabTreeModelItem *item, int index) 0070 { 0071 item->setParent(nullptr); 0072 item->parent = this; 0073 if (index < 0 || index > children.size()) { 0074 children.append(item); 0075 } else { 0076 children.insert(index, item); 0077 } 0078 } 0079 0080 TabTreeModel::TabTreeModel(BrowserWindow *window, QObject *parent) 0081 : QAbstractProxyModel(parent) 0082 , m_window(window) 0083 { 0084 connect(m_window, &BrowserWindow::aboutToClose, this, &TabTreeModel::syncTopLevelTabs); 0085 0086 connect(this, &QAbstractProxyModel::sourceModelChanged, this, &TabTreeModel::init); 0087 } 0088 0089 TabTreeModel::~TabTreeModel() 0090 { 0091 delete m_root; 0092 } 0093 0094 QModelIndex TabTreeModel::tabIndex(WebTab *tab) const 0095 { 0096 TabTreeModelItem *item = m_items.value(tab); 0097 if (!item) { 0098 return {}; 0099 } 0100 return createIndex(item->parent->children.indexOf(item), 0, item); 0101 } 0102 0103 WebTab *TabTreeModel::tab(const QModelIndex &index) const 0104 { 0105 TabTreeModelItem *it = item(index); 0106 return it ? it->tab : nullptr; 0107 } 0108 0109 Qt::ItemFlags TabTreeModel::flags(const QModelIndex &index) const 0110 { 0111 TabTreeModelItem *it = item(index); 0112 if (!it || !it->tab) { 0113 return Qt::ItemIsDropEnabled; 0114 } 0115 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; 0116 if (!it->tab->isPinned()) { 0117 flags |= Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; 0118 } 0119 return flags; 0120 } 0121 0122 QVariant TabTreeModel::data(const QModelIndex &index, int role) const 0123 { 0124 return sourceModel()->data(mapToSource(index), role); 0125 } 0126 0127 int TabTreeModel::rowCount(const QModelIndex &parent) const 0128 { 0129 TabTreeModelItem *it = item(parent); 0130 if (!it) { 0131 return 0; 0132 } 0133 return it->children.count(); 0134 } 0135 0136 int TabTreeModel::columnCount(const QModelIndex &parent) const 0137 { 0138 if (parent.column() > 0) { 0139 return 0; 0140 } 0141 return 1; 0142 } 0143 0144 bool TabTreeModel::hasChildren(const QModelIndex &parent) const 0145 { 0146 TabTreeModelItem *it = item(parent); 0147 if (!it) { 0148 return false; 0149 } 0150 return !it->children.isEmpty(); 0151 } 0152 0153 QModelIndex TabTreeModel::parent(const QModelIndex &child) const 0154 { 0155 TabTreeModelItem *it = item(child); 0156 if (!it) { 0157 return {}; 0158 } 0159 return index(it->parent); 0160 } 0161 0162 QModelIndex TabTreeModel::index(int row, int column, const QModelIndex &parent) const 0163 { 0164 if (!hasIndex(row, column, parent)) { 0165 return {}; 0166 } 0167 TabTreeModelItem *parentItem = item(parent); 0168 return createIndex(row, column, parentItem->children.at(row)); 0169 } 0170 0171 QModelIndex TabTreeModel::mapFromSource(const QModelIndex &sourceIndex) const 0172 { 0173 return tabIndex(sourceIndex.data(TabModel::WebTabRole).value<WebTab*>()); 0174 } 0175 0176 QModelIndex TabTreeModel::mapToSource(const QModelIndex &proxyIndex) const 0177 { 0178 TabTreeModelItem *it = item(proxyIndex); 0179 if (!it) { 0180 return {}; 0181 } 0182 return it->sourceIndex; 0183 } 0184 0185 bool TabTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const 0186 { 0187 Q_UNUSED(row) 0188 Q_UNUSED(parent) 0189 if (action != Qt::MoveAction || column > 0 || !m_window) { 0190 return false; 0191 } 0192 const auto *mimeData = qobject_cast<const TabModelMimeData*>(data); 0193 if (!mimeData) { 0194 return false; 0195 } 0196 WebTab *tab = mimeData->tab(); 0197 if (!tab) { 0198 return false; 0199 } 0200 // This would require moving the entire tab tree 0201 if (tab->browserWindow() != m_window && !tab->childTabs().isEmpty()) { 0202 return false; 0203 } 0204 return true; 0205 } 0206 0207 bool TabTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) 0208 { 0209 if (!canDropMimeData(data, action, row, column, parent)) { 0210 return false; 0211 } 0212 0213 const auto *mimeData = static_cast<const TabModelMimeData*>(data); 0214 WebTab *tab = mimeData->tab(); 0215 0216 if (tab->isPinned()) { 0217 tab->togglePinned(); 0218 } 0219 0220 if (tab->browserWindow() != m_window) { 0221 if (tab->browserWindow()) { 0222 tab->browserWindow()->tabWidget()->detachTab(tab); 0223 m_window->tabWidget()->addView(tab, Qz::NT_SelectedTab); 0224 } 0225 } 0226 0227 TabTreeModelItem *it = m_items.value(tab); 0228 TabTreeModelItem *parentItem = item(parent); 0229 if (!it || !parentItem) { 0230 return false; 0231 } 0232 if (it->parent == parentItem && row < 0) { 0233 return false; 0234 } 0235 if (!parentItem->tab) { 0236 tab->setParentTab(nullptr); 0237 if (row < 0) { 0238 row = m_root->children.count(); 0239 } 0240 const QModelIndex fromIdx = index(it); 0241 const int childPos = row > fromIdx.row() ? row - 1 : row; 0242 if (!beginMoveRows(fromIdx.parent(), fromIdx.row(), fromIdx.row(), QModelIndex(), row)) { 0243 qWarning() << "Invalid beginMoveRows" << fromIdx.parent() << fromIdx.row() << "root" << row; 0244 return true; 0245 } 0246 m_root->addChild(it, childPos); 0247 endMoveRows(); 0248 } else { 0249 parentItem->tab->addChildTab(tab, row); 0250 } 0251 0252 return true; 0253 } 0254 0255 void TabTreeModel::init() 0256 { 0257 delete m_root; 0258 m_items.clear(); 0259 0260 m_root = new TabTreeModelItem; 0261 0262 for (int i = 0; i < sourceModel()->rowCount(); ++i) { 0263 const QModelIndex index = sourceModel()->index(i, 0); 0264 auto *tab = index.data(TabModel::WebTabRole).value<WebTab*>(); 0265 if (tab && !tab->parentTab()) { 0266 auto *item = new TabTreeModelItem(tab, index); 0267 m_items[tab] = item; 0268 m_root->addChild(createItems(item)); 0269 } 0270 } 0271 0272 for (TabTreeModelItem *item : std::as_const(m_items)) { 0273 connectTab(item->tab); 0274 } 0275 0276 connect(sourceModel(), &QAbstractItemModel::dataChanged, this, &TabTreeModel::sourceDataChanged, Qt::UniqueConnection); 0277 connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &TabTreeModel::sourceRowsInserted, Qt::UniqueConnection); 0278 connect(sourceModel(), &QAbstractItemModel::rowsAboutToBeRemoved, this, &TabTreeModel::sourceRowsAboutToBeRemoved, Qt::UniqueConnection); 0279 connect(sourceModel(), &QAbstractItemModel::modelReset, this, &TabTreeModel::sourceReset, Qt::UniqueConnection); 0280 } 0281 0282 QModelIndex TabTreeModel::index(TabTreeModelItem *item) const 0283 { 0284 if (!item || item == m_root) { 0285 return {}; 0286 } 0287 return createIndex(item->parent->children.indexOf(item), 0, item); 0288 } 0289 0290 TabTreeModelItem *TabTreeModel::item(const QModelIndex &index) const 0291 { 0292 auto *it = static_cast<TabTreeModelItem*>(index.internalPointer()); 0293 return it ? it : m_root; 0294 } 0295 0296 TabTreeModelItem *TabTreeModel::createItems(TabTreeModelItem *root) 0297 { 0298 const auto children = root->tab->childTabs(); 0299 for (WebTab *child : children) { 0300 const QModelIndex index = sourceModel()->index(child->tabIndex(), 0); 0301 auto *item = new TabTreeModelItem(child, index); 0302 m_items[child] = item; 0303 root->addChild(createItems(item)); 0304 } 0305 return root; 0306 } 0307 0308 void TabTreeModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) 0309 { 0310 Q_EMIT dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight), roles); 0311 } 0312 0313 void TabTreeModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) 0314 { 0315 for (int i = start; i <= end; ++i) { 0316 insertIndex(sourceModel()->index(i, 0, parent)); 0317 } 0318 } 0319 0320 void TabTreeModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 0321 { 0322 for (int i = start; i <= end; ++i) { 0323 removeIndex(sourceModel()->index(i, 0, parent)); 0324 } 0325 } 0326 0327 void TabTreeModel::sourceReset() 0328 { 0329 beginResetModel(); 0330 init(); 0331 endResetModel(); 0332 } 0333 0334 void TabTreeModel::insertIndex(const QModelIndex &sourceIndex) 0335 { 0336 auto *tab = sourceIndex.data(TabModel::WebTabRole).value<WebTab*>(); 0337 if (!tab) { 0338 return; 0339 } 0340 TabTreeModelItem *parent = m_items.value(tab->parentTab()); 0341 if (!parent) { 0342 parent = m_root; 0343 } 0344 auto *item = new TabTreeModelItem(tab, sourceIndex); 0345 0346 const int idx = parent->children.count(); 0347 beginInsertRows(tabIndex(tab->parentTab()), idx, idx); 0348 m_items[tab] = item; 0349 parent->addChild(item); 0350 endInsertRows(); 0351 0352 connectTab(tab); 0353 } 0354 0355 void TabTreeModel::removeIndex(const QModelIndex &sourceIndex) 0356 { 0357 auto *tab = sourceIndex.data(TabModel::WebTabRole).value<WebTab*>(); 0358 if (!tab) { 0359 return; 0360 } 0361 TabTreeModelItem *item = m_items.value(tab); 0362 if (!item) { 0363 return; 0364 } 0365 0366 const QModelIndex index = mapFromSource(sourceIndex); 0367 beginRemoveRows(index.parent(), index.row(), index.row()); 0368 item->setParent(nullptr); 0369 Q_ASSERT(item->children.isEmpty()); 0370 delete item; 0371 endRemoveRows(); 0372 0373 tab->disconnect(this); 0374 } 0375 0376 void TabTreeModel::connectTab(WebTab *tab) 0377 { 0378 TabTreeModelItem *item = m_items.value(tab); 0379 Q_ASSERT(item); 0380 0381 connect(tab, &WebTab::parentTabChanged, this, [=](WebTab *parent) { 0382 // Handle only move to root, everything else is done in childTabAdded 0383 if (item->parent == m_root || parent) { 0384 return; 0385 } 0386 int pos = m_root->children.count(); 0387 // Move it to the same spot as old parent 0388 if (item->parent->parent == m_root) { 0389 pos = m_root->children.indexOf(item->parent); 0390 } 0391 const QModelIndex fromIdx = index(item); 0392 if (!beginMoveRows(fromIdx.parent(), fromIdx.row(), fromIdx.row(), QModelIndex(), pos)) { 0393 qWarning() << "Invalid beginMoveRows" << fromIdx.parent() << fromIdx.row() << "root" << pos; 0394 return; 0395 } 0396 m_root->addChild(item, pos); 0397 endMoveRows(); 0398 }); 0399 0400 connect(tab, &WebTab::childTabAdded, this, [=](WebTab *child, int pos) { 0401 TabTreeModelItem *from = m_items.value(child); 0402 if (!from) { 0403 return; 0404 } 0405 const QModelIndex fromIdx = index(from); 0406 const QModelIndex toIdx = index(item); 0407 const int childPos = fromIdx.parent() == toIdx && pos > fromIdx.row() ? pos - 1 : pos; 0408 if (!beginMoveRows(fromIdx.parent(), fromIdx.row(), fromIdx.row(), toIdx, pos)) { 0409 qWarning() << "Invalid beginMoveRows" << fromIdx.parent() << fromIdx.row() << toIdx << pos; 0410 return; 0411 } 0412 item->addChild(from, childPos); 0413 endMoveRows(); 0414 }); 0415 } 0416 0417 void TabTreeModel::syncTopLevelTabs() 0418 { 0419 // Move all normal top-level tabs to the beginning to preserve order in session 0420 0421 int index = -1; 0422 0423 const auto items = m_root->children; 0424 for (TabTreeModelItem *item : items) { 0425 if (!item->tab->isPinned()) { 0426 const int tabIndex = item->tab->tabIndex(); 0427 if (index < 0 || tabIndex < index) { 0428 index = tabIndex; 0429 } 0430 } 0431 } 0432 0433 if (index < 0) { 0434 return; 0435 } 0436 0437 for (TabTreeModelItem *item : items) { 0438 if (!item->tab->isPinned()) { 0439 item->tab->moveTab(index++); 0440 } 0441 } 0442 }