MuseScore/src/palette/view/palettemodel.cpp
2021-09-02 15:36:45 +02:00

1140 lines
39 KiB
C++

/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "palettemodel.h"
#include <QMimeData>
#include "internal/palettetree.h"
#include "internal/palettecelliconengine.h"
#include "engraving/libmscore/actionicon.h"
#include "engraving/libmscore/beam.h"
#include "engraving/libmscore/chordrest.h"
#include "engraving/libmscore/select.h"
#include "commonscene/commonscenetypes.h"
#include "translation.h"
using namespace mu;
using namespace mu::palette;
namespace Ms {
//---------------------------------------------------------
// PaletteTreeModel::PaletteTreeModel
//---------------------------------------------------------
PaletteTreeModel::PaletteTreeModel(PaletteTreePtr tree, QObject* parent)
: QAbstractItemModel(parent), _paletteTree(tree)
{
connect(this, &QAbstractItemModel::dataChanged, this, &PaletteTreeModel::onDataChanged);
connect(this, &QAbstractItemModel::layoutChanged, this, &PaletteTreeModel::setTreeChanged);
connect(this, &QAbstractItemModel::modelReset, this, &PaletteTreeModel::setTreeChanged);
connect(this, &QAbstractItemModel::rowsInserted, this, &PaletteTreeModel::setTreeChanged);
connect(this, &QAbstractItemModel::rowsMoved, this, &PaletteTreeModel::setTreeChanged);
connect(this, &QAbstractItemModel::rowsRemoved, this, &PaletteTreeModel::setTreeChanged);
configuration()->colorsChanged().onNotify(this, [this]() {
notifyAboutCellsChanged(Qt::DecorationRole);
});
}
//---------------------------------------------------------
// PaletteTreeModel::onDataChanged
//---------------------------------------------------------
void PaletteTreeModel::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight,
const QVector<int>& roles)
{
Q_UNUSED(topLeft);
Q_UNUSED(bottomRight);
static const std::set<int> nonPersistentRoles({ CellActiveRole, PaletteExpandedRole });
bool treeChanged = false;
for (int role : roles) {
if (!nonPersistentRoles.count(role)) {
treeChanged = true;
break;
}
}
if (treeChanged) {
setTreeChanged();
}
}
//---------------------------------------------------------
// PaletteTreeModel::setTreeChanged
//---------------------------------------------------------
void PaletteTreeModel::setTreeChanged()
{
_treeChanged = true;
if (!_treeChangedSignalBlocked) {
emit treeChanged();
}
}
//---------------------------------------------------------
// PaletteTreeModel::blockTreeChanged
//---------------------------------------------------------
bool PaletteTreeModel::blockTreeChanged(bool block)
{
const bool wasBlocked = _treeChangedSignalBlocked;
_treeChangedSignalBlocked = block;
if (wasBlocked && !block && _treeChanged) {
emit treeChanged();
}
return wasBlocked;
}
//---------------------------------------------------------
// PaletteTreeModel::iptrToPalette
//---------------------------------------------------------
Palette* PaletteTreeModel::iptrToPalette(void* iptr, int* idx)
{
const auto palette = std::find_if(palettes().begin(), palettes().end(), [iptr](const PalettePtr& p) {
return iptr == p.get();
});
if (idx) {
(*idx) = palette - palettes().begin();
}
if (palette != palettes().end()) {
return static_cast<Palette*>(iptr);
}
return nullptr;
}
void PaletteTreeModel::notifyAboutCellsChanged(int changedRole)
{
const size_t npalettes = palettes().size();
for (size_t row = 0; row < npalettes; ++row) {
Palette* palette = palettes()[row].get();
const QModelIndex parent = index(int(row), 0, QModelIndex());
const QModelIndex first = index(0, 0, parent);
const QModelIndex last = index(palette->cellsCount() - 1, 0, parent);
emit dataChanged(first, last, { changedRole });
}
}
//---------------------------------------------------------
// PaletteTreeModel::findPalette
//---------------------------------------------------------
const Palette* PaletteTreeModel::findPalette(const QModelIndex& index) const
{
if (index.internalPointer() != _paletteTree.get()) {
return nullptr;
}
const int row = index.row();
if (index.column() != 0 || row < 0 || row > int(palettes().size())) {
return nullptr;
}
return palettes()[row].get();
}
//---------------------------------------------------------
// PaletteTreeModel::findPalette
//---------------------------------------------------------
Palette* PaletteTreeModel::findPalette(const QModelIndex& index)
{
return const_cast<Palette*>(const_cast<const PaletteTreeModel*>(this)->findPalette(index));
}
//---------------------------------------------------------
// PaletteTreeModel::findCell
//---------------------------------------------------------
PaletteCellConstPtr PaletteTreeModel::findCell(const QModelIndex& index) const
{
if (const Palette* pp = iptrToPalette(index.internalPointer())) {
const int row = index.row();
if (index.column() != 0 || row < 0 || row >= pp->cellsCount()) {
return nullptr;
}
return pp->cellAt(row);
}
return nullptr;
}
//---------------------------------------------------------
// PaletteTreeModel::findCell
//---------------------------------------------------------
PaletteCellPtr PaletteTreeModel::findCell(const QModelIndex& index)
{
return std::const_pointer_cast<PaletteCell>(const_cast<const PaletteTreeModel*>(this)->findCell(index));
}
//---------------------------------------------------------
// PaletteTreeModel::setPaletteTree
//---------------------------------------------------------
void PaletteTreeModel::setPaletteTree(PaletteTreePtr newTree)
{
beginResetModel();
_paletteTree = newTree;
endResetModel();
_treeChanged = false;
}
//---------------------------------------------------------
// PaletteTreeModel::index
//---------------------------------------------------------
QModelIndex PaletteTreeModel::index(int row, int column, const QModelIndex& parent) const
{
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
if (!parent.isValid()) {
return createIndex(row, column, const_cast<void*>(static_cast<const void*>(_paletteTree.get())));
}
void* iptr = parent.internalPointer();
if (iptr == _paletteTree.get()) {
return createIndex(row, column, palettes()[parent.row()].get());
}
return QModelIndex();
}
//---------------------------------------------------------
// PaletteTreeModel::parent
//---------------------------------------------------------
QModelIndex PaletteTreeModel::parent(const QModelIndex& modelIndex) const
{
void* iptr = modelIndex.internalPointer();
if (iptr == _paletteTree.get()) {
return QModelIndex();
}
int row;
if (iptrToPalette(iptr, &row)) {
return index(row, /* column */ 0, QModelIndex());
}
return QModelIndex();
}
//---------------------------------------------------------
// PaletteTreeModel::rowCount
//---------------------------------------------------------
int PaletteTreeModel::rowCount(const QModelIndex& parent) const
{
if (!parent.isValid()) {
return int(palettes().size());
}
void* iptr = parent.internalPointer();
if (iptr == _paletteTree.get()) {
const int row = parent.row();
if (parent.column() != 0 || row < 0 || row >= int(palettes().size())) {
return 0;
}
return palettes()[row]->cellsCount();
}
return 0;
}
//---------------------------------------------------------
// PaletteTreeModel::columnCount
//---------------------------------------------------------
int PaletteTreeModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return 1;
}
//---------------------------------------------------------
// PaletteTreeModel::data
//---------------------------------------------------------
QVariant PaletteTreeModel::data(const QModelIndex& index, int role) const
{
if (const Palette* pp = findPalette(index)) {
switch (role) {
case Qt::DisplayRole:
case Qt::ToolTipRole:
return pp->translatedName();
case Qt::AccessibleTextRole:
return QString("%1 palette").arg(pp->translatedName());
case VisibleRole:
return pp->isVisible();
case CustomRole:
return pp->isCustom();
case EditableRole:
return pp->isEditable();
case GridSizeRole:
return pp->scaledGridSize();
case DrawGridRole:
return pp->drawGrid();
case PaletteExpandedRole:
return pp->isExpanded();
// TODO showMore?
case PaletteTypeRole:
return QVariant::fromValue(pp->type());
case PaletteContentTypeRole:
return QVariant::fromValue(pp->contentType());
}
return QVariant();
}
if (PaletteCellConstPtr cell = findCell(index)) {
switch (role) {
case Qt::DisplayRole:
return QVariant(); // Don't show element names in
// item views (i.e. just show icons). If you need
// to know the name, use the ToolTip instead.
case Qt::ToolTipRole:
return cell->translatedName();
case Qt::AccessibleTextRole: {
QString name = cell->translatedName();
//ScoreAccessibility::makeReadable(name); //! TODO
return name;
}
case Qt::DecorationRole: {
qreal extraMag = 1.0;
if (const Palette* pp = iptrToPalette(index.internalPointer())) {
extraMag = pp->mag();
}
return QIcon(new PaletteCellIconEngine(cell, extraMag * configuration()->paletteScaling()));
}
case PaletteCellRole:
return QVariant::fromValue(cell.get());
case VisibleRole:
return cell->visible;
case CustomRole:
return cell->custom;
case EditableRole: {
if (const Palette* pp = iptrToPalette(index.internalPointer())) {
return pp->isEditable();
}
return false;
}
case MimeDataRole: {
QVariantMap map;
if (cell->element) {
map[mu::commonscene::MIME_SYMBOL_FORMAT] = cell->element->mimeData(PointF());
}
map[PaletteCell::mimeDataFormat] = cell->toMimeData();
return map;
}
case CellActiveRole:
return cell->active;
default:
break;
}
return QVariant();
}
// data for root item
switch (role) {
case EditableRole:
return true;
default:
break;
}
return QVariant();
}
//---------------------------------------------------------
// PaletteTreeModel::setData
//---------------------------------------------------------
bool PaletteTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (Palette* pp = findPalette(index)) {
switch (role) {
case VisibleRole:
if (value.canConvert<bool>()) {
const bool val = value.toBool();
if (val != pp->isVisible()) {
pp->setVisible(val);
emit dataChanged(index, index, { VisibleRole });
}
return true;
}
return false;
case EditableRole:
if (value.canConvert<bool>()) {
const bool val = value.toBool();
if (val != pp->isEditable()) {
pp->setEditable(val);
emit dataChanged(index, index, { EditableRole });
// notify cells editability changed too
const QModelIndex childFirstIndex = PaletteTreeModel::index(0, 0, index);
const int rows = rowCount(index);
const QModelIndex childLastIndex = PaletteTreeModel::index(rows - 1, 0, index);
emit dataChanged(childFirstIndex, childLastIndex, { EditableRole });
}
return true;
}
return false;
case PaletteExpandedRole:
if (value.canConvert<bool>()) {
const bool val = value.toBool();
if (val != pp->isExpanded()) {
const bool singlePalette = configuration()->isSinglePalette();
if (singlePalette && val) {
for (auto& palette : palettes()) {
palette->setExpanded(false);
}
pp->setExpanded(val);
const QModelIndex parent = index.parent();
const int rows = rowCount(parent);
const QModelIndex first = PaletteTreeModel::index(0, 0, parent);
const QModelIndex last = PaletteTreeModel::index(rows - 1, 0, parent);
emit dataChanged(first, last, { PaletteExpandedRole });
} else {
pp->setExpanded(val);
emit dataChanged(index, index, { PaletteExpandedRole });
}
}
return true;
}
return false;
case Qt::DisplayRole:
pp->setName(value.toString());
emit dataChanged(index, index, { Qt::DisplayRole, Qt::AccessibleTextRole });
return true;
// case CustomRole:
// if (value.canConvert<bool>()) {
// const bool val = value.toBool();
// if (val != pp->custom()) {
// pp->setCustom(val);
// emit dataChanged(index, index, { CustomRole });
// }
// return true;
// }
// return false;
// case gridSizeRole:
// return pp->gridSize();
// case drawGridRole:
// return pp->drawGrid();
default:
break;
}
return false;
}
if (PaletteCellPtr cell = findCell(index)) {
switch (role) {
// case Qt::DisplayRole:
// case Qt::ToolTipRole:
// or EditRole?
// return cell->name;
case PaletteCellRole: {
PaletteCell* newCell = value.value<PaletteCell*>();
if (!newCell) {
return false;
}
*cell = *newCell;
emit dataChanged(index, index);
return true;
};
case VisibleRole:
if (value.canConvert<bool>()) {
const bool val = value.toBool();
if (val != cell->visible) {
cell->visible = val;
emit dataChanged(index, index, { VisibleRole });
}
return true;
}
return false;
case CustomRole:
if (value.canConvert<bool>()) {
const bool val = value.toBool();
if (val != cell->custom) {
cell->custom = val;
emit dataChanged(index, index, { CustomRole });
}
return true;
}
return false;
case MimeDataRole: {
const QVariantMap map = value.toMap();
if (map.contains(PaletteCell::mimeDataFormat)) {
const QByteArray cellMimeData = map[PaletteCell::mimeDataFormat].toByteArray();
PaletteCellPtr newCell(PaletteCell::fromMimeData(cellMimeData));
if (!newCell) {
return false;
}
*cell = *newCell;
} else if (map.contains(mu::commonscene::MIME_SYMBOL_FORMAT)) {
const QByteArray elementMimeData = map[mu::commonscene::MIME_SYMBOL_FORMAT].toByteArray();
*cell = *PaletteCell::fromElementMimeData(elementMimeData);
cell->custom = true; // mark the updated cell custom
} else {
return false;
}
emit dataChanged(index, index);
return true;
}
default:
break;
}
return false;
}
return false;
}
//---------------------------------------------------------
// PaletteTreeModel::roleNames
//---------------------------------------------------------
QHash<int, QByteArray> PaletteTreeModel::roleNames() const
{
QHash<int, QByteArray> roles(QAbstractItemModel::roleNames());
roles[MimeDataRole] = "mimeData";
roles[GridSizeRole] = "gridSize";
roles[DrawGridRole] = "drawGrid";
roles[CustomRole] = "custom";
roles[EditableRole] = "editable";
roles[PaletteExpandedRole] = "expanded";
roles[CellActiveRole] = "cellActive";
roles[Qt::AccessibleTextRole] = "accessibleText";
return roles;
}
//---------------------------------------------------------
// PaletteTreeModel::flags
//---------------------------------------------------------
Qt::ItemFlags PaletteTreeModel::flags(const QModelIndex& index) const
{
return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled;
}
//---------------------------------------------------------
// PaletteTreeModel::supportedDropActions
//---------------------------------------------------------
Qt::DropActions PaletteTreeModel::supportedDropActions() const
{
return Qt::DropActions(Qt::CopyAction | Qt::MoveAction);
}
//---------------------------------------------------------
// PaletteTreeModel::mimeData
//---------------------------------------------------------
QMimeData* PaletteTreeModel::mimeData(const QModelIndexList& indexes) const
{
QMimeData* mime = QAbstractItemModel::mimeData(indexes); // TODO: needed or use only "our" MIME data?
if (indexes.empty() || indexes.size() > 1) {
return mime;
}
if (const Palette* pp = findPalette(indexes[0])) {
mime->setData(Palette::mimeDataFormat, pp->toMimeData());
} else if (PaletteCellConstPtr cell = findCell(indexes[0])) {
mime->setData(mu::commonscene::MIME_SYMBOL_FORMAT, cell->element->mimeData(PointF()));
}
return mime;
}
//---------------------------------------------------------
// PaletteTreeModel::mimeTypes
//---------------------------------------------------------
QStringList PaletteTreeModel::mimeTypes() const
{
QStringList types = QAbstractItemModel::mimeTypes();
types << mu::commonscene::MIME_SYMBOL_FORMAT;
return types;
}
//---------------------------------------------------------
// PaletteTreeModel::canDropMimeData
//---------------------------------------------------------
bool PaletteTreeModel::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
const QModelIndex& parent) const
{
Q_UNUSED(column);
if (!parent.isValid()) {
return (action == Qt::CopyAction) && data->hasFormat(Palette::mimeDataFormat);
}
if (const Palette* pp = findPalette(parent)) {
if (row < 0 || row > int(pp->cellsCount())) {
return false;
}
if (data->hasFormat(PaletteCell::mimeDataFormat)) {
return action & (Qt::CopyAction | Qt::MoveAction);
} else if (data->hasFormat(mu::commonscene::MIME_SYMBOL_FORMAT)) {
return action == Qt::CopyAction;
}
}
return false;
}
//---------------------------------------------------------
// PaletteTreeModel::dropMimeData
//---------------------------------------------------------
bool PaletteTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
const QModelIndex& parent)
{
Q_UNUSED(column); // when dropping at the end of palette, column == -1. Probably an effect of proxy models
if (!parent.isValid()) {
if (action != Qt::CopyAction || !data->hasFormat(Palette::mimeDataFormat)) {
return false;
}
if (row < 0 || row > int(palettes().size())) {
return false;
}
auto palette = Palette::fromMimeData(data->data(Palette::mimeDataFormat));
if (!palette) {
return false;
}
beginInsertRows(parent, row, row);
palettes().insert(palettes().begin() + row, palette);
endInsertRows();
return true;
}
if (Palette* pp = findPalette(parent)) {
if (row < 0 || row > int(pp->cellsCount())) {
return false;
}
PaletteCellPtr cell;
if (data->hasFormat(PaletteCell::mimeDataFormat)) {
cell = PaletteCell::fromMimeData(data->data(PaletteCell::mimeDataFormat));
if (action == Qt::CopyAction) {
cell->custom = true;
}
} else if (data->hasFormat(mu::commonscene::MIME_SYMBOL_FORMAT)) {
cell = PaletteCell::fromElementMimeData(data->data(mu::commonscene::MIME_SYMBOL_FORMAT));
cell->custom = true; // the cell is created by dropping an element so it is custom
}
if (!cell) {
return false;
}
beginInsertRows(parent, row, row);
pp->insertCell(row, cell);
endInsertRows();
return true;
}
return false;
}
//---------------------------------------------------------
// PaletteTreeModel::moveRows
//---------------------------------------------------------
bool PaletteTreeModel::moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
const QModelIndex& destinationParent, int destinationChild)
{
const bool sameParent = sourceParent == destinationParent;
if (!sourceParent.isValid()) {
// moving palettes
if (sourceRow + count > int(palettes().size()) || destinationChild >= int(palettes().size())) {
return false;
}
if (!sameParent) {
// Cannot move between different parents, at least for now
// TODO: reconsider?
return false;
}
// The moved rows are considered to be inserted *before* destinationRow,
// so if we want to move a row down in the list within the same parent
// then we should increment the destinationChild index.
const int destinationRow = (destinationChild >= sourceRow) ? destinationChild + 1 : destinationChild;
if (sameParent && sourceRow == destinationRow) {
return false;
}
std::vector<PalettePtr> movedPalettes;
auto srcBegin = palettes().begin() + sourceRow;
auto srcEnd = srcBegin + count;
// As of Qt 5.12, Qt proxy models rebuild the entire index mapping if
// layoutChanged() gets emitted (i.e. if begin/endMoveRows() gets called).
// Performance is much better when doing remove + insert rows instead.
beginRemoveRows(sourceParent, sourceRow, sourceRow + count - 1);
movedPalettes.reserve(count);
movedPalettes.insert(movedPalettes.end(), std::make_move_iterator(srcBegin), std::make_move_iterator(srcEnd));
palettes().erase(srcBegin, srcEnd);
endRemoveRows();
const int destIdx = (destinationRow < sourceRow) ? destinationRow : (destinationRow - count);
auto dest = palettes().begin() + destIdx;
beginInsertRows(destinationParent, destIdx, destIdx + count - 1);
palettes().insert(dest, std::make_move_iterator(movedPalettes.begin()),
std::make_move_iterator(movedPalettes.end()));
endInsertRows();
return true;
}
Palette* sourcePalette = findPalette(sourceParent);
Palette* destPalette = sameParent ? sourcePalette : findPalette(destinationParent);
if (sourcePalette && destPalette) {
// moving palette cells
if (sourceRow + count > int(sourcePalette->cellsCount()) || destinationChild > int(destPalette->cellsCount())) {
return false;
}
// The moved rows are considered to be inserted *before* destinationRow,
// so if we want to move a row down in the list within the same parent
// then we should increment the destinationChild index.
const int destinationRow
= (sameParent && destinationChild >= sourceRow) ? destinationChild + 1 : destinationChild;
if (sameParent && sourceRow == destinationRow) {
return false;
}
// As of Qt 5.12, Qt proxy models rebuild the entire index mapping if
// layoutChanged() gets emitted (i.e. if begin/endMoveRows() gets called).
// Performance is much better when doing remove + insert rows instead.
beginRemoveRows(sourceParent, sourceRow, sourceRow + count - 1);
auto movedCells(sourcePalette->takeCells(sourceRow, count));
endRemoveRows();
const int destIdx = (sameParent && destinationRow >= sourceRow) ? (destinationRow - count) : destinationRow;
beginInsertRows(destinationParent, destIdx, destIdx + count - 1);
destPalette->insertCells(destIdx, movedCells);
endInsertRows();
return true;
}
return false;
}
//---------------------------------------------------------
// PaletteTreeModel::removeRows
//---------------------------------------------------------
bool PaletteTreeModel::removeRows(int row, int count, const QModelIndex& parent)
{
if (!parent.isValid()) {
// removing palettes
if (row < 0 || row + count > int(palettes().size())) {
return false;
}
beginRemoveRows(parent, row, row + count - 1);
auto rangeBegin = palettes().begin() + row;
auto rangeEnd = rangeBegin + count;
palettes().erase(rangeBegin, rangeEnd);
endRemoveRows();
return true;
}
if (Palette* palette = findPalette(parent)) {
// removing palette cells
if (row < 0 || row + count > palette->cellsCount()) {
return false;
}
beginRemoveRows(parent, row, row + count - 1);
palette->takeCells(row, count);
endRemoveRows();
return true;
}
return false;
}
//---------------------------------------------------------
// PaletteTreeModel::insertRows
//---------------------------------------------------------
bool PaletteTreeModel::insertRows(int row, int count, const QModelIndex& parent)
{
if (!parent.isValid()) {
// inserting palettes
if (row < 0 || row > int(palettes().size())) {
return false;
}
beginInsertRows(parent, row, row + count - 1);
for (int i = 0; i < count; ++i) {
PalettePtr p = std::make_shared<Palette>(Palette::Type::Custom);
p->setName(QT_TRANSLATE_NOOP("palette", "Custom"));
p->setGridSize(QSize(48, 48));
p->setExpanded(true);
palettes().insert(palettes().begin() + row, p);
}
endInsertRows();
return true;
}
if (Palette* palette = findPalette(parent)) {
// inserting palette cells
if (row < 0 || row > palette->cellsCount()) {
return false;
}
beginInsertRows(parent, row, row + count - 1);
for (int i = 0; i < count; ++i) {
PaletteCellPtr cell = std::make_shared<PaletteCell>();
palette->insertCell(row, cell);
}
endInsertRows();
return true;
}
return false;
}
//---------------------------------------------------------
// PaletteTreeModel::insertPalette
//---------------------------------------------------------
bool PaletteTreeModel::insertPalette(PalettePtr pp, int row, const QModelIndex& parent)
{
if (row < 0 || row > int(palettes().size()) || parent != QModelIndex()) {
return false;
}
beginInsertRows(parent, row, row);
palettes().insert(palettes().begin() + row, pp);
endInsertRows();
return true;
}
//---------------------------------------------------------
// PaletteTreeModel::updateCellsState
//---------------------------------------------------------
void PaletteTreeModel::updateCellsState(const Selection& sel)
{
const ChordRest* cr = sel.firstChordRest();
const Beam::Mode bm = cr ? cr->beamMode() : Beam::Mode::NONE;
const ActionIconType beamActionType = Beam::actionIconTypeForBeamMode(bm);
bool deactivateAll = !cr;
for (EngravingItem* e : sel.elements()) {
if (e->isNote()) {
e = e->parentElement();
}
if (e->isChordRest()) {
if (toChordRest(e)->beamMode() != bm) {
deactivateAll = true;
}
}
}
const size_t npalettes = palettes().size();
for (size_t row = 0; row < npalettes; ++row) {
Palette* palette = palettes()[row].get();
// TODO: should this be turned on for all palettes?
if (palette->type() != Palette::Type::Beam) {
continue;
}
for (int ci = 0; ci < palette->cellsCount(); ++ci) {
PaletteCellPtr cell = palette->cellAt(ci);
if (deactivateAll) {
cell->active = false;
} else if (cell->element && cell->element->isActionIcon()) {
const ActionIcon* action = toActionIcon(cell->element.get());
cell->active = (action->actionType() == beamActionType);
}
}
}
notifyAboutCellsChanged(CellActiveRole);
}
//---------------------------------------------------------
// PaletteTreeModel::retranslate
//---------------------------------------------------------
void PaletteTreeModel::retranslate()
{
_paletteTree->retranslate();
}
//---------------------------------------------------------
// PaletteTreeModel::findPaletteCell
//---------------------------------------------------------
QModelIndex PaletteTreeModel::findPaletteCell(const PaletteCell& cell, const QModelIndex& parent) const
{
if (const Palette* pp = findPalette(parent)) {
const int idx = pp->indexOfCell(cell);
if (idx == -1) {
return QModelIndex();
}
return index(idx, /* column */ 0, parent);
}
return QModelIndex();
}
//---------------------------------------------------------
// PaletteTreeModel::match
/// Currently only searching for a given palette cell is
/// implemented, for anything else the corresponding
/// member function of QAbstractItemModel is used.
//---------------------------------------------------------
QModelIndexList PaletteTreeModel::match(const QModelIndex& start, int role, const QVariant& value, int hits,
Qt::MatchFlags flags) const
{
if (role != PaletteCellRole || flags != Qt::MatchExactly || hits != 1
|| !value.canConvert<const PaletteCell*>()
|| !findPalette(start.parent())
) {
return QAbstractItemModel::match(start, role, value, hits, flags);
}
const PaletteCell* cell = value.value<const PaletteCell*>();
if (!cell) {
return QModelIndexList();
}
return QModelIndexList({ findPaletteCell(*cell, start.parent()) });
}
//---------------------------------------------------------
// PaletteTreeModel::itemDataChanged
//---------------------------------------------------------
void PaletteTreeModel::itemDataChanged(const QModelIndex& idx)
{
emit dataChanged(idx, idx);
if (findPalette(idx)) {
// palette cells appearance depends on palette settings
const QModelIndex childFirstIndex = index(0, 0, idx);
const int rows = rowCount(idx);
const QModelIndex childLastIndex = index(rows - 1, 0, idx);
emit dataChanged(childFirstIndex, childLastIndex, { Qt::DecorationRole });
}
}
//---------------------------------------------------------
// PaletteCellFilter::addChainedFilter
/// Ownership over the added filter is passed to this
/// filter.
//---------------------------------------------------------
void PaletteCellFilter::addChainedFilter(PaletteCellFilter* newFilter)
{
PaletteCellFilter* f = this;
while (f->chainedFilter) {
f = f->chainedFilter;
}
newFilter->setParent(f);
f->chainedFilter = newFilter;
connect(newFilter, &PaletteCellFilter::filterChanged, f, &PaletteCellFilter::filterChanged);
}
//---------------------------------------------------------
// PaletteCellFilter::accept
//---------------------------------------------------------
bool PaletteCellFilter::accept(const PaletteCell& cell) const
{
const PaletteCellFilter* f = this;
while (f) {
if (!f->acceptCell(cell)) {
return false;
}
f = f->chainedFilter;
}
return true;
}
//---------------------------------------------------------
// PaletteCellFilter::connectToModel
//---------------------------------------------------------
void PaletteCellFilter::connectToModel(const QAbstractItemModel* model)
{
// TODO: are all these needed?
// columnsInserted(const QModelIndex &parent, int first, int last)
// columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column)
// columnsRemoved(const QModelIndex &parent, int first, int last)
connect(model, &QAbstractItemModel::dataChanged, this, &PaletteCellFilter::filterChanged);
// dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = ...)
connect(model, &QAbstractItemModel::layoutChanged, this, &PaletteCellFilter::filterChanged);
// layoutChanged(const QList<QPersistentModelIndex> &parents = ..., QAbstractItemModel::LayoutChangeHint hint = ...)
connect(model, &QAbstractItemModel::modelReset, this, &PaletteCellFilter::filterChanged);
// modelReset()
connect(model, &QAbstractItemModel::rowsInserted, this, &PaletteCellFilter::filterChanged);
// rowsInserted(const QModelIndex &parent, int first, int last)
connect(model, &QAbstractItemModel::rowsMoved, this, &PaletteCellFilter::filterChanged);
// rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
connect(model, &QAbstractItemModel::rowsRemoved, this, &PaletteCellFilter::filterChanged);
// rowsRemoved(const QModelIndex &parent, int first, int last)
}
//---------------------------------------------------------
// ExcludePaletteCellFilter
//---------------------------------------------------------
class ExcludePaletteCellFilter : public PaletteCellFilter
{
const Palette* excludePalette;
const QPersistentModelIndex paletteIndex; // filter is valid as long as this index is valid too
public:
ExcludePaletteCellFilter(const Palette* p, QPersistentModelIndex index, QObject* parent = nullptr)
: PaletteCellFilter(parent), excludePalette(p), paletteIndex(index) {}
bool acceptCell(const PaletteCell& cell) const override
{
return paletteIndex.isValid() && -1 == excludePalette->indexOfCell(cell, /* matchName */ false);
}
};
//---------------------------------------------------------
// PaletteTreeModel::getFilter
/// The ownership of the returned filter is passed to a
/// caller.
//---------------------------------------------------------
PaletteCellFilter* PaletteTreeModel::getFilter(const QModelIndex& index) const
{
if (const Palette* pp = findPalette(index)) {
ExcludePaletteCellFilter* filter = new ExcludePaletteCellFilter(pp, index);
filter->connectToModel(this);
return filter;
}
// TODO: make a filter for a single cell?
return nullptr;
}
//---------------------------------------------------------
// FilterPaletteTreeModel::FilterPaletteTreeModel
//---------------------------------------------------------
FilterPaletteTreeModel::FilterPaletteTreeModel(PaletteCellFilter* filter, PaletteTreeModel* model, QObject* parent)
: QSortFilterProxyModel(parent), cellFilter(filter)
{
cellFilter->setParent(this);
// connect(cellFilter, &PaletteCellFilter::filterChanged, this, &QSortFilterProxyModel::invalidate);
connect(cellFilter, &PaletteCellFilter::filterChanged, this, &FilterPaletteTreeModel::invalidateFilter);
setSourceModel(model);
}
//---------------------------------------------------------
// FilterPaletteTreeModel::filterAcceptsRow
//---------------------------------------------------------
bool FilterPaletteTreeModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
const QModelIndex& cellIndex = sourceModel()->index(sourceRow, /* column */ 0, sourceParent);
const QVariant cellData = sourceModel()->data(cellIndex, PaletteTreeModel::PaletteCellRole);
const PaletteCell* cell = cellData.value<const PaletteCell*>();
if (!cell) { // a palette or just an unrelated model
return true;
}
return cellFilter->accept(*cell);
}
//---------------------------------------------------------
// PaletteCellFilterProxyModel::PaletteCellFilterProxyModel
//---------------------------------------------------------
PaletteCellFilterProxyModel::PaletteCellFilterProxyModel(QObject* parent)
: QSortFilterProxyModel(parent)
{
setFilterRole(Qt::ToolTipRole); // palette cells have no data for DisplayRole
}
//---------------------------------------------------------
// PaletteCellFilterProxyModel::filterAcceptsRow
//---------------------------------------------------------
bool PaletteCellFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
const QAbstractItemModel* model = sourceModel();
const QModelIndex rowIndex = model->index(sourceRow, 0, sourceParent);
const int rowCount = model->rowCount(rowIndex);
if (rowCount == 0) {
if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) {
return true;
}
// accept row if its parent is accepted by filter: necessary to be able to search by palette name
if (sourceParent.isValid()
&& QSortFilterProxyModel::filterAcceptsRow(sourceParent.row(), sourceParent.parent())) {
return true;
}
return false;
}
for (int i = 0; i < rowCount; ++i) {
if (filterAcceptsRow(i, rowIndex)) {
return true;
}
}
return false;
}
} // namespace Ms