LCOV - code coverage report
Current view: top level - muse/framework/uicomponents/qml/Muse/UiComponents - abstracttableviewmodel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 235 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2025 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "abstracttableviewmodel.h"
      23              : 
      24              : #include <QSet>
      25              : #include <QTimer>
      26              : 
      27              : #include "internal/tableviewlistcell.h"
      28              : 
      29              : using namespace muse::uicomponents;
      30              : 
      31            0 : AbstractTableViewModel::AbstractTableViewModel(QObject* parent)
      32              :     : QAbstractTableModel(parent),
      33            0 :     m_selectionModel(new ItemMultiSelectionModel(this))
      34              : {
      35            0 :     m_selectionModel->setModel(this);
      36            0 :     connect(m_selectionModel, &ItemMultiSelectionModel::selectionChanged,
      37            0 :             this, &AbstractTableViewModel::onSelectionChanged);
      38            0 : }
      39              : 
      40            0 : void AbstractTableViewModel::setHorizontalHeaders(const QVector<TableViewHeader*>& headers)
      41              : {
      42            0 :     m_horizontalHeaders = headers;
      43            0 : }
      44              : 
      45            0 : void AbstractTableViewModel::insertHorizontalHeader(int column, TableViewHeader* header)
      46              : {
      47            0 :     if (column < 0 || column > m_horizontalHeaders.size()) {
      48            0 :         return;
      49              :     }
      50              : 
      51            0 :     m_horizontalHeaders.insert(column, header);
      52              : }
      53              : 
      54            0 : void AbstractTableViewModel::removeHorizontalHeader(int column)
      55              : {
      56            0 :     if (!isColumnValid(column)) {
      57            0 :         return;
      58              :     }
      59              : 
      60            0 :     m_horizontalHeaders.removeAt(column);
      61              : }
      62              : 
      63            0 : void AbstractTableViewModel::setVerticalHeaders(const QVector<TableViewHeader*>& headers)
      64              : {
      65            0 :     m_verticalHeaders = headers;
      66            0 : }
      67              : 
      68            0 : void AbstractTableViewModel::insertVerticalHeader(int row, TableViewHeader* header)
      69              : {
      70            0 :     if (row < 0 || row > m_verticalHeaders.size()) {
      71            0 :         return;
      72              :     }
      73              : 
      74            0 :     m_verticalHeaders.insert(row, header);
      75              : }
      76              : 
      77            0 : void AbstractTableViewModel::removeVerticalHeader(int row)
      78              : {
      79            0 :     if (!isRowValid(row)) {
      80            0 :         return;
      81              :     }
      82              : 
      83            0 :     m_verticalHeaders.removeAt(row);
      84              : }
      85              : 
      86            0 : ItemMultiSelectionModel* AbstractTableViewModel::selectionModel() const
      87              : {
      88            0 :     return m_selectionModel;
      89              : }
      90              : 
      91            0 : void AbstractTableViewModel::setTable(const QVector<QVector<TableViewCell*> >& table)
      92              : {
      93            0 :     for (const auto& row : std::as_const(m_table)) {
      94            0 :         for (TableViewCell* cell : row) {
      95            0 :             if (cell) {
      96            0 :                 disconnectCellSignals(cell);
      97              :             }
      98              :         }
      99              :     }
     100              : 
     101            0 :     beginResetModel();
     102              : 
     103            0 :     m_table = table;
     104              : 
     105            0 :     for (const auto& row : std::as_const(m_table)) {
     106            0 :         for (TableViewCell* cell : row) {
     107            0 :             if (cell) {
     108            0 :                 connectCellSignals(cell);
     109              :             }
     110              :         }
     111              :     }
     112              : 
     113            0 :     endResetModel();
     114            0 : }
     115              : 
     116            0 : void AbstractTableViewModel::insertRow(int row, const QVector<TableViewCell*>& cells)
     117              : {
     118            0 :     if (row < 0 || row > m_table.size()) {
     119            0 :         return;
     120              :     }
     121              : 
     122            0 :     beginResetModel();
     123              : 
     124            0 :     m_table.insert(row, cells);
     125              : 
     126            0 :     for (const auto& cell : std::as_const(m_table.at(row))) {
     127            0 :         connectCellSignals(cell);
     128              :     }
     129              : 
     130            0 :     endResetModel();
     131              : 
     132            0 :     QTimer::singleShot(100, [this, row](){
     133            0 :         m_selectionModel->select(index(row, 0));
     134            0 :     });
     135              : }
     136              : 
     137            0 : void AbstractTableViewModel::removeRow(int row)
     138              : {
     139            0 :     if (!isRowValid(row)) {
     140            0 :         return;
     141              :     }
     142              : 
     143            0 :     beginResetModel();
     144              : 
     145            0 :     for (const auto& cell : std::as_const(m_table.at(row))) {
     146            0 :         disconnectCellSignals(cell);
     147              :     }
     148              : 
     149            0 :     m_table.removeAt(row);
     150              : 
     151            0 :     endResetModel();
     152              : 
     153            0 :     QModelIndex removedIndex = index(row, 0);
     154            0 :     QItemSelection selectionItem(removedIndex, removedIndex);
     155            0 :     emit m_selectionModel->selectionChanged(selectionItem, selectionItem);
     156              : 
     157            0 :     if (isRowValid(row)) {
     158            0 :         QTimer::singleShot(100, [this, row](){
     159            0 :             m_selectionModel->select(index(row, 0));
     160            0 :         });
     161              :     }
     162            0 : }
     163              : 
     164            0 : int AbstractTableViewModel::rowCount(const QModelIndex& parent) const
     165              : {
     166              :     Q_UNUSED(parent);
     167            0 :     return m_table.size();
     168              : }
     169              : 
     170            0 : int AbstractTableViewModel::columnCount(const QModelIndex& parent) const
     171              : {
     172              :     Q_UNUSED(parent);
     173            0 :     return m_horizontalHeaders.size();
     174              : }
     175              : 
     176            0 : QVariant AbstractTableViewModel::data(const QModelIndex& index, int role) const
     177              : {
     178            0 :     if (!isIndexValid(index.row(), index.column())) {
     179            0 :         return QVariant();
     180              :     }
     181              : 
     182            0 :     switch (role) {
     183            0 :     case DisplayRole:
     184            0 :         return QVariant::fromValue(m_table.at(index.row()).at(index.column()));
     185              :     }
     186              : 
     187            0 :     return QVariant();
     188              : }
     189              : 
     190            0 : QVariant AbstractTableViewModel::headerData(int section, Qt::Orientation orientation, int role) const
     191              : {
     192            0 :     if (role != Qt::DisplayRole) {
     193            0 :         return QVariant();
     194              :     }
     195              : 
     196            0 :     if (orientation == Qt::Horizontal) {
     197            0 :         if (!m_horizontalHeaders.empty() && isColumnValid(section)) {
     198            0 :             return QVariant::fromValue(m_horizontalHeaders.at(section));
     199              :         }
     200              :     } else {
     201            0 :         if (!m_verticalHeaders.empty() && isRowValid(section)) {
     202            0 :             return QVariant::fromValue(m_verticalHeaders.at(section));
     203              :         }
     204              :     }
     205              : 
     206            0 :     return QVariant();
     207              : }
     208              : 
     209            0 : QHash<int, QByteArray> AbstractTableViewModel::roleNames() const
     210              : {
     211              :     static const QHash<int, QByteArray> roles {
     212            0 :         { DisplayRole, "display" }
     213            0 :     };
     214              : 
     215            0 :     return roles;
     216            0 : }
     217              : 
     218            0 : bool AbstractTableViewModel::isIndexValid(int row, int column) const
     219              : {
     220            0 :     return isRowValid(row) && isColumnValid(column);
     221              : }
     222              : 
     223            0 : bool AbstractTableViewModel::isRowValid(int row) const
     224              : {
     225            0 :     return row >= 0 && row < m_table.size();
     226              : }
     227              : 
     228            0 : bool AbstractTableViewModel::isColumnValid(int column) const
     229              : {
     230            0 :     return column >= 0 && column < m_horizontalHeaders.size();
     231              : }
     232              : 
     233            0 : TableViewHeader* AbstractTableViewModel::makeHorizontalHeader(const QString& title, TableViewCellType::Type cellType,
     234              :                                                               TableViewCellEditMode::Mode cellEditMode, int preferredWidth,
     235              :                                                               const MenuItemList& availableFormats)
     236              : {
     237            0 :     TableViewHeader* header = new TableViewHeader(this);
     238            0 :     header->setTitle(title);
     239              : 
     240            0 :     header->setCellType(cellType);
     241            0 :     header->setCellEditMode(cellEditMode);
     242            0 :     header->setAvailableFormats(availableFormats);
     243              : 
     244            0 :     if (preferredWidth != -1) {
     245            0 :         header->setPreferredWidth(preferredWidth);
     246              :     }
     247              : 
     248            0 :     return header;
     249              : }
     250              : 
     251            0 : TableViewCell* AbstractTableViewModel::makeCell(const Val& value, const Val& subValue)
     252              : {
     253            0 :     TableViewCell* cell = nullptr;
     254              : 
     255            0 :     if (value.type() == Val::Type::List) {
     256            0 :         auto listCell = new TableViewListCell(this);
     257            0 :         listCell->setCurrent(subValue.toQString());
     258            0 :         cell = std::move(listCell);
     259              :     } else {
     260            0 :         cell = new TableViewCell(this);
     261              :     }
     262              : 
     263            0 :     cell->setValue(value);
     264              : 
     265            0 :     return cell;
     266              : }
     267              : 
     268            0 : TableViewCell* AbstractTableViewModel::findCell(int row, int column) const
     269              : {
     270            0 :     if (!isIndexValid(row, column)) {
     271            0 :         return nullptr;
     272              :     }
     273              : 
     274            0 :     return m_table.at(row).at(column);
     275              : }
     276              : 
     277            0 : TableViewHeader* AbstractTableViewModel::findHorizontalHeader(int column) const
     278              : {
     279            0 :     if (!isColumnValid(column)) {
     280            0 :         return nullptr;
     281              :     }
     282              : 
     283            0 :     return m_horizontalHeaders.at(column);
     284              : }
     285              : 
     286            0 : TableViewHeader* AbstractTableViewModel::findVerticalHeader(int row) const
     287              : {
     288            0 :     if (!isRowValid(row)) {
     289            0 :         return nullptr;
     290              :     }
     291              : 
     292            0 :     return m_verticalHeaders.at(row);
     293              : }
     294              : 
     295            0 : void AbstractTableViewModel::connectCellSignals(TableViewCell* cell)
     296              : {
     297            0 :     if (!cell) {
     298            0 :         return;
     299              :     }
     300              : 
     301            0 :     connect(cell, &TableViewCell::hoveredChanged, this, &AbstractTableViewModel::onCellHoveredChanged);
     302            0 :     connect(cell, &TableViewCell::pressedChanged, this, &AbstractTableViewModel::onCellPressedChanged);
     303              : 
     304            0 :     cell->setRequestChangeFunction([&](int row, int column, const Val& value)-> bool {
     305            0 :         return doCellValueChangeRequested(row, column, value);
     306              :     });
     307              : }
     308              : 
     309            0 : void AbstractTableViewModel::disconnectCellSignals(TableViewCell* cell)
     310              : {
     311            0 :     if (!cell) {
     312            0 :         return;
     313              :     }
     314              : 
     315            0 :     disconnect(cell, &TableViewCell::hoveredChanged, this, &AbstractTableViewModel::onCellHoveredChanged);
     316            0 :     disconnect(cell, &TableViewCell::pressedChanged, this, &AbstractTableViewModel::onCellPressedChanged);
     317              : 
     318            0 :     cell->setRequestChangeFunction(nullptr);
     319              : }
     320              : 
     321            0 : void AbstractTableViewModel::onCellHoveredChanged()
     322              : {
     323            0 :     if (m_updatingRowState) {
     324            0 :         return;
     325              :     }
     326              : 
     327            0 :     TableViewCell* cell = qobject_cast<TableViewCell*>(sender());
     328            0 :     if (!cell) {
     329            0 :         return;
     330              :     }
     331              : 
     332            0 :     updateRowHovered(cell, cell->hovered());
     333              : }
     334              : 
     335            0 : void AbstractTableViewModel::onCellPressedChanged()
     336              : {
     337            0 :     if (m_updatingRowState) {
     338            0 :         return;
     339              :     }
     340              : 
     341            0 :     TableViewCell* cell = qobject_cast<TableViewCell*>(sender());
     342            0 :     if (!cell) {
     343            0 :         return;
     344              :     }
     345              : 
     346            0 :     updateRowPressed(cell, cell->pressed());
     347              : }
     348              : 
     349            0 : bool AbstractTableViewModel::doCellValueChangeRequested(int row, int column, const Val& value)
     350              : {
     351              :     Q_UNUSED(row);
     352              :     Q_UNUSED(column);
     353              :     Q_UNUSED(value);
     354              : 
     355            0 :     return false;
     356              : }
     357              : 
     358            0 : void AbstractTableViewModel::updateRowHovered(TableViewCell* changedCell, bool hovered)
     359              : {
     360            0 :     if (m_updatingRowState) {
     361            0 :         return;
     362              :     }
     363              : 
     364            0 :     int row = findRowForCell(changedCell);
     365            0 :     if (row < 0) {
     366            0 :         return;
     367              :     }
     368              : 
     369            0 :     m_updatingRowState = true;
     370              : 
     371            0 :     const QVector<TableViewCell*>& rowCells = m_table.at(row);
     372            0 :     for (TableViewCell* cell : rowCells) {
     373            0 :         cell->setHovered(hovered);
     374              :     }
     375              : 
     376            0 :     m_updatingRowState = false;
     377              : }
     378              : 
     379            0 : void AbstractTableViewModel::updateRowPressed(TableViewCell* changedCell, bool pressed)
     380              : {
     381            0 :     if (m_updatingRowState) {
     382            0 :         return;
     383              :     }
     384              : 
     385            0 :     int row = findRowForCell(changedCell);
     386            0 :     if (row < 0) {
     387            0 :         return;
     388              :     }
     389              : 
     390            0 :     m_updatingRowState = true;
     391              : 
     392            0 :     const QVector<TableViewCell*>& rowCells = m_table.at(row);
     393            0 :     for (TableViewCell* cell : rowCells) {
     394            0 :         cell->setPressed(pressed);
     395              :     }
     396              : 
     397            0 :     m_updatingRowState = false;
     398              : }
     399              : 
     400            0 : int AbstractTableViewModel::findRowForCell(TableViewCell* cell) const
     401              : {
     402            0 :     for (int row = 0; row < m_table.size(); ++row) {
     403            0 :         const QVector<TableViewCell*>& rowCells = m_table.at(row);
     404            0 :         for (TableViewCell* rowCell : rowCells) {
     405            0 :             if (rowCell == cell) {
     406            0 :                 return row;
     407              :             }
     408              :         }
     409              :     }
     410            0 :     return -1;
     411              : }
     412              : 
     413            0 : int AbstractTableViewModel::findColumnForCell(TableViewCell* cell) const
     414              : {
     415            0 :     int column = -1;
     416            0 :     for (int row = 0; row < m_table.size(); ++row) {
     417            0 :         for (int col = 0; col < m_table.size(); ++col) {
     418            0 :             if (m_table.at(row).at(col) == cell) {
     419            0 :                 column = col;
     420            0 :                 break;
     421              :             }
     422              :         }
     423              :     }
     424              : 
     425            0 :     return column;
     426              : }
     427              : 
     428            0 : void AbstractTableViewModel::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
     429              : {
     430            0 :     if (m_updatingSelection) {
     431            0 :         return;
     432              :     }
     433              : 
     434            0 :     QSet<int> selectedRows;
     435            0 :     for (const QModelIndex& index : selected.indexes()) {
     436            0 :         if (index.isValid()) {
     437            0 :             selectedRows.insert(index.row());
     438              :         }
     439            0 :     }
     440              : 
     441            0 :     for (const QModelIndex& index : m_selectionModel->selectedIndexes()) {
     442            0 :         if (index.isValid()) {
     443            0 :             selectedRows.insert(index.row());
     444              :         }
     445            0 :     }
     446              : 
     447            0 :     QSet<int> deselectedRows;
     448            0 :     for (const QModelIndex& index : deselected.indexes()) {
     449            0 :         if (index.isValid()) {
     450            0 :             deselectedRows.insert(index.row());
     451              :         }
     452            0 :     }
     453              : 
     454            0 :     m_updatingSelection = true;
     455              : 
     456            0 :     for (int row : selectedRows) {
     457            0 :         selectRowCells(row);
     458              :     }
     459              : 
     460            0 :     for (int row : deselectedRows) {
     461            0 :         deselectRowCells(row);
     462              :     }
     463              : 
     464            0 :     m_updatingSelection = false;
     465            0 : }
     466              : 
     467            0 : void AbstractTableViewModel::selectRowCells(int row)
     468              : {
     469            0 :     if (!isRowValid(row)) {
     470            0 :         return;
     471              :     }
     472              : 
     473            0 :     for (int column = 0; column < columnCount(); ++column) {
     474            0 :         QModelIndex index = this->index(row, column);
     475            0 :         if (index.isValid()) {
     476            0 :             m_selectionModel->select(index, QItemSelectionModel::Select);
     477              :         }
     478              :     }
     479              : }
     480              : 
     481            0 : void AbstractTableViewModel::deselectRowCells(int row)
     482              : {
     483            0 :     if (!isRowValid(row)) {
     484            0 :         return;
     485              :     }
     486              : 
     487            0 :     for (int column = 0; column < columnCount(); ++column) {
     488            0 :         QModelIndex index = this->index(row, column);
     489            0 :         if (index.isValid()) {
     490            0 :             m_selectionModel->select(index, QItemSelectionModel::Deselect);
     491              :         }
     492              :     }
     493              : }
        

Generated by: LCOV version 2.0-1