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

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 "tabmodel.h"
0019 #include "webtab.h"
0020 #include "tabwidget.h"
0021 #include "browserwindow.h"
0022 
0023 // TabModelMimeData
0024 TabModelMimeData::TabModelMimeData()
0025     : QMimeData()
0026 {
0027 }
0028 
0029 WebTab *TabModelMimeData::tab() const
0030 {
0031     return m_tab;
0032 }
0033 
0034 void TabModelMimeData::setTab(WebTab *tab)
0035 {
0036     m_tab = tab;
0037 }
0038 
0039 bool TabModelMimeData::hasFormat(const QString &format) const
0040 {
0041     return mimeType() == format;
0042 }
0043 
0044 QStringList TabModelMimeData::formats() const
0045 {
0046     return {mimeType()};
0047 }
0048 
0049 // static
0050 QString TabModelMimeData::mimeType()
0051 {
0052     return QSL("application/falkon.tabmodel.tab");
0053 }
0054 
0055 // TabModel
0056 TabModel::TabModel(BrowserWindow *window, QObject *parent)
0057     : QAbstractListModel(parent)
0058     , m_window(window)
0059 {
0060     init();
0061 }
0062 
0063 QModelIndex TabModel::tabIndex(WebTab *tab) const
0064 {
0065     const int idx = m_tabs.indexOf(tab);
0066     if (idx < 0) {
0067         return {};
0068     }
0069     return index(idx);
0070 }
0071 
0072 WebTab *TabModel::tab(const QModelIndex &index) const
0073 {
0074     return m_tabs.value(index.row());
0075 }
0076 
0077 int TabModel::rowCount(const QModelIndex &parent) const
0078 {
0079     if (parent.isValid()) {
0080         return 0;
0081     }
0082     return m_tabs.count();
0083 }
0084 
0085 Qt::ItemFlags TabModel::flags(const QModelIndex &index) const
0086 {
0087     if (!index.isValid()) {
0088         return Qt::ItemIsDropEnabled;
0089     }
0090     return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
0091 }
0092 
0093 QVariant TabModel::data(const QModelIndex &index, int role) const
0094 {
0095     if (index.row() < 0 || index.row() > m_tabs.count()) {
0096         return {};
0097     }
0098 
0099     WebTab *t = tab(index);
0100     if (!t) {
0101         return {};
0102     }
0103 
0104     switch (role) {
0105     case WebTabRole:
0106         return QVariant::fromValue(t);
0107 
0108     case TitleRole:
0109     case Qt::DisplayRole:
0110         return t->title();
0111 
0112     case IconRole:
0113     case Qt::DecorationRole:
0114         return t->icon();
0115 
0116     case PinnedRole:
0117         return t->isPinned();
0118 
0119     case RestoredRole:
0120         return t->isRestored();
0121 
0122     case CurrentTabRole:
0123         return t->isCurrentTab();
0124 
0125     case LoadingRole:
0126         return t->isLoading();
0127 
0128     case AudioPlayingRole:
0129         return t->isPlaying();
0130 
0131     case AudioMutedRole:
0132         return t->isMuted();
0133 
0134     case BackgroundActivityRole:
0135         return t->backgroundActivity();
0136 
0137     default:
0138         return {};
0139     }
0140 }
0141 
0142 Qt::DropActions TabModel::supportedDropActions() const
0143 {
0144     return Qt::MoveAction;
0145 }
0146 
0147 QStringList TabModel::mimeTypes() const
0148 {
0149     return {TabModelMimeData::mimeType()};
0150 }
0151 
0152 QMimeData *TabModel::mimeData(const QModelIndexList &indexes) const
0153 {
0154     if (indexes.isEmpty()) {
0155         return nullptr;
0156     }
0157     auto *tab = indexes.at(0).data(WebTabRole).value<WebTab*>();
0158     if (!tab) {
0159         return nullptr;
0160     }
0161     auto *mimeData = new TabModelMimeData;
0162     mimeData->setTab(tab);
0163     return mimeData;
0164 }
0165 
0166 bool TabModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
0167 {
0168     Q_UNUSED(row)
0169     if (action != Qt::MoveAction || parent.isValid() || column > 0 || !m_window) {
0170         return false;
0171     }
0172     const auto *mimeData = qobject_cast<const TabModelMimeData*>(data);
0173     if (!mimeData) {
0174         return false;
0175     }
0176     return mimeData->tab();
0177 }
0178 
0179 bool TabModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
0180 {
0181     if (!canDropMimeData(data, action, row, column, parent)) {
0182         return false;
0183     }
0184 
0185     const auto *mimeData = static_cast<const TabModelMimeData*>(data);
0186     WebTab *tab = mimeData->tab();
0187 
0188     if (tab->browserWindow() == m_window) {
0189         if (row < 0) {
0190             row = tab->isPinned() ? m_window->tabWidget()->pinnedTabsCount() : m_window->tabWidget()->count();
0191         }
0192         tab->moveTab(row > mimeData->tab()->tabIndex() ? row - 1 : row);
0193     } else {
0194         if (row < 0) {
0195             row = m_window->tabCount();
0196         }
0197         if (tab->browserWindow()) {
0198             tab->browserWindow()->tabWidget()->detachTab(tab);
0199         }
0200         tab->setPinned(row < m_window->tabWidget()->pinnedTabsCount());
0201         m_window->tabWidget()->insertView(row, tab, Qz::NT_SelectedTab);
0202     }
0203 
0204     return true;
0205 }
0206 
0207 void TabModel::init()
0208 {
0209     for (int i = 0; i < m_window->tabCount(); ++i) {
0210         tabInserted(i);
0211     }
0212 
0213     connect(m_window->tabWidget(), &TabWidget::tabInserted, this, &TabModel::tabInserted);
0214     connect(m_window->tabWidget(), &TabWidget::tabRemoved, this, &TabModel::tabRemoved);
0215     connect(m_window->tabWidget(), &TabWidget::tabMoved, this, &TabModel::tabMoved);
0216 
0217     connect(m_window, &QObject::destroyed, this, [this]() {
0218         beginResetModel();
0219         m_window = nullptr;
0220         m_tabs.clear();
0221         endResetModel();
0222     });
0223 }
0224 
0225 void TabModel::tabInserted(int index)
0226 {
0227     WebTab *tab = m_window->tabWidget()->webTab(index);
0228 
0229     beginInsertRows(QModelIndex(), index, index);
0230     m_tabs.insert(index, tab);
0231     endInsertRows();
0232 
0233     auto emitDataChanged = [this](WebTab *tab, int role) {
0234         const QModelIndex idx = tabIndex(tab);
0235         Q_EMIT dataChanged(idx, idx, {role});
0236     };
0237 
0238     connect(tab, &WebTab::titleChanged, this, std::bind(emitDataChanged, tab, Qt::DisplayRole));
0239     connect(tab, &WebTab::titleChanged, this, std::bind(emitDataChanged, tab, TitleRole));
0240     connect(tab, &WebTab::iconChanged, this, std::bind(emitDataChanged, tab, Qt::DecorationRole));
0241     connect(tab, &WebTab::iconChanged, this, std::bind(emitDataChanged, tab, IconRole));
0242     connect(tab, &WebTab::pinnedChanged, this, std::bind(emitDataChanged, tab, PinnedRole));
0243     connect(tab, &WebTab::restoredChanged, this, std::bind(emitDataChanged, tab, RestoredRole));
0244     connect(tab, &WebTab::currentTabChanged, this, std::bind(emitDataChanged, tab, CurrentTabRole));
0245     connect(tab, &WebTab::loadingChanged, this, std::bind(emitDataChanged, tab, LoadingRole));
0246     connect(tab, &WebTab::playingChanged, this, std::bind(emitDataChanged, tab, AudioPlayingRole));
0247     connect(tab, &WebTab::mutedChanged, this, std::bind(emitDataChanged, tab, AudioMutedRole));
0248     connect(tab, &WebTab::backgroundActivityChanged, this, std::bind(emitDataChanged, tab, BackgroundActivityRole));
0249 }
0250 
0251 void TabModel::tabRemoved(int index)
0252 {
0253     beginRemoveRows(QModelIndex(), index, index);
0254     m_tabs.remove(index);
0255     endRemoveRows();
0256 }
0257 
0258 void TabModel::tabMoved(int from, int to)
0259 {
0260     if (!beginMoveRows(QModelIndex(), from, from, QModelIndex(), to > from ? to + 1 : to)) {
0261         qWarning() << "Invalid beginMoveRows" << from << (to > from ? to + 1 : to);
0262         return;
0263     }
0264     m_tabs.insert(to, m_tabs.takeAt(from));
0265     endMoveRows();
0266 }