1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2019 Werner Schweer and others
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19
20 #include "paletteworkspace.h"
21
22 #include "libmscore/keysig.h"
23 #include "libmscore/timesig.h"
24
25 #include "createpalettedialog.h"
26 #include "keyedit.h"
27 #include "musescore.h"
28 #include "palette.h" // applyPaletteElement
29 #include "palettedialog.h"
30 #include "palettecelldialog.h"
31 #include "palettewidget.h"
32 #include "timedialog.h"
33
34 namespace Ms {
35
36 //---------------------------------------------------------
37 // PaletteElementEditor::valid
38 //---------------------------------------------------------
39
valid() const40 bool PaletteElementEditor::valid() const
41 {
42 using Type = PalettePanel::Type;
43 switch (_type) {
44 case Type::KeySig:
45 case Type::TimeSig:
46 return true;
47 default:
48 break;
49 }
50 return false;
51 }
52
53 //---------------------------------------------------------
54 // PaletteElementEditor::actionName
55 //---------------------------------------------------------
56
actionName() const57 QString PaletteElementEditor::actionName() const
58 {
59 using Type = PalettePanel::Type;
60 switch (_type) {
61 case Type::KeySig:
62 return tr("Create Key Signature");
63 case Type::TimeSig:
64 return tr("Create Time Signature");
65 default:
66 break;
67 }
68 return QString();
69 }
70
71 //---------------------------------------------------------
72 // PaletteElementEditor::onElementAdded
73 //---------------------------------------------------------
74
onElementAdded(const Element * el)75 void PaletteElementEditor::onElementAdded(const Element* el)
76 {
77 if (!_paletteIndex.isValid()
78 || !_paletteIndex.data(PaletteTreeModel::VisibleRole).toBool()) {
79 QMessageBox::information(mscore, "", tr("The palette was hidden or changed"));
80 return;
81 }
82 QVariantMap mimeData;
83 mimeData[mimeSymbolFormat] = el->mimeData(QPointF());
84 _controller->insert(_paletteIndex, -1, mimeData, Qt::CopyAction);
85 }
86
87 //---------------------------------------------------------
88 // PaletteElementEditor::open
89 //---------------------------------------------------------
90
open()91 void PaletteElementEditor::open()
92 {
93 if (!_paletteIndex.isValid())
94 return;
95
96 QWidget* editor = nullptr;
97
98 using Type = PalettePanel::Type;
99 switch (_type) {
100 case Type::KeySig: {
101 KeyEditor* keyEditor = new KeyEditor(mscore);
102 keyEditor->showKeyPalette(false);
103 connect(keyEditor, &KeyEditor::keySigAdded, this, &PaletteElementEditor::onElementAdded);
104 editor = keyEditor;
105 }
106 break;
107 case Type::TimeSig: {
108 TimeDialog* timeEditor = new TimeDialog(mscore);
109 timeEditor->showTimePalette(false);
110 connect(timeEditor, &TimeDialog::timeSigAdded, this, &PaletteElementEditor::onElementAdded);
111 editor = timeEditor;
112 }
113 break;
114 default:
115 break;
116 }
117
118 if (!editor)
119 return;
120
121 mscore->stackUnder(editor);
122 editor->setAttribute(Qt::WA_DeleteOnClose);
123
124 editor->show();
125 }
126
127 //---------------------------------------------------------
128 // findPaletteIndex
129 //---------------------------------------------------------
130
findPaletteIndex(const QAbstractItemModel * model,PalettePanel::Type type)131 static QModelIndex findPaletteIndex(const QAbstractItemModel* model, PalettePanel::Type type)
132 {
133 constexpr int role = PaletteTreeModel::PaletteTypeRole;
134 const QModelIndex start = model->index(0, 0);
135 const QModelIndexList foundIndexList = model->match(start, role, QVariant::fromValue(type));
136
137 if (!foundIndexList.empty())
138 return foundIndexList[0];
139 return QModelIndex();
140 }
141
142 //---------------------------------------------------------
143 // convertIndex
144 //---------------------------------------------------------
145
convertIndex(const QModelIndex & index,const QAbstractItemModel * targetModel)146 static QModelIndex convertIndex(const QModelIndex& index, const QAbstractItemModel* targetModel)
147 {
148 if (index.model() == targetModel || !index.isValid())
149 return index;
150
151 constexpr int typeRole = PaletteTreeModel::PaletteTypeRole;
152 const auto type = index.model()->data(index, typeRole).value<PalettePanel::Type>();
153
154 return findPaletteIndex(targetModel, type);
155 }
156
157 //---------------------------------------------------------
158 // convertProxyIndex
159 //---------------------------------------------------------
160
convertProxyIndex(const QModelIndex & srcIndex,const QAbstractItemModel * targetModel)161 static QModelIndex convertProxyIndex(const QModelIndex& srcIndex, const QAbstractItemModel* targetModel)
162 {
163 QModelIndex index = srcIndex;
164 while (index.model() != targetModel) {
165 if (auto m = qobject_cast<const QAbstractProxyModel*>(index.model()))
166 index = m->mapToSource(index);
167 else
168 break;
169 }
170
171 if (targetModel && index.model() != targetModel)
172 return QModelIndex();
173 return index;
174 }
175
176 //---------------------------------------------------------
177 // AbstractPaletteController::elementEditor
178 //---------------------------------------------------------
179
elementEditor(const QModelIndex & paletteIndex)180 PaletteElementEditor* AbstractPaletteController::elementEditor(const QModelIndex& paletteIndex)
181 {
182 PaletteElementEditor* ed = new PaletteElementEditor(this, paletteIndex, paletteIndex.data(PaletteTreeModel::PaletteTypeRole).value<PalettePanel::Type>(), this);
183 QQmlEngine::setObjectOwnership(ed, QQmlEngine::JavaScriptOwnership);
184 return ed;
185 }
186
187 //---------------------------------------------------------
188 // UserPaletteController::dropAction
189 //---------------------------------------------------------
190
dropAction(const QVariantMap & mimeData,Qt::DropAction proposedAction,const QModelIndex & parent,bool internal) const191 Qt::DropAction UserPaletteController::dropAction(const QVariantMap& mimeData, Qt::DropAction proposedAction, const QModelIndex& parent, bool internal) const
192 {
193 if (internal && !userEditable())
194 return Qt::IgnoreAction;
195 const bool parentEditingEnabled = model()->data(parent, PaletteTreeModel::EditableRole).toBool();
196 if (!parentEditingEnabled)
197 return Qt::IgnoreAction;
198
199 if (mimeData.contains(PaletteCell::mimeDataFormat) && proposedAction == Qt::MoveAction) {
200 const auto cell = PaletteCell::readMimeData(mimeData[PaletteCell::mimeDataFormat].toByteArray());
201 if (!cell)
202 return Qt::IgnoreAction;
203 if (_filterCustom && cell->custom != _custom)
204 return Qt::IgnoreAction;
205 return Qt::MoveAction;
206 }
207 if (mimeData.contains(mimeSymbolFormat) && proposedAction == Qt::CopyAction) {
208 if (_filterCustom && !_custom)
209 return Qt::IgnoreAction;
210 return Qt::CopyAction;
211 }
212 return Qt::IgnoreAction;
213 }
214
215 //---------------------------------------------------------
216 // UserPaletteController::insert
217 //---------------------------------------------------------
218
insert(const QModelIndex & parent,int row,const QVariantMap & mimeData,Qt::DropAction action)219 bool UserPaletteController::insert(const QModelIndex& parent, int row, const QVariantMap& mimeData, Qt::DropAction action)
220 {
221 if (dropAction(mimeData, action, parent, false) == Qt::IgnoreAction)
222 return false;
223
224 if (row < 0)
225 row = parent.model()->rowCount(parent);
226
227 PaletteCellPtr cell;
228
229 if (mimeData.contains(PaletteCell::mimeDataFormat)) {
230 cell = PaletteCell::readMimeData(mimeData[PaletteCell::mimeDataFormat].toByteArray());
231
232 if (!cell)
233 return false;
234 if (_filterCustom && cell->custom != _custom)
235 return false;
236
237 if (action == Qt::MoveAction) {
238 const QModelIndex visiblePaletteParentIndex = convertIndex(parent, _userPalette);
239 const QModelIndex foundIndex(_userPalette->findPaletteCell(*cell, visiblePaletteParentIndex));
240 if (foundIndex.isValid())
241 return _userPalette->setData(foundIndex, _visible, PaletteTreeModel::VisibleRole);
242 else if (!userEditable())
243 return false;
244 }
245 }
246 else if (mimeData.contains(mimeSymbolFormat) && (action == Qt::CopyAction))
247 cell = PaletteCell::readElementMimeData(mimeData[mimeSymbolFormat].toByteArray());
248
249 if (!cell)
250 return false;
251
252 if (_filterCustom) {
253 if (!_custom)
254 return false; // can only move non-custom cells
255 cell->custom = _custom;
256 }
257 cell->visible = _visible;
258
259 QMimeData data;
260 data.setData(PaletteCell::mimeDataFormat, cell->mimeData());
261 constexpr int column = 0;
262 return model()->dropMimeData(&data, action, row, column, parent);
263 }
264
265 //---------------------------------------------------------
266 // UserPaletteController::insertNewItem
267 //---------------------------------------------------------
268
insertNewItem(const QModelIndex & parent,int row)269 bool UserPaletteController::insertNewItem(const QModelIndex& parent, int row)
270 {
271 if (!canEdit(parent))
272 return false;
273
274 const bool newItemIsPalette = !parent.isValid();
275
276 if (newItemIsPalette) {
277 CreatePaletteDialog dlg(mscore);
278 const int result = dlg.exec();
279 if (result == QDialog::Rejected || dlg.paletteName().isEmpty())
280 return false;
281
282 if (!model()->insertRow(row, parent))
283 return false;
284 model()->setData(model()->index(row, 0, parent), dlg.paletteName(), Qt::DisplayRole);
285 return true;
286 }
287
288 return model()->insertRow(row, parent);
289 }
290
291 //---------------------------------------------------------
292 // UserPaletteController::move
293 //---------------------------------------------------------
294
move(const QModelIndex & sourceParent,int sourceRow,const QModelIndex & destinationParent,int destinationChild)295 bool UserPaletteController::move(const QModelIndex& sourceParent, int sourceRow, const QModelIndex& destinationParent, int destinationChild)
296 {
297 if (!canEdit(sourceParent) || !canEdit(destinationParent))
298 return false;
299 if (sourceParent == destinationParent && (sourceParent.model() == model() || !sourceParent.isValid())) {
300 const QModelIndex srcIndex = convertProxyIndex(model()->index(sourceRow, 0, sourceParent), _userPalette);
301 const QModelIndex destIndex = convertProxyIndex(model()->index(destinationChild, 0, destinationParent), _userPalette);
302 return _userPalette->moveRow(srcIndex.parent(), srcIndex.row(), destIndex.parent(), destIndex.row());
303 }
304 return false;
305 }
306
307 //---------------------------------------------------------
308 // UserPaletteController::showHideOrDeleteDialog
309 //---------------------------------------------------------
310
showHideOrDeleteDialog(const QString & question,std::function<void (AbstractPaletteController::RemoveAction)> resultHandler) const311 void UserPaletteController::showHideOrDeleteDialog(const QString& question, std::function<void(AbstractPaletteController::RemoveAction)> resultHandler) const
312 {
313 QMessageBox* msg = new QMessageBox(mscore);
314 msg->setIcon(QMessageBox::Question);
315 msg->setText(question);
316 msg->setTextFormat(Qt::PlainText);
317 QPushButton* deleteButton = msg->addButton(tr("Delete permanently"), QMessageBox::DestructiveRole);
318 QPushButton* hideButton = msg->addButton(tr("Hide"), QMessageBox::AcceptRole);
319 msg->addButton(QMessageBox::Cancel);
320 msg->setDefaultButton(hideButton);
321
322 connect(msg, &QDialog::finished, this, [=]() {
323 RemoveAction action = RemoveAction::NoAction;
324
325 const QAbstractButton* btn = msg->clickedButton();
326 if (btn == deleteButton)
327 action = RemoveAction::DeletePermanently;
328 else if (btn == hideButton)
329 action = RemoveAction::Hide;
330
331 resultHandler(action);
332 });
333
334 msg->setWindowModality(Qt::ApplicationModal);
335 msg->setAttribute(Qt::WA_DeleteOnClose);
336 msg->open();
337 }
338
339 //---------------------------------------------------------
340 // UserPaletteController::queryRemove
341 //---------------------------------------------------------
342
queryRemove(const QModelIndexList & removeIndices,int customCount)343 void UserPaletteController::queryRemove(const QModelIndexList& removeIndices, int customCount)
344 {
345 using RemoveAction = AbstractPaletteController::RemoveAction;
346
347 if (removeIndices.empty() || !canEdit(removeIndices[0].parent()))
348 return;
349
350 if (!customCount) {
351 remove(removeIndices, RemoveAction::AutoAction);
352 return;
353 }
354
355 // these parameters should not depend on exact index as all they should have the same parent in palette tree
356 const bool visible = removeIndices[0].data(PaletteTreeModel::VisibleRole).toBool();
357 const bool isCell = bool(removeIndices[0].data(PaletteTreeModel::PaletteCellRole).value<const PaletteCell*>());
358
359 RemoveAction action = RemoveAction::AutoAction;
360
361 if (isCell) {
362 if (visible) {
363 showHideOrDeleteDialog(
364 customCount == 1 ? tr("Do you want to hide this custom palette cell or permanently delete it?") : tr("Do you want to hide these custom palette cells or permanently delete them?"),
365 [=](RemoveAction action) { remove(removeIndices, action); }
366 );
367 return;
368 }
369 else {
370 QMessageBox* msg = new QMessageBox(
371 QMessageBox::Question,
372 "",
373 customCount == 1 ? tr("Do you want to permanently delete this custom palette cell?") : tr("Do you want to permanently delete these custom palette cells?"),
374 QMessageBox::Yes | QMessageBox::No,
375 mscore
376 );
377
378 connect(msg, &QDialog::finished, this, [=]() {
379 const auto result = msg->standardButton(msg->clickedButton());
380 if (result == QMessageBox::Yes)
381 remove(removeIndices, RemoveAction::DeletePermanently);
382 });
383
384 msg->setWindowModality(Qt::ApplicationModal);
385 msg->setAttribute(Qt::WA_DeleteOnClose);
386 msg->open();
387 return;
388 }
389 }
390 else {
391 if (visible) {
392 showHideOrDeleteDialog(
393 customCount == 1 ? tr("Do you want to hide this custom palette or permanently delete it?") : tr("Do you want to hide these custom palettes or permanently delete them?"),
394 [=](RemoveAction action) { remove(removeIndices, action); }
395 );
396 return;
397 }
398 else
399 action = RemoveAction::Hide;
400 }
401
402 remove(removeIndices, action);
403 }
404
405 //---------------------------------------------------------
406 // UserPaletteController::remove
407 //---------------------------------------------------------
408
remove(const QModelIndexList & unsortedRemoveIndices,AbstractPaletteController::RemoveAction action)409 void UserPaletteController::remove(const QModelIndexList& unsortedRemoveIndices, AbstractPaletteController::RemoveAction action)
410 {
411 using RemoveAction = AbstractPaletteController::RemoveAction;
412
413 if (action == RemoveAction::NoAction)
414 return;
415
416 QModelIndexList removeIndices = unsortedRemoveIndices;
417 std::sort(removeIndices.begin(), removeIndices.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
418
419 // remove in reversed order to leave the previous model indices in the list valid
420 for (auto i = removeIndices.rbegin(); i != removeIndices.rend(); ++i) {
421 const QModelIndex& index = *i;
422
423 RemoveAction indexAction = action;
424 const bool custom = model()->data(index, PaletteTreeModel::CustomRole).toBool();
425
426 if (!custom || indexAction == RemoveAction::AutoAction) {
427 const bool isCell = bool(model()->data(index, PaletteTreeModel::PaletteCellRole).value<const PaletteCell*>());
428 indexAction = custom ? RemoveAction::NoAction : (isCell ? RemoveAction::DeletePermanently : RemoveAction::Hide);
429 }
430
431 switch (indexAction) {
432 case RemoveAction::NoAction:
433 break;
434 case RemoveAction::Hide:
435 model()->setData(index, false, PaletteTreeModel::VisibleRole);
436 break;
437 case RemoveAction::DeletePermanently:
438 model()->removeRow(index.row(), index.parent());
439 break;
440 case RemoveAction::AutoAction:
441 // impossible, we have just assigned another action for that case
442 Q_ASSERT(false);
443 break;
444 }
445 }
446 }
447
448 //---------------------------------------------------------
449 // UserPaletteController::remove
450 //---------------------------------------------------------
451
remove(const QModelIndex & index)452 void UserPaletteController::remove(const QModelIndex& index)
453 {
454 if (!canEdit(index.parent()))
455 return;
456
457 const bool customItem = index.data(PaletteTreeModel::CustomRole).toBool();
458 queryRemove({ index }, customItem ? 1 : 0);
459 }
460
461 //---------------------------------------------------------
462 // UserPaletteController::removeSelection
463 //---------------------------------------------------------
464
removeSelection(const QModelIndexList & selectedIndexes,const QModelIndex & parent)465 void UserPaletteController::removeSelection(const QModelIndexList& selectedIndexes, const QModelIndex& parent)
466 {
467 if (!canEdit(parent))
468 return;
469
470 QModelIndexList removeIndices;
471 int customItemsCount = 0;
472
473 for (const QModelIndex& idx : selectedIndexes) {
474 if (idx.parent() == parent) {
475 removeIndices.push_back(idx);
476 const bool custom = idx.data(PaletteTreeModel::CustomRole).toBool();
477 if (custom)
478 ++customItemsCount;
479 }
480 }
481
482 queryRemove(removeIndices, customItemsCount);
483 }
484
485 //---------------------------------------------------------
486 // UserPaletteController::editPaletteProperties
487 //---------------------------------------------------------
488
editPaletteProperties(const QModelIndex & index)489 void UserPaletteController::editPaletteProperties(const QModelIndex& index)
490 {
491 if (!canEdit(index))
492 return;
493
494 const QModelIndex srcIndex = convertProxyIndex(index, _userPalette);
495 PalettePanel* p = _userPalette->findPalettePanel(srcIndex);
496 if (!p)
497 return;
498
499 PaletteTreeModel* m = _userPalette;
500 PalettePropertiesDialog* d = new PalettePropertiesDialog(p, mscore);
501
502 const bool treeChangedWasBlocked = m->blockTreeChanged(true);
503 const bool paletteChangedState = m->paletteTreeChanged();
504
505 connect(d, &QDialog::accepted, m, [m, treeChangedWasBlocked]() {
506 m->blockTreeChanged(treeChangedWasBlocked);
507 });
508 connect(d, &QDialog::rejected, m, [m, srcIndex, paletteChangedState, treeChangedWasBlocked]() {
509 m->itemDataChanged(srcIndex);
510 paletteChangedState ? m->setTreeChanged() : m->setTreeUnchanged();
511 m->blockTreeChanged(treeChangedWasBlocked);
512 });
513 connect(d, &PalettePropertiesDialog::changed, m, [m, srcIndex]() {
514 m->itemDataChanged(srcIndex);
515 });
516
517 d->setModal(true);
518 d->setAttribute(Qt::WA_DeleteOnClose);
519 d->open();
520 }
521
522 //---------------------------------------------------------
523 // UserPaletteController::editCellProperties
524 //---------------------------------------------------------
525
editCellProperties(const QModelIndex & index)526 void UserPaletteController::editCellProperties(const QModelIndex& index)
527 {
528 if (!canEdit(index))
529 return;
530
531 const QModelIndex srcIndex = convertProxyIndex(index, _userPalette);
532 PaletteCellPtr cell = _userPalette->findCell(srcIndex);
533 if (!cell)
534 return;
535
536 PaletteCellPropertiesDialog* d = new PaletteCellPropertiesDialog(cell.get(), mscore);
537 PaletteTreeModel* m = _userPalette;
538
539 const bool treeChangedWasBlocked = m->blockTreeChanged(true);
540 const bool paletteChangedState = m->paletteTreeChanged();
541
542 connect(d, &QDialog::accepted, m, [m, treeChangedWasBlocked]() {
543 m->blockTreeChanged(treeChangedWasBlocked);
544 });
545 connect(d, &QDialog::rejected, m, [m, srcIndex, paletteChangedState, treeChangedWasBlocked]() {
546 m->itemDataChanged(srcIndex);
547 paletteChangedState ? m->setTreeChanged() : m->setTreeUnchanged();
548 m->blockTreeChanged(treeChangedWasBlocked);
549 });
550 connect(d, &PaletteCellPropertiesDialog::changed, m, [m, srcIndex]() {
551 m->itemDataChanged(srcIndex);
552 });
553
554 d->setModal(true);
555 d->setAttribute(Qt::WA_DeleteOnClose);
556 d->open();
557 }
558
559 //---------------------------------------------------------
560 // UserPaletteController::canEdit
561 //---------------------------------------------------------
562
canEdit(const QModelIndex & index) const563 bool UserPaletteController::canEdit(const QModelIndex& index) const
564 {
565 if (!userEditable())
566 return false;
567
568 return model()->data(index, PaletteTreeModel::EditableRole).toBool();
569 }
570
571 //---------------------------------------------------------
572 // UserPaletteController::applyPaletteElement
573 //---------------------------------------------------------
574
applyPaletteElement(const QModelIndex & index,Qt::KeyboardModifiers modifiers)575 bool UserPaletteController::applyPaletteElement(const QModelIndex& index, Qt::KeyboardModifiers modifiers)
576 {
577 const PaletteCell* cell = model()->data(index, PaletteTreeModel::PaletteCellRole).value<const PaletteCell*>();
578 if (cell && cell->element)
579 return Palette::applyPaletteElement(cell->element.get(), modifiers);
580 return false;
581 }
582
583 //---------------------------------------------------------
584 // PaletteWorkspace
585 //---------------------------------------------------------
586
PaletteWorkspace(PaletteTreeModel * user,PaletteTreeModel * master,QObject * parent)587 PaletteWorkspace::PaletteWorkspace(PaletteTreeModel* user, PaletteTreeModel* master, QObject* parent)
588 : QObject(parent), userPalette(user), masterPalette(master), defaultPalette(nullptr)
589 {
590 if (userPalette)
591 connect(userPalette, &PaletteTreeModel::treeChanged, this, &PaletteWorkspace::userPaletteChanged);
592
593 preferencesListenerID = preferences.addOnSetListener([this](const QString& key, const QVariant& /*value*/) {
594 if (key == PREF_APP_USESINGLEPALETTE)
595 emit singlePaletteChanged();
596 });
597 }
598
599 //---------------------------------------------------------
600 // ~PaletteWorkspace
601 //---------------------------------------------------------
602
~PaletteWorkspace()603 PaletteWorkspace::~PaletteWorkspace()
604 {
605 preferences.removeOnSetListener(preferencesListenerID);
606 }
607
608 //---------------------------------------------------------
609 // PaletteWorkspace::singlePalette
610 //---------------------------------------------------------
611
singlePalette() const612 bool PaletteWorkspace::singlePalette() const
613 {
614 return preferences.getBool(PREF_APP_USESINGLEPALETTE);
615 }
616
setSinglePalette(bool val)617 void PaletteWorkspace::setSinglePalette(bool val) {
618 if (val != singlePalette())
619 preferences.setPreference(PREF_APP_USESINGLEPALETTE, val);
620 // No need to emit `singlePaletteChanged()` here;
621 // that's already done by the `onSetListener` lambda.
622 // That's better, because the signal will also be emitted
623 // when this preference is changed from the Preferences Dialog.
624 }
625
626 //---------------------------------------------------------
627 // PaletteWorkspace::masterPaletteModel
628 //---------------------------------------------------------
629
mainPaletteModel()630 QAbstractItemModel* PaletteWorkspace::mainPaletteModel()
631 {
632 if (!mainPalette) {
633 // PaletteCellFilter* filter = new VisibilityCellFilter(/* acceptedValue */ true);
634 // mainPalette = new FilterPaletteTreeModel(filter, userPalette, this);
635 QSortFilterProxyModel* visFilterModel = new QSortFilterProxyModel(this);
636 visFilterModel->setFilterRole(PaletteTreeModel::VisibleRole);
637 visFilterModel->setFilterFixedString("true");
638 visFilterModel->setSourceModel(userPalette);
639
640 // Wrap it into another proxy model to enable filtering by palette cell name
641 QSortFilterProxyModel* textFilterModel = new PaletteCellFilterProxyModel(this);
642 textFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
643 textFilterModel->setSourceModel(visFilterModel);
644 visFilterModel->setParent(textFilterModel);
645
646 mainPalette = textFilterModel;
647 }
648 return mainPalette;
649 }
650
651 //---------------------------------------------------------
652 // PaletteWorkspace::masterPaletteModel
653 //---------------------------------------------------------
654
getMainPaletteController()655 AbstractPaletteController* PaletteWorkspace::getMainPaletteController()
656 {
657 if (!mainPaletteController)
658 mainPaletteController = new UserPaletteController(mainPaletteModel(), userPalette, this);
659 // mainPaletteController = new PaletteController(mainPaletteModel(), this, this);
660 return mainPaletteController;
661 }
662
663 //---------------------------------------------------------
664 // PaletteWorkspace::customPaletteIndex
665 //---------------------------------------------------------
666
customElementsPaletteIndex(const QModelIndex & index)667 QModelIndex PaletteWorkspace::customElementsPaletteIndex(const QModelIndex& index)
668 {
669 const QAbstractItemModel* model = customElementsPaletteModel();
670 if (index.model() == mainPalette && index.parent() == QModelIndex()) {
671 const int row = convertProxyIndex(index, userPalette).row();
672 return model->index(row, 0);
673 }
674 return convertIndex(index, model);
675 }
676
677 //---------------------------------------------------------
678 // PaletteWorkspace::customElementsPaletteModel
679 //---------------------------------------------------------
680
customElementsPaletteModel()681 FilterPaletteTreeModel* PaletteWorkspace::customElementsPaletteModel()
682 {
683 if (!customPoolPalette) {
684 PaletteCellFilter* filter = new VisibilityCellFilter(/* acceptedValue */ false);
685 filter->addChainedFilter(new CustomizedCellFilter(/* acceptedValue */ true));
686 customPoolPalette = new FilterPaletteTreeModel(filter, userPalette, this);
687 }
688 return customPoolPalette;
689 }
690
691 //---------------------------------------------------------
692 // PaletteWorkspace::getCustomElementsPaletteController
693 //---------------------------------------------------------
694
getCustomElementsPaletteController()695 AbstractPaletteController* PaletteWorkspace::getCustomElementsPaletteController()
696 {
697 if (!customElementsPaletteController) {
698 customElementsPaletteController = new UserPaletteController(customElementsPaletteModel(), userPalette, this);
699 customElementsPaletteController->setVisible(false);
700 customElementsPaletteController->setCustom(true);
701 }
702 // customElementsPaletteController = new CustomPaletteController(customElementsPaletteModel(), this, this);
703 return customElementsPaletteController;
704 }
705
706 //---------------------------------------------------------
707 // PaletteWorkspace::poolPaletteIndex
708 //---------------------------------------------------------
709
poolPaletteIndex(const QModelIndex & index,Ms::FilterPaletteTreeModel * poolPalette)710 QModelIndex PaletteWorkspace::poolPaletteIndex(const QModelIndex& index, Ms::FilterPaletteTreeModel* poolPalette)
711 {
712 const QModelIndex poolPaletteIndex = convertIndex(index, poolPalette);
713 if (poolPaletteIndex.isValid())
714 return poolPaletteIndex;
715 const auto contentType = index.data(PaletteTreeModel::PaletteContentTypeRole).value<PalettePanel::Type>();
716 return findPaletteIndex(poolPalette, contentType);
717 }
718
719 //---------------------------------------------------------
720 // PaletteWorkspace::masterPaletteModel
721 //---------------------------------------------------------
722
poolPaletteModel(const QModelIndex & index)723 FilterPaletteTreeModel* PaletteWorkspace::poolPaletteModel(const QModelIndex& index)
724 {
725 const QModelIndex filterIndex = convertProxyIndex(index, userPalette);
726 PaletteCellFilter* filter = userPalette->getFilter(filterIndex); // TODO: or mainPalette?
727 if (!filter)
728 return nullptr;
729
730 FilterPaletteTreeModel* m = new FilterPaletteTreeModel(filter, masterPalette);
731 QQmlEngine::setObjectOwnership(m, QQmlEngine::JavaScriptOwnership);
732 return m;
733 }
734
735 //---------------------------------------------------------
736 // PaletteWorkspace::masterPaletteController
737 //---------------------------------------------------------
738
poolPaletteController(FilterPaletteTreeModel * poolPaletteModel,const QModelIndex & rootIndex)739 AbstractPaletteController* PaletteWorkspace::poolPaletteController(FilterPaletteTreeModel* poolPaletteModel, const QModelIndex& rootIndex)
740 {
741 Q_UNUSED(rootIndex);
742 UserPaletteController* c = new UserPaletteController(poolPaletteModel, userPalette);
743 c->setVisible(false);
744 c->setCustom(false);
745 c->setUserEditable(false);
746 // AbstractPaletteController* c = new MasterPaletteController(poolPaletteModel, userPalette, this);
747 QQmlEngine::setObjectOwnership(c, QQmlEngine::JavaScriptOwnership);
748 return c;
749 }
750
751 //---------------------------------------------------------
752 // PaletteWorkspace::availableExtraPalettePanelsModel
753 //---------------------------------------------------------
754
availableExtraPalettesModel()755 QAbstractItemModel* PaletteWorkspace::availableExtraPalettesModel()
756 {
757 QStandardItemModel* m = new QStandardItemModel;
758
759 {
760 auto roleNames = m->roleNames();
761 roleNames[CustomRole] = "custom";
762 roleNames[PaletteIndexRole] = "paletteIndex";
763 m->setItemRoleNames(roleNames);
764 }
765
766 QStandardItem* root = m->invisibleRootItem();
767
768 const int masterRows = masterPalette->rowCount();
769 for (int row = 0; row < masterRows; ++row) {
770 const QModelIndex idx = masterPalette->index(row, 0);
771 // add everything that cannot be found in user palette
772 if (!convertIndex(idx, userPalette).isValid()) {
773 const QString name = masterPalette->data(idx, Qt::DisplayRole).toString();
774 QStandardItem* item = new QStandardItem(name);
775 item->setData(false, CustomRole); // this palette is from master palette, hence not custom
776 item->setData(QPersistentModelIndex(idx), PaletteIndexRole);
777 root->appendRow(item);
778 }
779 }
780
781 const int userRows = userPalette->rowCount();
782 for (int row = 0; row < userRows; ++row) {
783 const QModelIndex idx = userPalette->index(row, 0);
784 // add invisible custom palettes
785 const bool visible = userPalette->data(idx, PaletteTreeModel::VisibleRole).toBool();
786 const bool custom = userPalette->data(idx, PaletteTreeModel::CustomRole).toBool();
787 if (!visible) {
788 const QString name = userPalette->data(idx, Qt::DisplayRole).toString();
789 QStandardItem* item = new QStandardItem(name);
790 item->setData(custom, CustomRole);
791 item->setData(QPersistentModelIndex(idx), PaletteIndexRole);
792 root->appendRow(item);
793 }
794 }
795
796 QQmlEngine::setObjectOwnership(m, QQmlEngine::JavaScriptOwnership);
797 return m;
798 }
799
800 //---------------------------------------------------------
801 // PaletteWorkspace::addPalette
802 //---------------------------------------------------------
803
addPalette(const QPersistentModelIndex & index)804 bool PaletteWorkspace::addPalette(const QPersistentModelIndex& index)
805 {
806 if (!index.isValid())
807 return false;
808
809 if (index.model() == userPalette) {
810 const bool ok = userPalette->setData(index, true, PaletteTreeModel::VisibleRole);
811 if (!ok)
812 return false;
813 const QModelIndex parent = index.parent();
814 userPalette->moveRow(parent, index.row(), parent, 0);
815 return true;
816 }
817
818 if (index.model() == masterPalette) {
819 QMimeData* data = masterPalette->mimeData({ QModelIndex(index) });
820 const bool success = userPalette->dropMimeData(data, Qt::CopyAction, 0, 0, QModelIndex());
821 data->deleteLater();
822 return success;
823 }
824
825 return false;
826 }
827
828 //---------------------------------------------------------
829 // PaletteWorkspace::removeCustomPalette
830 //---------------------------------------------------------
831
removeCustomPalette(const QPersistentModelIndex & index)832 bool PaletteWorkspace::removeCustomPalette(const QPersistentModelIndex& index)
833 {
834 if (!index.isValid())
835 return false;
836
837
838 if (index.model() == userPalette) {
839 const bool custom = index.data(PaletteTreeModel::CustomRole).toBool();
840 if (!custom)
841 return false;
842
843 const auto answer = QMessageBox::question(
844 nullptr,
845 "",
846 tr("Do you want to permanently delete this custom palette?"),
847 QMessageBox::Yes | QMessageBox::No
848 );
849
850 if (answer == QMessageBox::Yes)
851 return userPalette->removeRow(index.row(), index.parent());
852 return false;
853 }
854
855 return false;
856 }
857
858 //---------------------------------------------------------
859 // PaletteWorkspace::resetPalette
860 //---------------------------------------------------------
861
resetPalette(const QModelIndex & index)862 bool PaletteWorkspace::resetPalette(const QModelIndex& index)
863 {
864 if (!index.isValid())
865 return false;
866
867 const auto answer =
868 (MScore::noGui && MScore::testMode)
869 ? QMessageBox::Yes
870 : QMessageBox::question(
871 nullptr,
872 "",
873 tr("Do you want to restore this palette to its default state? All changes to this palette will be lost."),
874 QMessageBox::Yes | QMessageBox::No
875 );
876
877 if (answer != QMessageBox::Yes)
878 return false;
879
880 Q_ASSERT(defaultPalette != userPalette);
881
882 QAbstractItemModel* resetModel = nullptr;
883 QModelIndex resetIndex;
884
885 if (defaultPalette) {
886 resetModel = defaultPalette;
887 resetIndex = convertIndex(index, defaultPalette);
888 }
889
890 if (!resetIndex.isValid()) {
891 resetModel = masterPalette;
892 resetIndex = convertIndex(index, masterPalette);
893 }
894
895 const QModelIndex userPaletteIndex = convertProxyIndex(index, userPalette);
896 const QModelIndex parent = userPaletteIndex.parent();
897 const int row = userPaletteIndex.row();
898 const int column = userPaletteIndex.column();
899
900 // restore visibility and expanded state of the palette after restoring its state
901 const bool wasVisible = index.data(PaletteTreeModel::VisibleRole).toBool();
902 const bool wasExpanded = index.data(PaletteTreeModel::PaletteExpandedRole).toBool();
903
904 if (!userPalette->removeRow(row, parent))
905 return false;
906
907 if (resetIndex.isValid()) {
908 QMimeData* data = resetModel->mimeData({ resetIndex });
909 userPalette->dropMimeData(data, Qt::CopyAction, row, column, parent);
910 data->deleteLater();
911 }
912 else
913 userPalette->insertRow(row, parent);
914
915 const QModelIndex newIndex = userPalette->index(row, column, parent);
916 userPalette->setData(newIndex, wasVisible, PaletteTreeModel::VisibleRole);
917 userPalette->setData(newIndex, wasExpanded, PaletteTreeModel::PaletteExpandedRole);
918
919 return true;
920 }
921
922 //---------------------------------------------------------
923 // PaletteWorkspace::savePalette
924 //---------------------------------------------------------
925
savePalette(const QModelIndex & index)926 bool PaletteWorkspace::savePalette(const QModelIndex& index)
927 {
928 const QModelIndex srcIndex = convertProxyIndex(index, userPalette);
929 const PalettePanel* pp = userPalette->findPalettePanel(srcIndex);
930 if (!pp)
931 return false;
932
933 const QString path = mscore->getPaletteFilename(/* load? */ false, pp->translatedName());
934
935 if (path.isEmpty())
936 return false;
937 return pp->writeToFile(path);
938 }
939
940 //---------------------------------------------------------
941 // PaletteWorkspace::loadPalette
942 //---------------------------------------------------------
943
loadPalette(const QModelIndex & index)944 bool PaletteWorkspace::loadPalette(const QModelIndex& index)
945 {
946 const QString path = mscore->getPaletteFilename(/* load? */ true);
947 if (path.isEmpty())
948 return false;
949
950 std::unique_ptr<PalettePanel> pp(new PalettePanel);
951 if (!pp->readFromFile(path))
952 return false;
953 pp->setType(PalettePanel::Type::Custom); // mark the loaded palette custom
954
955 const QModelIndex srcIndex = convertProxyIndex(index, userPalette);
956
957 const int row = srcIndex.row();
958 const QModelIndex parent = srcIndex.parent();
959
960 return userPalette->insertPalettePanel(std::move(pp), row, parent);
961 }
962
963 //---------------------------------------------------------
964 // PaletteWorkspace::setUserPaletteTree
965 //---------------------------------------------------------
966
setUserPaletteTree(std::unique_ptr<PaletteTree> tree)967 void PaletteWorkspace::setUserPaletteTree(std::unique_ptr<PaletteTree> tree)
968 {
969 if (userPalette) {
970 disconnect(userPalette, &PaletteTreeModel::treeChanged, this, &PaletteWorkspace::userPaletteChanged);
971 userPalette->setPaletteTree(std::move(tree));
972 connect(userPalette, &PaletteTreeModel::treeChanged, this, &PaletteWorkspace::userPaletteChanged);
973 }
974 else {
975 userPalette = new PaletteTreeModel(std::move(tree), /* parent */ this);
976 connect(userPalette, &PaletteTreeModel::treeChanged, this, &PaletteWorkspace::userPaletteChanged);
977 }
978 }
979
setDefaultPaletteTree(std::unique_ptr<PaletteTree> tree)980 void PaletteWorkspace::setDefaultPaletteTree(std::unique_ptr<PaletteTree> tree)
981 {
982 if (defaultPalette)
983 defaultPalette->setPaletteTree(std::move(tree));
984 else
985 defaultPalette = new PaletteTreeModel(std::move(tree), /* parent */ this);
986 }
987
988 //---------------------------------------------------------
989 // PaletteWorkspace::write
990 //---------------------------------------------------------
991
write(XmlWriter & xml) const992 void PaletteWorkspace::write(XmlWriter& xml) const
993 {
994 if (!userPalette)
995 return;
996 if (const PaletteTree* tree = userPalette->paletteTree())
997 tree->write(xml);
998 }
999
1000 //---------------------------------------------------------
1001 // PaletteWorkspace::read
1002 //---------------------------------------------------------
1003
read(XmlReader & e)1004 bool PaletteWorkspace::read(XmlReader& e)
1005 {
1006 std::unique_ptr<PaletteTree> tree(new PaletteTree);
1007 if (!tree->read(e))
1008 return false;
1009
1010 setUserPaletteTree(std::move(tree));
1011
1012 return true;
1013 }
1014
1015 //---------------------------------------------------------
1016 // PaletteWorkspace::needsItemDestructionAccessibilityWorkaround
1017 /// Checks whether workaround for palette search crash
1018 /// with NVDA is needed, see issue #298899.
1019 //---------------------------------------------------------
1020
needsItemDestructionAccessibilityWorkaround() const1021 bool PaletteWorkspace::needsItemDestructionAccessibilityWorkaround() const
1022 {
1023 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
1024 // Qt switched to a new accessibility backend since 5.11
1025 // and no crashes are reported for 5.12 so presumably
1026 // the workaround is not needed since Qt 5.11.
1027 return false;
1028 #else
1029 return QAccessible::isActive();
1030 #endif
1031 }
1032 } // namespace Ms
1033