Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 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 :
23 : #include "palettemodel.h"
24 :
25 : #include <QMimeData>
26 :
27 : #include "internal/palettetree.h"
28 : #include "internal/palettecelliconengine.h"
29 :
30 : #include "engraving/dom/actionicon.h"
31 : #include "engraving/dom/beam.h"
32 : #include "engraving/dom/chordrest.h"
33 : #include "engraving/dom/note.h"
34 : #include "engraving/dom/select.h"
35 :
36 : #include "modularity/ioc.h"
37 : #include "translation.h"
38 :
39 : using namespace mu;
40 : using namespace mu::palette;
41 : using namespace mu::engraving;
42 :
43 : //---------------------------------------------------------
44 : // PaletteTreeModel::PaletteTreeModel
45 : //---------------------------------------------------------
46 :
47 0 : PaletteTreeModel::PaletteTreeModel(PaletteTreePtr tree, const muse::modularity::ContextPtr& ctx, QObject* parent)
48 0 : : QAbstractItemModel(parent), muse::Contextable(ctx), _paletteTree(tree)
49 : {
50 0 : connect(this, &QAbstractItemModel::dataChanged, this, &PaletteTreeModel::onDataChanged);
51 0 : connect(this, &QAbstractItemModel::layoutChanged, this, &PaletteTreeModel::setTreeChanged);
52 0 : connect(this, &QAbstractItemModel::modelReset, this, &PaletteTreeModel::setTreeChanged);
53 0 : connect(this, &QAbstractItemModel::rowsInserted, this, &PaletteTreeModel::setTreeChanged);
54 0 : connect(this, &QAbstractItemModel::rowsMoved, this, &PaletteTreeModel::setTreeChanged);
55 0 : connect(this, &QAbstractItemModel::rowsRemoved, this, &PaletteTreeModel::setTreeChanged);
56 :
57 0 : configuration()->colorsChanged().onNotify(this, [this]() {
58 0 : notifyAboutCellsChanged(Qt::DecorationRole);
59 0 : });
60 0 : }
61 :
62 : //---------------------------------------------------------
63 : // PaletteTreeModel::onDataChanged
64 : //---------------------------------------------------------
65 :
66 0 : void PaletteTreeModel::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight,
67 : const QVector<int>& roles)
68 : {
69 : Q_UNUSED(topLeft);
70 : Q_UNUSED(bottomRight);
71 0 : static const std::set<int> nonPersistentRoles({ CellActiveRole, PaletteExpandedRole, Qt::DecorationRole });
72 :
73 0 : bool treeChanged = false;
74 0 : for (int role : roles) {
75 0 : if (!nonPersistentRoles.count(role)) {
76 0 : treeChanged = true;
77 0 : break;
78 : }
79 : }
80 :
81 0 : if (treeChanged) {
82 0 : setTreeChanged();
83 : }
84 0 : }
85 :
86 : //---------------------------------------------------------
87 : // PaletteTreeModel::setTreeChanged
88 : //---------------------------------------------------------
89 :
90 0 : void PaletteTreeModel::setTreeChanged()
91 : {
92 0 : _treeChanged = true;
93 0 : if (!_treeChangedSignalBlocked) {
94 0 : emit treeChanged();
95 : }
96 0 : }
97 :
98 : //---------------------------------------------------------
99 : // PaletteTreeModel::blockTreeChanged
100 : //---------------------------------------------------------
101 :
102 0 : bool PaletteTreeModel::blockTreeChanged(bool block)
103 : {
104 0 : const bool wasBlocked = _treeChangedSignalBlocked;
105 0 : _treeChangedSignalBlocked = block;
106 :
107 0 : if (wasBlocked && !block && _treeChanged) {
108 0 : emit treeChanged();
109 : }
110 :
111 0 : return wasBlocked;
112 : }
113 :
114 : //---------------------------------------------------------
115 : // PaletteTreeModel::iptrToPalette
116 : //---------------------------------------------------------
117 :
118 0 : Palette* PaletteTreeModel::iptrToPalette(void* iptr, int* idx)
119 : {
120 0 : const auto palette = std::find_if(palettes().begin(), palettes().end(), [iptr](const PalettePtr& p) {
121 0 : return iptr == p.get();
122 : });
123 :
124 0 : if (idx) {
125 0 : (*idx) = palette - palettes().begin();
126 : }
127 :
128 0 : if (palette != palettes().end()) {
129 0 : return static_cast<Palette*>(iptr);
130 : }
131 0 : return nullptr;
132 : }
133 :
134 0 : void PaletteTreeModel::notifyAboutCellsChanged(int changedRole)
135 : {
136 0 : const size_t npalettes = palettes().size();
137 0 : for (size_t row = 0; row < npalettes; ++row) {
138 0 : Palette* palette = palettes()[row].get();
139 :
140 0 : const QModelIndex parent = index(int(row), 0, QModelIndex());
141 0 : const QModelIndex first = index(0, 0, parent);
142 0 : const QModelIndex last = index(palette->cellsCount() - 1, 0, parent);
143 0 : emit dataChanged(first, last, { changedRole });
144 : }
145 0 : }
146 :
147 : //---------------------------------------------------------
148 : // PaletteTreeModel::findPalette
149 : //---------------------------------------------------------
150 :
151 0 : const Palette* PaletteTreeModel::findPalette(const QModelIndex& index) const
152 : {
153 0 : if (index.internalPointer() != _paletteTree.get()) {
154 0 : return nullptr;
155 : }
156 :
157 0 : const int row = index.row();
158 0 : if (index.column() != 0 || row < 0 || row > int(palettes().size())) {
159 0 : return nullptr;
160 : }
161 :
162 0 : return palettes()[row].get();
163 : }
164 :
165 : //---------------------------------------------------------
166 : // PaletteTreeModel::findPalette
167 : //---------------------------------------------------------
168 :
169 0 : Palette* PaletteTreeModel::findPalette(const QModelIndex& index)
170 : {
171 0 : return const_cast<Palette*>(const_cast<const PaletteTreeModel*>(this)->findPalette(index));
172 : }
173 :
174 : //---------------------------------------------------------
175 : // PaletteTreeModel::findCell
176 : //---------------------------------------------------------
177 :
178 0 : PaletteCellConstPtr PaletteTreeModel::findCell(const QModelIndex& index) const
179 : {
180 0 : if (const Palette* pp = iptrToPalette(index.internalPointer())) {
181 0 : const int row = index.row();
182 0 : if (index.column() != 0 || row < 0 || row >= pp->cellsCount()) {
183 0 : return nullptr;
184 : }
185 0 : return pp->cellAt(row);
186 : }
187 :
188 0 : return nullptr;
189 : }
190 :
191 : //---------------------------------------------------------
192 : // PaletteTreeModel::findCell
193 : //---------------------------------------------------------
194 :
195 0 : PaletteCellPtr PaletteTreeModel::findCell(const QModelIndex& index)
196 : {
197 0 : return std::const_pointer_cast<PaletteCell>(const_cast<const PaletteTreeModel*>(this)->findCell(index));
198 : }
199 :
200 : //---------------------------------------------------------
201 : // PaletteTreeModel::setPaletteTree
202 : //---------------------------------------------------------
203 :
204 0 : void PaletteTreeModel::setPaletteTree(PaletteTreePtr newTree)
205 : {
206 0 : beginResetModel();
207 0 : _paletteTree = newTree;
208 0 : endResetModel();
209 :
210 0 : _treeChanged = false;
211 0 : }
212 :
213 : //---------------------------------------------------------
214 : // PaletteTreeModel::index
215 : //---------------------------------------------------------
216 :
217 0 : QModelIndex PaletteTreeModel::index(int row, int column, const QModelIndex& parent) const
218 : {
219 0 : if (!hasIndex(row, column, parent)) {
220 0 : return QModelIndex();
221 : }
222 :
223 0 : if (!parent.isValid()) {
224 0 : return createIndex(row, column, const_cast<void*>(static_cast<const void*>(_paletteTree.get())));
225 : }
226 :
227 0 : void* iptr = parent.internalPointer();
228 :
229 0 : if (iptr == _paletteTree.get()) {
230 0 : return createIndex(row, column, palettes()[parent.row()].get());
231 : }
232 :
233 0 : return QModelIndex();
234 : }
235 :
236 : //---------------------------------------------------------
237 : // PaletteTreeModel::parent
238 : //---------------------------------------------------------
239 :
240 0 : QModelIndex PaletteTreeModel::parent(const QModelIndex& modelIndex) const
241 : {
242 0 : void* iptr = modelIndex.internalPointer();
243 :
244 0 : if (iptr == _paletteTree.get()) {
245 0 : return QModelIndex();
246 : }
247 :
248 : int row;
249 0 : if (iptrToPalette(iptr, &row)) {
250 0 : return index(row, /* column */ 0, QModelIndex());
251 : }
252 :
253 0 : return QModelIndex();
254 : }
255 :
256 : //---------------------------------------------------------
257 : // PaletteTreeModel::rowCount
258 : //---------------------------------------------------------
259 :
260 0 : int PaletteTreeModel::rowCount(const QModelIndex& parent) const
261 : {
262 0 : if (!parent.isValid()) {
263 0 : return int(palettes().size());
264 : }
265 :
266 0 : void* iptr = parent.internalPointer();
267 :
268 0 : if (iptr == _paletteTree.get()) {
269 0 : const int row = parent.row();
270 0 : if (parent.column() != 0 || row < 0 || row >= int(palettes().size())) {
271 0 : return 0;
272 : }
273 0 : return palettes()[row]->cellsCount();
274 : }
275 :
276 0 : return 0;
277 : }
278 :
279 : //---------------------------------------------------------
280 : // PaletteTreeModel::columnCount
281 : //---------------------------------------------------------
282 :
283 0 : int PaletteTreeModel::columnCount(const QModelIndex& parent) const
284 : {
285 : Q_UNUSED(parent);
286 0 : return 1;
287 : }
288 :
289 : //---------------------------------------------------------
290 : // PaletteTreeModel::data
291 : //---------------------------------------------------------
292 :
293 0 : QVariant PaletteTreeModel::data(const QModelIndex& index, int role) const
294 : {
295 0 : if (const Palette* pp = findPalette(index)) {
296 0 : switch (role) {
297 0 : case Qt::DisplayRole:
298 : case Qt::ToolTipRole:
299 0 : return pp->translatedName();
300 0 : case Qt::AccessibleTextRole:
301 0 : return QString("%1 palette").arg(pp->translatedName());
302 0 : case VisibleRole:
303 0 : return pp->isVisible();
304 0 : case CustomRole:
305 0 : return pp->isCustom();
306 0 : case EditableRole:
307 0 : return pp->isEditable();
308 0 : case GridSizeRole:
309 0 : return pp->scaledGridSize();
310 0 : case DrawGridRole:
311 0 : return pp->drawGrid();
312 0 : case PaletteExpandedRole:
313 0 : return pp->isExpanded();
314 : // TODO showMore?
315 0 : case PaletteTypeRole:
316 0 : return QVariant::fromValue(pp->type());
317 0 : case PaletteContentTypeRole:
318 0 : return QVariant::fromValue(pp->contentType());
319 : }
320 0 : return QVariant();
321 : }
322 :
323 0 : if (PaletteCellConstPtr cell = findCell(index)) {
324 0 : switch (role) {
325 0 : case Qt::DisplayRole:
326 0 : return QVariant(); // Don't show element names in
327 : // item views (i.e. just show icons). If you need
328 : // to know the name, use the ToolTip instead.
329 0 : case Qt::ToolTipRole:
330 0 : return cell->translatedName();
331 0 : case Qt::AccessibleTextRole: {
332 0 : QString name = cell->translatedName();
333 : //ScoreAccessibility::makeReadable(name); //! TODO
334 0 : return name;
335 0 : }
336 0 : case Qt::DecorationRole: {
337 0 : qreal extraMag = 1.0;
338 0 : if (const Palette* pp = iptrToPalette(index.internalPointer())) {
339 0 : extraMag = pp->mag();
340 : }
341 0 : return QIcon(new PaletteCellIconEngine(cell, extraMag * configuration()->paletteScaling()));
342 : }
343 0 : case PaletteCellRole:
344 0 : return QVariant::fromValue(cell.get());
345 0 : case VisibleRole:
346 0 : return cell->visible;
347 0 : case CustomRole:
348 0 : return cell->custom;
349 0 : case EditableRole: {
350 0 : if (const Palette* pp = iptrToPalette(index.internalPointer())) {
351 0 : return pp->isEditable();
352 : }
353 0 : return false;
354 : }
355 0 : case MimeDataRole: {
356 0 : QVariantMap map;
357 0 : if (cell->element) {
358 0 : map[mimeSymbolFormat] = cell->element->mimeData().toQByteArray();
359 : }
360 0 : map[PaletteCell::mimeDataFormat] = cell->toMimeData();
361 0 : return map;
362 0 : }
363 0 : case CellActiveRole:
364 0 : return cell->active;
365 0 : default:
366 0 : break;
367 : }
368 0 : return QVariant();
369 0 : }
370 :
371 : // data for root item
372 0 : switch (role) {
373 0 : case EditableRole:
374 0 : return true;
375 0 : default:
376 0 : break;
377 : }
378 :
379 0 : return QVariant();
380 : }
381 :
382 : //---------------------------------------------------------
383 : // PaletteTreeModel::setData
384 : //---------------------------------------------------------
385 :
386 0 : bool PaletteTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
387 : {
388 0 : if (Palette* pp = findPalette(index)) {
389 0 : switch (role) {
390 0 : case VisibleRole:
391 0 : if (value.canConvert<bool>()) {
392 0 : const bool val = value.toBool();
393 0 : if (val != pp->isVisible()) {
394 0 : pp->setVisible(val);
395 0 : emit dataChanged(index, index, { VisibleRole });
396 : }
397 0 : return true;
398 : }
399 0 : return false;
400 0 : case EditableRole:
401 0 : if (value.canConvert<bool>()) {
402 0 : const bool val = value.toBool();
403 0 : if (val != pp->isEditable()) {
404 0 : pp->setEditable(val);
405 0 : emit dataChanged(index, index, { EditableRole });
406 : // notify cells editability changed too
407 0 : const QModelIndex childFirstIndex = PaletteTreeModel::index(0, 0, index);
408 0 : const int rows = rowCount(index);
409 0 : const QModelIndex childLastIndex = PaletteTreeModel::index(rows - 1, 0, index);
410 0 : emit dataChanged(childFirstIndex, childLastIndex, { EditableRole });
411 : }
412 0 : return true;
413 : }
414 0 : return false;
415 0 : case PaletteExpandedRole:
416 0 : if (value.canConvert<bool>()) {
417 0 : const bool val = value.toBool();
418 0 : if (val != pp->isExpanded()) {
419 0 : const bool singlePalette = configuration()->isSinglePalette().val;
420 :
421 0 : if (singlePalette && val) {
422 0 : for (auto& palette : palettes()) {
423 0 : palette->setExpanded(false);
424 : }
425 0 : pp->setExpanded(val);
426 :
427 0 : const QModelIndex parent = index.parent();
428 0 : const int rows = rowCount(parent);
429 0 : const QModelIndex first = PaletteTreeModel::index(0, 0, parent);
430 0 : const QModelIndex last = PaletteTreeModel::index(rows - 1, 0, parent);
431 0 : emit dataChanged(first, last, { PaletteExpandedRole });
432 0 : } else {
433 0 : pp->setExpanded(val);
434 0 : emit dataChanged(index, index, { PaletteExpandedRole });
435 : }
436 : }
437 0 : return true;
438 : }
439 0 : return false;
440 0 : case Qt::DisplayRole:
441 0 : pp->setName(value.toString());
442 0 : emit dataChanged(index, index, { Qt::DisplayRole, Qt::AccessibleTextRole });
443 0 : return true;
444 : // case CustomRole:
445 : // if (value.canConvert<bool>()) {
446 : // const bool val = value.toBool();
447 : // if (val != pp->custom()) {
448 : // pp->setCustom(val);
449 : // emit dataChanged(index, index, { CustomRole });
450 : // }
451 : // return true;
452 : // }
453 : // return false;
454 : // case gridSizeRole:
455 : // return pp->gridSize();
456 : // case drawGridRole:
457 : // return pp->drawGrid();
458 0 : default:
459 0 : break;
460 : }
461 0 : return false;
462 : }
463 :
464 0 : if (PaletteCellPtr cell = findCell(index)) {
465 0 : switch (role) {
466 : // case Qt::DisplayRole:
467 : // case Qt::ToolTipRole:
468 : // or EditRole?
469 : // return cell->name;
470 0 : case PaletteCellRole: {
471 0 : PaletteCell* newCell = value.value<PaletteCell*>();
472 0 : if (!newCell) {
473 0 : return false;
474 : }
475 0 : cell->element = newCell->element;
476 0 : cell->untranslatedElement = newCell->untranslatedElement;
477 0 : cell->name = newCell->name;
478 0 : cell->id = newCell->id;
479 0 : emit dataChanged(index, index);
480 0 : return true;
481 : };
482 0 : case VisibleRole:
483 0 : if (value.canConvert<bool>()) {
484 0 : const bool val = value.toBool();
485 0 : if (val != cell->visible) {
486 0 : cell->visible = val;
487 0 : emit dataChanged(index, index, { VisibleRole });
488 : }
489 0 : return true;
490 : }
491 0 : return false;
492 0 : case CustomRole:
493 0 : if (value.canConvert<bool>()) {
494 0 : const bool val = value.toBool();
495 0 : if (val != cell->custom) {
496 0 : cell->custom = val;
497 0 : emit dataChanged(index, index, { CustomRole });
498 : }
499 0 : return true;
500 : }
501 0 : return false;
502 0 : case MimeDataRole: {
503 0 : const QVariantMap map = value.toMap();
504 :
505 0 : if (map.contains(PaletteCell::mimeDataFormat)) {
506 0 : const QByteArray cellMimeData = map[PaletteCell::mimeDataFormat].toByteArray();
507 0 : PaletteCellPtr newCell(PaletteCell::fromMimeData(cellMimeData, iocContext()));
508 0 : if (!newCell) {
509 0 : return false;
510 : }
511 :
512 0 : cell->element = newCell->element;
513 0 : cell->untranslatedElement = newCell->untranslatedElement;
514 0 : cell->name = newCell->name;
515 0 : cell->id = newCell->id;
516 0 : } else if (map.contains(mimeSymbolFormat)) {
517 0 : const QByteArray elementMimeData = map[mimeSymbolFormat].toByteArray();
518 0 : PaletteCellPtr newCell = PaletteCell::fromElementMimeData(elementMimeData, iocContext());
519 :
520 0 : cell->element = newCell->element;
521 0 : cell->untranslatedElement = newCell->untranslatedElement;
522 0 : cell->name = newCell->name;
523 0 : cell->id = newCell->id;
524 :
525 0 : cell->custom = true; // mark the updated cell custom
526 0 : } else {
527 0 : return false;
528 : }
529 :
530 0 : emit dataChanged(index, index);
531 0 : return true;
532 0 : }
533 0 : default:
534 0 : break;
535 : }
536 0 : return false;
537 0 : }
538 :
539 0 : return false;
540 : }
541 :
542 : //---------------------------------------------------------
543 : // PaletteTreeModel::roleNames
544 : //---------------------------------------------------------
545 :
546 0 : QHash<int, QByteArray> PaletteTreeModel::roleNames() const
547 : {
548 0 : QHash<int, QByteArray> roles(QAbstractItemModel::roleNames());
549 0 : roles[MimeDataRole] = "mimeData";
550 0 : roles[GridSizeRole] = "gridSize";
551 0 : roles[DrawGridRole] = "drawGrid";
552 0 : roles[CustomRole] = "custom";
553 0 : roles[EditableRole] = "editable";
554 0 : roles[PaletteExpandedRole] = "expanded";
555 0 : roles[CellActiveRole] = "cellActive";
556 0 : roles[Qt::AccessibleTextRole] = "accessibleText";
557 0 : return roles;
558 0 : }
559 :
560 : //---------------------------------------------------------
561 : // PaletteTreeModel::flags
562 : //---------------------------------------------------------
563 :
564 0 : Qt::ItemFlags PaletteTreeModel::flags(const QModelIndex& index) const
565 : {
566 0 : return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled;
567 : }
568 :
569 : //---------------------------------------------------------
570 : // PaletteTreeModel::supportedDropActions
571 : //---------------------------------------------------------
572 :
573 0 : Qt::DropActions PaletteTreeModel::supportedDropActions() const
574 : {
575 0 : return Qt::DropActions(Qt::CopyAction | Qt::MoveAction);
576 : }
577 :
578 : //---------------------------------------------------------
579 : // PaletteTreeModel::mimeData
580 : //---------------------------------------------------------
581 :
582 0 : QMimeData* PaletteTreeModel::mimeData(const QModelIndexList& indexes) const
583 : {
584 0 : QMimeData* mime = QAbstractItemModel::mimeData(indexes); // TODO: needed or use only "our" MIME data?
585 :
586 0 : if (indexes.empty() || indexes.size() > 1) {
587 0 : return mime;
588 : }
589 :
590 0 : if (const Palette* pp = findPalette(indexes[0])) {
591 0 : mime->setData(Palette::mimeDataFormat, pp->toMimeData());
592 0 : } else if (PaletteCellConstPtr cell = findCell(indexes[0])) {
593 0 : mime->setData(mimeSymbolFormat, cell->element->mimeData().toQByteArray());
594 0 : }
595 :
596 0 : return mime;
597 : }
598 :
599 : //---------------------------------------------------------
600 : // PaletteTreeModel::mimeTypes
601 : //---------------------------------------------------------
602 :
603 0 : QStringList PaletteTreeModel::mimeTypes() const
604 : {
605 0 : QStringList types = QAbstractItemModel::mimeTypes();
606 0 : types << mimeSymbolFormat;
607 0 : return types;
608 0 : }
609 :
610 : //---------------------------------------------------------
611 : // PaletteTreeModel::canDropMimeData
612 : //---------------------------------------------------------
613 :
614 0 : bool PaletteTreeModel::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
615 : const QModelIndex& parent) const
616 : {
617 : Q_UNUSED(column);
618 :
619 0 : if (!parent.isValid()) {
620 0 : return (action == Qt::CopyAction) && data->hasFormat(Palette::mimeDataFormat);
621 : }
622 :
623 0 : if (const Palette* pp = findPalette(parent)) {
624 0 : if (row < 0 || row > int(pp->cellsCount())) {
625 0 : return false;
626 : }
627 :
628 0 : if (data->hasFormat(PaletteCell::mimeDataFormat)) {
629 0 : return action & (Qt::CopyAction | Qt::MoveAction);
630 0 : } else if (data->hasFormat(mimeSymbolFormat)) {
631 0 : return action == Qt::CopyAction;
632 : }
633 : }
634 0 : return false;
635 : }
636 :
637 : //---------------------------------------------------------
638 : // PaletteTreeModel::dropMimeData
639 : //---------------------------------------------------------
640 :
641 0 : bool PaletteTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
642 : const QModelIndex& parent)
643 : {
644 : Q_UNUSED(column); // when dropping at the end of palette, column == -1. Probably an effect of proxy models
645 :
646 0 : if (!parent.isValid()) {
647 0 : if (action != Qt::CopyAction || !data->hasFormat(Palette::mimeDataFormat)) {
648 0 : return false;
649 : }
650 0 : if (row < 0 || row > int(palettes().size())) {
651 0 : return false;
652 : }
653 :
654 0 : auto palette = Palette::fromMimeData(data->data(Palette::mimeDataFormat), iocContext());
655 0 : if (!palette) {
656 0 : return false;
657 : }
658 :
659 0 : beginInsertRows(parent, row, row);
660 0 : palettes().insert(palettes().begin() + row, palette);
661 0 : endInsertRows();
662 0 : return true;
663 0 : }
664 :
665 0 : if (Palette* pp = findPalette(parent)) {
666 0 : if (row < 0 || row > int(pp->cellsCount())) {
667 0 : return false;
668 : }
669 :
670 0 : PaletteCellPtr cell;
671 :
672 0 : if (data->hasFormat(PaletteCell::mimeDataFormat)) {
673 0 : cell = PaletteCell::fromMimeData(data->data(PaletteCell::mimeDataFormat), iocContext());
674 0 : if (action == Qt::CopyAction) {
675 0 : cell->custom = true;
676 : }
677 0 : } else if (data->hasFormat(mimeSymbolFormat)) {
678 0 : cell = PaletteCell::fromElementMimeData(data->data(mimeSymbolFormat), iocContext());
679 0 : cell->custom = true; // the cell is created by dropping an element so it is custom
680 : }
681 :
682 0 : if (!cell) {
683 0 : return false;
684 : }
685 :
686 0 : beginInsertRows(parent, row, row);
687 0 : pp->insertCell(row, cell);
688 0 : endInsertRows();
689 0 : return true;
690 0 : }
691 0 : return false;
692 : }
693 :
694 : //---------------------------------------------------------
695 : // PaletteTreeModel::moveRows
696 : //---------------------------------------------------------
697 :
698 0 : bool PaletteTreeModel::moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
699 : const QModelIndex& destinationParent, int destinationChild)
700 : {
701 0 : const bool sameParent = sourceParent == destinationParent;
702 :
703 0 : if (!sourceParent.isValid()) {
704 : // moving palettes
705 0 : if (sourceRow + count > int(palettes().size()) || destinationChild >= int(palettes().size())) {
706 0 : return false;
707 : }
708 :
709 0 : if (!sameParent) {
710 : // Cannot move between different parents, at least for now
711 : // TODO: reconsider?
712 0 : return false;
713 : }
714 :
715 : // The moved rows are considered to be inserted *before* destinationRow,
716 : // so if we want to move a row down in the list within the same parent
717 : // then we should increment the destinationChild index.
718 0 : const int destinationRow = (destinationChild >= sourceRow) ? destinationChild + 1 : destinationChild;
719 :
720 0 : if (sameParent && sourceRow == destinationRow) {
721 0 : return false;
722 : }
723 :
724 0 : std::vector<PalettePtr> movedPalettes;
725 :
726 0 : auto srcBegin = palettes().begin() + sourceRow;
727 0 : auto srcEnd = srcBegin + count;
728 :
729 : // As of Qt 5.12, Qt proxy models rebuild the entire index mapping if
730 : // layoutChanged() gets emitted (i.e. if begin/endMoveRows() gets called).
731 : // Performance is much better when doing remove + insert rows instead.
732 0 : beginRemoveRows(sourceParent, sourceRow, sourceRow + count - 1);
733 0 : movedPalettes.reserve(count);
734 0 : movedPalettes.insert(movedPalettes.end(), std::make_move_iterator(srcBegin), std::make_move_iterator(srcEnd));
735 0 : palettes().erase(srcBegin, srcEnd);
736 0 : endRemoveRows();
737 :
738 0 : const int destIdx = (destinationRow < sourceRow) ? destinationRow : (destinationRow - count);
739 0 : auto dest = palettes().begin() + destIdx;
740 :
741 0 : beginInsertRows(destinationParent, destIdx, destIdx + count - 1);
742 0 : palettes().insert(dest, std::make_move_iterator(movedPalettes.begin()),
743 : std::make_move_iterator(movedPalettes.end()));
744 0 : endInsertRows();
745 :
746 0 : return true;
747 0 : }
748 :
749 0 : Palette* sourcePalette = findPalette(sourceParent);
750 0 : Palette* destPalette = sameParent ? sourcePalette : findPalette(destinationParent);
751 0 : if (sourcePalette && destPalette) {
752 : // moving palette cells
753 0 : if (sourceRow + count > int(sourcePalette->cellsCount()) || destinationChild > int(destPalette->cellsCount())) {
754 0 : return false;
755 : }
756 :
757 : // The moved rows are considered to be inserted *before* destinationRow,
758 : // so if we want to move a row down in the list within the same parent
759 : // then we should increment the destinationChild index.
760 0 : const int destinationRow
761 0 : = (sameParent && destinationChild >= sourceRow) ? destinationChild + 1 : destinationChild;
762 :
763 0 : if (sameParent && sourceRow == destinationRow) {
764 0 : return false;
765 : }
766 :
767 : // As of Qt 5.12, Qt proxy models rebuild the entire index mapping if
768 : // layoutChanged() gets emitted (i.e. if begin/endMoveRows() gets called).
769 : // Performance is much better when doing remove + insert rows instead.
770 0 : beginRemoveRows(sourceParent, sourceRow, sourceRow + count - 1);
771 0 : auto movedCells(sourcePalette->takeCells(sourceRow, count));
772 0 : endRemoveRows();
773 :
774 0 : const int destIdx = (sameParent && destinationRow >= sourceRow) ? (destinationRow - count) : destinationRow;
775 :
776 0 : beginInsertRows(destinationParent, destIdx, destIdx + count - 1);
777 0 : destPalette->insertCells(destIdx, movedCells);
778 0 : endInsertRows();
779 :
780 0 : return true;
781 0 : }
782 :
783 0 : return false;
784 : }
785 :
786 : //---------------------------------------------------------
787 : // PaletteTreeModel::removeRows
788 : //---------------------------------------------------------
789 :
790 0 : bool PaletteTreeModel::removeRows(int row, int count, const QModelIndex& parent)
791 : {
792 0 : if (!parent.isValid()) {
793 : // removing palettes
794 0 : if (row < 0 || row + count > int(palettes().size())) {
795 0 : return false;
796 : }
797 :
798 0 : beginRemoveRows(parent, row, row + count - 1);
799 0 : auto rangeBegin = palettes().begin() + row;
800 0 : auto rangeEnd = rangeBegin + count;
801 0 : palettes().erase(rangeBegin, rangeEnd);
802 0 : endRemoveRows();
803 0 : return true;
804 : }
805 :
806 0 : if (Palette* palette = findPalette(parent)) {
807 : // removing palette cells
808 0 : if (row < 0 || row + count > palette->cellsCount()) {
809 0 : return false;
810 : }
811 :
812 0 : beginRemoveRows(parent, row, row + count - 1);
813 0 : palette->takeCells(row, count);
814 0 : endRemoveRows();
815 0 : return true;
816 : }
817 :
818 0 : return false;
819 : }
820 :
821 : //---------------------------------------------------------
822 : // PaletteTreeModel::insertRows
823 : //---------------------------------------------------------
824 :
825 0 : bool PaletteTreeModel::insertRows(int row, int count, const QModelIndex& parent)
826 : {
827 0 : if (!parent.isValid()) {
828 : // inserting palettes
829 0 : if (row < 0 || row > int(palettes().size())) {
830 0 : return false;
831 : }
832 :
833 0 : beginInsertRows(parent, row, row + count - 1);
834 0 : for (int i = 0; i < count; ++i) {
835 0 : PalettePtr p = std::make_shared<Palette>(iocContext(), Palette::Type::Custom);
836 0 : p->setName(QT_TRANSLATE_NOOP("palette", "Untitled palette"));
837 0 : p->setGridSize(QSize(48, 48));
838 0 : p->setExpanded(true);
839 0 : palettes().insert(palettes().begin() + row, p);
840 0 : }
841 0 : endInsertRows();
842 0 : return true;
843 : }
844 :
845 0 : if (Palette* palette = findPalette(parent)) {
846 : // inserting palette cells
847 0 : if (row < 0 || row > palette->cellsCount()) {
848 0 : return false;
849 : }
850 :
851 0 : beginInsertRows(parent, row, row + count - 1);
852 0 : for (int i = 0; i < count; ++i) {
853 0 : PaletteCellPtr cell = std::make_shared<PaletteCell>(iocContext(), palette);
854 0 : palette->insertCell(row, cell);
855 0 : }
856 0 : endInsertRows();
857 0 : return true;
858 : }
859 :
860 0 : return false;
861 : }
862 :
863 : //---------------------------------------------------------
864 : // PaletteTreeModel::insertPalette
865 : //---------------------------------------------------------
866 :
867 0 : bool PaletteTreeModel::insertPalette(PalettePtr pp, int row, const QModelIndex& parent)
868 : {
869 0 : if (row < 0 || row > int(palettes().size()) || parent != QModelIndex()) {
870 0 : return false;
871 : }
872 0 : beginInsertRows(parent, row, row);
873 0 : palettes().insert(palettes().begin() + row, pp);
874 0 : endInsertRows();
875 0 : return true;
876 : }
877 :
878 : //---------------------------------------------------------
879 : // PaletteTreeModel::updateCellsState
880 : //---------------------------------------------------------
881 :
882 0 : void PaletteTreeModel::updateCellsState(const Selection& sel)
883 : {
884 0 : const ChordRest* cr = sel.firstChordRest();
885 0 : const BeamMode bm = cr ? cr->beamMode() : BeamMode::NONE;
886 0 : const ActionIconType beamActionType = Beam::actionIconTypeForBeamMode(bm);
887 0 : bool deactivateAll = !cr;
888 :
889 0 : for (EngravingItem* e : sel.elements()) {
890 0 : if (e->isNote()) {
891 0 : e = toNote(e)->chord();
892 : }
893 0 : if (e->isChordRest()) {
894 0 : if (toChordRest(e)->beamMode() != bm) {
895 0 : deactivateAll = true;
896 : }
897 : }
898 : }
899 :
900 0 : const size_t npalettes = palettes().size();
901 0 : for (size_t row = 0; row < npalettes; ++row) {
902 0 : Palette* palette = palettes()[row].get();
903 : // TODO: should this be turned on for all palettes?
904 0 : if (palette->type() != Palette::Type::Beam) {
905 0 : continue;
906 : }
907 :
908 0 : for (int ci = 0; ci < palette->cellsCount(); ++ci) {
909 0 : PaletteCellPtr cell = palette->cellAt(ci);
910 0 : if (deactivateAll) {
911 0 : cell->active = false;
912 0 : } else if (cell->element && cell->element->isActionIcon()) {
913 0 : const ActionIcon* action = toActionIcon(cell->element.get());
914 0 : cell->active = (action->actionType() == beamActionType);
915 : }
916 0 : }
917 : }
918 :
919 0 : notifyAboutCellsChanged(CellActiveRole);
920 0 : }
921 :
922 : //---------------------------------------------------------
923 : // PaletteTreeModel::retranslate
924 : //---------------------------------------------------------
925 :
926 0 : void PaletteTreeModel::retranslate()
927 : {
928 0 : _paletteTree->retranslate();
929 0 : }
930 :
931 : //---------------------------------------------------------
932 : // PaletteTreeModel::findPaletteCell
933 : //---------------------------------------------------------
934 :
935 0 : QModelIndex PaletteTreeModel::findPaletteCell(const PaletteCell& cell, const QModelIndex& parent) const
936 : {
937 0 : if (const Palette* pp = findPalette(parent)) {
938 0 : const int idx = pp->indexOfCell(cell);
939 0 : if (idx == -1) {
940 0 : return QModelIndex();
941 : }
942 0 : return index(idx, /* column */ 0, parent);
943 : }
944 0 : return QModelIndex();
945 : }
946 :
947 : //---------------------------------------------------------
948 : // PaletteTreeModel::match
949 : /// Currently only searching for a given palette cell is
950 : /// implemented, for anything else the corresponding
951 : /// member function of QAbstractItemModel is used.
952 : //---------------------------------------------------------
953 :
954 0 : QModelIndexList PaletteTreeModel::match(const QModelIndex& start, int role, const QVariant& value, int hits,
955 : Qt::MatchFlags flags) const
956 : {
957 0 : if (role != PaletteCellRole || flags != Qt::MatchExactly || hits != 1
958 0 : || !value.canConvert<const PaletteCell*>()
959 0 : || !findPalette(start.parent())
960 : ) {
961 0 : return QAbstractItemModel::match(start, role, value, hits, flags);
962 : }
963 :
964 0 : const PaletteCell* cell = value.value<const PaletteCell*>();
965 0 : if (!cell) {
966 0 : return QModelIndexList();
967 : }
968 :
969 0 : return QModelIndexList({ findPaletteCell(*cell, start.parent()) });
970 : }
971 :
972 : //---------------------------------------------------------
973 : // PaletteTreeModel::itemDataChanged
974 : //---------------------------------------------------------
975 :
976 0 : void PaletteTreeModel::itemDataChanged(const QModelIndex& idx)
977 : {
978 0 : emit dataChanged(idx, idx);
979 0 : if (findPalette(idx)) {
980 : // palette cells appearance depends on palette settings
981 0 : const QModelIndex childFirstIndex = index(0, 0, idx);
982 0 : const int rows = rowCount(idx);
983 0 : const QModelIndex childLastIndex = index(rows - 1, 0, idx);
984 0 : emit dataChanged(childFirstIndex, childLastIndex, { Qt::DecorationRole });
985 : }
986 0 : }
987 :
988 : //---------------------------------------------------------
989 : // PaletteCellFilter::addChainedFilter
990 : /// Ownership over the added filter is passed to this
991 : /// filter.
992 : //---------------------------------------------------------
993 :
994 0 : void PaletteCellFilter::addChainedFilter(PaletteCellFilter* newFilter)
995 : {
996 0 : PaletteCellFilter* f = this;
997 0 : while (f->chainedFilter) {
998 0 : f = f->chainedFilter;
999 : }
1000 :
1001 0 : newFilter->setParent(f);
1002 0 : f->chainedFilter = newFilter;
1003 0 : connect(newFilter, &PaletteCellFilter::filterChanged, f, &PaletteCellFilter::filterChanged);
1004 0 : }
1005 :
1006 : //---------------------------------------------------------
1007 : // PaletteCellFilter::accept
1008 : //---------------------------------------------------------
1009 :
1010 0 : bool PaletteCellFilter::accept(const PaletteCell& cell) const
1011 : {
1012 0 : const PaletteCellFilter* f = this;
1013 0 : while (f) {
1014 0 : if (!f->acceptCell(cell)) {
1015 0 : return false;
1016 : }
1017 0 : f = f->chainedFilter;
1018 : }
1019 0 : return true;
1020 : }
1021 :
1022 : //---------------------------------------------------------
1023 : // PaletteCellFilter::connectToModel
1024 : //---------------------------------------------------------
1025 :
1026 0 : void PaletteCellFilter::connectToModel(const QAbstractItemModel* model)
1027 : {
1028 : // TODO: are all these needed?
1029 : // columnsInserted(const QModelIndex &parent, int first, int last)
1030 : // columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column)
1031 : // columnsRemoved(const QModelIndex &parent, int first, int last)
1032 0 : connect(model, &QAbstractItemModel::dataChanged, this, &PaletteCellFilter::filterChanged);
1033 : // dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = ...)
1034 0 : connect(model, &QAbstractItemModel::layoutChanged, this, &PaletteCellFilter::filterChanged);
1035 : // layoutChanged(const QList<QPersistentModelIndex> &parents = ..., QAbstractItemModel::LayoutChangeHint hint = ...)
1036 0 : connect(model, &QAbstractItemModel::modelReset, this, &PaletteCellFilter::filterChanged);
1037 : // modelReset()
1038 0 : connect(model, &QAbstractItemModel::rowsInserted, this, &PaletteCellFilter::filterChanged);
1039 : // rowsInserted(const QModelIndex &parent, int first, int last)
1040 0 : connect(model, &QAbstractItemModel::rowsMoved, this, &PaletteCellFilter::filterChanged);
1041 : // rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
1042 0 : connect(model, &QAbstractItemModel::rowsRemoved, this, &PaletteCellFilter::filterChanged);
1043 : // rowsRemoved(const QModelIndex &parent, int first, int last)
1044 0 : }
1045 :
1046 : //---------------------------------------------------------
1047 : // ExcludePaletteCellFilter
1048 : //---------------------------------------------------------
1049 :
1050 : class ExcludePaletteCellFilter : public PaletteCellFilter
1051 : {
1052 : const Palette* excludePalette;
1053 : const QPersistentModelIndex paletteIndex; // filter is valid as long as this index is valid too
1054 :
1055 : public:
1056 0 : ExcludePaletteCellFilter(const Palette* p, QPersistentModelIndex index, QObject* parent = nullptr)
1057 0 : : PaletteCellFilter(parent), excludePalette(p), paletteIndex(index) {}
1058 :
1059 0 : bool acceptCell(const PaletteCell& cell) const override
1060 : {
1061 0 : return paletteIndex.isValid() && -1 == excludePalette->indexOfCell(cell, /* matchName */ false);
1062 : }
1063 : };
1064 :
1065 : //---------------------------------------------------------
1066 : // PaletteTreeModel::getFilter
1067 : /// The ownership of the returned filter is passed to a
1068 : /// caller.
1069 : //---------------------------------------------------------
1070 :
1071 0 : PaletteCellFilter* PaletteTreeModel::getFilter(const QModelIndex& index) const
1072 : {
1073 0 : if (const Palette* pp = findPalette(index)) {
1074 0 : ExcludePaletteCellFilter* filter = new ExcludePaletteCellFilter(pp, index);
1075 0 : filter->connectToModel(this);
1076 0 : return filter;
1077 : }
1078 :
1079 : // TODO: make a filter for a single cell?
1080 :
1081 0 : return nullptr;
1082 : }
1083 :
1084 : //---------------------------------------------------------
1085 : // FilterPaletteTreeModel::FilterPaletteTreeModel
1086 : //---------------------------------------------------------
1087 :
1088 0 : FilterPaletteTreeModel::FilterPaletteTreeModel(PaletteCellFilter* filter, PaletteTreeModel* model, QObject* parent)
1089 0 : : QSortFilterProxyModel(parent), cellFilter(filter)
1090 : {
1091 0 : cellFilter->setParent(this);
1092 : // connect(cellFilter, &PaletteCellFilter::filterChanged, this, &QSortFilterProxyModel::invalidate);
1093 0 : connect(cellFilter, &PaletteCellFilter::filterChanged, this, &FilterPaletteTreeModel::invalidateFilter);
1094 :
1095 0 : setSourceModel(model);
1096 0 : }
1097 :
1098 : //---------------------------------------------------------
1099 : // FilterPaletteTreeModel::filterAcceptsRow
1100 : //---------------------------------------------------------
1101 :
1102 0 : bool FilterPaletteTreeModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
1103 : {
1104 0 : const QModelIndex& cellIndex = sourceModel()->index(sourceRow, /* column */ 0, sourceParent);
1105 0 : const QVariant cellData = sourceModel()->data(cellIndex, PaletteTreeModel::PaletteCellRole);
1106 0 : const PaletteCell* cell = cellData.value<const PaletteCell*>();
1107 0 : if (!cell) { // a palette or just an unrelated model
1108 0 : return true;
1109 : }
1110 0 : return cellFilter->accept(*cell);
1111 0 : }
1112 :
1113 : //---------------------------------------------------------
1114 : // PaletteCellFilterProxyModel::PaletteCellFilterProxyModel
1115 : //---------------------------------------------------------
1116 :
1117 0 : PaletteCellFilterProxyModel::PaletteCellFilterProxyModel(QObject* parent)
1118 0 : : QSortFilterProxyModel(parent)
1119 : {
1120 0 : setFilterRole(Qt::ToolTipRole); // palette cells have no data for DisplayRole
1121 0 : }
1122 :
1123 : //---------------------------------------------------------
1124 : // PaletteCellFilterProxyModel::filterAcceptsRow
1125 : //---------------------------------------------------------
1126 :
1127 0 : bool PaletteCellFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
1128 : {
1129 0 : const QAbstractItemModel* model = sourceModel();
1130 0 : const QModelIndex rowIndex = model->index(sourceRow, 0, sourceParent);
1131 0 : const int rowCount = model->rowCount(rowIndex);
1132 :
1133 0 : if (rowCount == 0) {
1134 0 : if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) {
1135 0 : return true;
1136 : }
1137 : // accept row if its parent is accepted by filter: necessary to be able to search by palette name
1138 0 : if (sourceParent.isValid()
1139 0 : && QSortFilterProxyModel::filterAcceptsRow(sourceParent.row(), sourceParent.parent())) {
1140 0 : return true;
1141 : }
1142 0 : return false;
1143 : }
1144 :
1145 0 : for (int i = 0; i < rowCount; ++i) {
1146 0 : if (filterAcceptsRow(i, rowIndex)) {
1147 0 : return true;
1148 : }
1149 : }
1150 :
1151 0 : return false;
1152 : }
|