LCOV - code coverage report
Current view: top level - src/palette/internal - paletteprovider.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 575 0
Test Date: 2026-11-05 04:07:02

            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 "paletteprovider.h"
      24              : 
      25              : #include <QFileDialog>
      26              : #include <QStandardItemModel>
      27              : #include <QJsonDocument>
      28              : #include <QQmlEngine>
      29              : #include <QMimeData>
      30              : #include <QStandardPaths>
      31              : 
      32              : #include "engraving/dom/mscore.h"
      33              : 
      34              : #include "notation/inotation.h"
      35              : #include "notation/inotationinteraction.h" // IWYU pragma: keep
      36              : 
      37              : #include "palettecreator.h"
      38              : 
      39              : #include "io/path.h"
      40              : 
      41              : #include "translation.h"
      42              : #include "types/uri.h"
      43              : 
      44              : #include "app_config.h"
      45              : 
      46              : using namespace muse;
      47              : using namespace mu::palette;
      48              : using namespace mu::engraving;
      49              : 
      50              : // ========================================================
      51              : // PaletteElementEditor
      52              : // ========================================================
      53              : 
      54            0 : bool PaletteElementEditor::valid() const
      55              : {
      56              :     using Type = Palette::Type;
      57            0 :     switch (_type) {
      58            0 :     case Type::KeySig:
      59              :     case Type::TimeSig:
      60            0 :         return true;
      61            0 :     default:
      62            0 :         break;
      63              :     }
      64            0 :     return false;
      65              : }
      66              : 
      67            0 : QString PaletteElementEditor::actionName() const
      68              : {
      69              :     using Type = Palette::Type;
      70            0 :     switch (_type) {
      71            0 :     case Type::KeySig:
      72            0 :         return muse::qtrc("palette", "Create key signature");
      73            0 :     case Type::TimeSig:
      74            0 :         return muse::qtrc("palette", "Create time signature");
      75            0 :     default:
      76            0 :         break;
      77              :     }
      78            0 :     return QString();
      79              : }
      80              : 
      81            0 : void PaletteElementEditor::setPaletteIndex(QPersistentModelIndex paletteIndex)
      82              : {
      83            0 :     _paletteIndex = paletteIndex;
      84            0 : }
      85              : 
      86            0 : void PaletteElementEditor::onElementAdded(const ElementPtr element)
      87              : {
      88            0 :     if (!element) {
      89            0 :         return;
      90              :     }
      91              : 
      92              :     static const QMap<ElementType, Palette::Type> elementTypeToPaletteType {
      93              :         { ElementType::TIMESIG, Palette::Type::TimeSig },
      94              :         { ElementType::KEYSIG, Palette::Type::KeySig }
      95            0 :     };
      96              : 
      97            0 :     Palette::Type paletteType = elementTypeToPaletteType.value(element->type(), Palette::Type::Unknown);
      98              : 
      99            0 :     if (paletteType != _type) {
     100            0 :         return;
     101              :     }
     102              : 
     103            0 :     if (!_paletteIndex.isValid()
     104            0 :         || !_paletteIndex.data(PaletteTreeModel::VisibleRole).toBool()) {
     105            0 :         interactive()->info("", muse::trc("palette", "The palette was hidden or changed"));
     106            0 :         return;
     107              :     }
     108              : 
     109            0 :     QVariantMap mimeData;
     110            0 :     mimeData[mimeSymbolFormat] = element->mimeData().toQByteArray();
     111              : 
     112            0 :     _controller->insert(_paletteIndex, -1, mimeData, Qt::CopyAction);
     113            0 : }
     114              : 
     115            0 : void PaletteElementEditor::open()
     116              : {
     117            0 :     if (!_paletteIndex.isValid()) {
     118            0 :         return;
     119              :     }
     120              : 
     121            0 :     muse::UriQuery uri;
     122              : 
     123              :     using Type = Palette::Type;
     124            0 :     switch (_type) {
     125            0 :     case Type::KeySig: {
     126            0 :         uri = muse::UriQuery("musescore://notation/keysignatures");
     127            0 :         uri.addParam("showKeyPalette", Val(false));
     128              :     }
     129            0 :     break;
     130            0 :     case Type::TimeSig: {
     131            0 :         uri = muse::UriQuery("musescore://notation/timesignatures");
     132            0 :         uri.addParam("showTimePalette", Val(false));
     133              :     }
     134            0 :     break;
     135            0 :     default:
     136            0 :         break;
     137              :     }
     138              : 
     139            0 :     if (uri.isValid()) {
     140            0 :         uri.addParam("sync", Val(false));
     141              : 
     142            0 :         paletteProvider()->addCustomItemRequested().onReceive(this, [this](ElementPtr item) {
     143            0 :             onElementAdded(item);
     144            0 :         }, async::Asyncable::Mode::SetReplace);
     145              : 
     146            0 :         if (interactive()->isOpened(uri).val) {
     147            0 :             interactive()->raise(uri);
     148              :         } else {
     149            0 :             interactive()->open(uri);
     150              :         }
     151              :     }
     152            0 : }
     153              : 
     154              : // ========================================================
     155              : // Model indices
     156              : // ========================================================
     157              : 
     158            0 : static QModelIndex findPaletteIndex(const QAbstractItemModel* model, Palette::Type type)
     159              : {
     160            0 :     constexpr int role = PaletteTreeModel::PaletteTypeRole;
     161            0 :     const QModelIndex start = model->index(0, 0);
     162            0 :     const QModelIndexList foundIndexList = model->match(start, role, QVariant::fromValue(type));
     163              : 
     164            0 :     if (!foundIndexList.empty()) {
     165            0 :         return foundIndexList[0];
     166              :     }
     167            0 :     return QModelIndex();
     168            0 : }
     169              : 
     170            0 : static QModelIndex convertIndex(const QModelIndex& index, const QAbstractItemModel* targetModel)
     171              : {
     172            0 :     if (index.model() == targetModel || !index.isValid()) {
     173            0 :         return index;
     174              :     }
     175              : 
     176            0 :     constexpr int typeRole = PaletteTreeModel::PaletteTypeRole;
     177            0 :     const auto type = index.model()->data(index, typeRole).value<Palette::Type>();
     178              : 
     179            0 :     return findPaletteIndex(targetModel, type);
     180              : }
     181              : 
     182            0 : static QModelIndex convertProxyIndex(const QModelIndex& srcIndex, const QAbstractItemModel* targetModel)
     183              : {
     184            0 :     QModelIndex index = srcIndex;
     185            0 :     while (index.model() != targetModel) {
     186            0 :         if (auto m = qobject_cast<const QAbstractProxyModel*>(index.model())) {
     187            0 :             index = m->mapToSource(index);
     188              :         } else {
     189            0 :             break;
     190              :         }
     191              :     }
     192              : 
     193            0 :     if (targetModel && index.model() != targetModel) {
     194            0 :         return QModelIndex();
     195              :     }
     196            0 :     return index;
     197              : }
     198              : 
     199              : // ========================================================
     200              : // AbstractPaletteController
     201              : // ========================================================
     202            0 : PaletteElementEditor* AbstractPaletteController::elementEditor(const QModelIndex& paletteIndex)
     203              : {
     204            0 :     Palette::Type paletteType = paletteIndex.data(PaletteTreeModel::PaletteTypeRole).value<Palette::Type>();
     205              : 
     206            0 :     if (m_paletteElementEditorMap.contains(paletteType)) {
     207            0 :         PaletteElementEditor* ed = m_paletteElementEditorMap[paletteType];
     208            0 :         ed->setPaletteIndex(paletteIndex);
     209            0 :         return ed;
     210              :     }
     211              : 
     212              :     PaletteElementEditor* ed = new PaletteElementEditor(
     213            0 :         this, paletteIndex, paletteIndex.data(PaletteTreeModel::PaletteTypeRole).value<Palette::Type>(),
     214            0 :         iocContext(), this);
     215              : 
     216            0 :     m_paletteElementEditorMap.insert(paletteType, ed);
     217            0 :     return ed;
     218              : }
     219              : 
     220              : // ========================================================
     221              : //   UserPaletteController
     222              : // ========================================================
     223              : 
     224            0 : Qt::DropAction UserPaletteController::dropAction(const QVariantMap& mimeData, Qt::DropAction proposedAction,
     225              :                                                  const QModelIndex& parent, bool internal) const
     226              : {
     227            0 :     if (internal && !userEditable()) {
     228            0 :         return Qt::IgnoreAction;
     229              :     }
     230            0 :     const bool parentEditingEnabled = model()->data(parent, PaletteTreeModel::EditableRole).toBool();
     231            0 :     if (!parentEditingEnabled) {
     232            0 :         return Qt::IgnoreAction;
     233              :     }
     234              : 
     235            0 :     if (mimeData.contains(PaletteCell::mimeDataFormat) && proposedAction == Qt::MoveAction) {
     236            0 :         const auto cell = PaletteCell::fromMimeData(mimeData[PaletteCell::mimeDataFormat].toByteArray(), iocContext());
     237            0 :         if (!cell) {
     238            0 :             return Qt::IgnoreAction;
     239              :         }
     240            0 :         if (_filterCustom && cell->custom != _custom) {
     241            0 :             return Qt::IgnoreAction;
     242              :         }
     243            0 :         return Qt::MoveAction;
     244            0 :     }
     245            0 :     if (mimeData.contains(mimeSymbolFormat) && proposedAction == Qt::CopyAction) {
     246            0 :         if (_filterCustom && !_custom) {
     247            0 :             return Qt::IgnoreAction;
     248              :         }
     249            0 :         return Qt::CopyAction;
     250              :     }
     251            0 :     return Qt::IgnoreAction;
     252              : }
     253              : 
     254            0 : bool UserPaletteController::insert(const QModelIndex& parent, int row, const QVariantMap& mimeData,
     255              :                                    Qt::DropAction action)
     256              : {
     257            0 :     if (dropAction(mimeData, action, parent, false) == Qt::IgnoreAction) {
     258            0 :         return false;
     259              :     }
     260              : 
     261            0 :     if (row < 0) {
     262            0 :         row = parent.model()->rowCount(parent);
     263              :     }
     264              : 
     265            0 :     PaletteCellPtr cell;
     266              : 
     267            0 :     if (mimeData.contains(PaletteCell::mimeDataFormat)) {
     268            0 :         cell = PaletteCell::fromMimeData(mimeData[PaletteCell::mimeDataFormat].toByteArray(), iocContext());
     269              : 
     270            0 :         if (!cell) {
     271            0 :             return false;
     272              :         }
     273            0 :         if (_filterCustom && cell->custom != _custom) {
     274            0 :             return false;
     275              :         }
     276              : 
     277            0 :         if (action == Qt::MoveAction) {
     278            0 :             const QModelIndex visiblePaletteParentIndex = convertIndex(parent, _userPalette);
     279            0 :             const QModelIndex foundIndex(_userPalette->findPaletteCell(*cell, visiblePaletteParentIndex));
     280            0 :             if (foundIndex.isValid()) {
     281            0 :                 return _userPalette->setData(foundIndex, _visible, PaletteTreeModel::VisibleRole);
     282            0 :             } else if (!userEditable()) {
     283            0 :                 return false;
     284              :             }
     285              :         }
     286            0 :     } else if (mimeData.contains(mimeSymbolFormat) && (action == Qt::CopyAction)) {
     287            0 :         cell = PaletteCell::fromElementMimeData(mimeData[mimeSymbolFormat].toByteArray(), iocContext());
     288              :     }
     289              : 
     290            0 :     if (!cell) {
     291            0 :         return false;
     292              :     }
     293              : 
     294            0 :     if (_filterCustom) {
     295            0 :         if (!_custom) {
     296            0 :             return false;       // can only move non-custom cells
     297              :         }
     298            0 :         cell->custom = _custom;
     299              :     }
     300            0 :     cell->visible = _visible;
     301              : 
     302            0 :     QMimeData data;
     303            0 :     data.setData(PaletteCell::mimeDataFormat, cell->toMimeData());
     304            0 :     constexpr int column = 0;
     305            0 :     return model()->dropMimeData(&data, action, row, column, parent);
     306            0 : }
     307              : 
     308            0 : bool UserPaletteController::insertNewItem(const QModelIndex& parent, int row, const QString& name)
     309              : {
     310            0 :     if (!canEdit(parent)) {
     311            0 :         return false;
     312              :     }
     313              : 
     314            0 :     const bool newItemIsPalette = !parent.isValid();
     315              : 
     316            0 :     if (newItemIsPalette) {
     317            0 :         if (!model()->insertRow(row, parent)) {
     318            0 :             return false;
     319              :         }
     320            0 :         model()->setData(model()->index(row, 0, parent), name, Qt::DisplayRole);
     321            0 :         return true;
     322              :     }
     323              : 
     324            0 :     return model()->insertRow(row, parent);
     325              : }
     326              : 
     327            0 : bool UserPaletteController::move(const QModelIndex& sourceParent, int sourceRow, const QModelIndex& destinationParent,
     328              :                                  int destinationChild)
     329              : {
     330            0 :     if (!canEdit(sourceParent) || !canEdit(destinationParent)) {
     331            0 :         return false;
     332              :     }
     333            0 :     if (sourceParent == destinationParent && (sourceParent.model() == model() || !sourceParent.isValid())) {
     334            0 :         const QModelIndex srcIndex = convertProxyIndex(model()->index(sourceRow, 0, sourceParent), _userPalette);
     335            0 :         const QModelIndex destIndex = convertProxyIndex(model()->index(destinationChild, 0,
     336            0 :                                                                        destinationParent), _userPalette);
     337            0 :         return _userPalette->moveRow(srcIndex.parent(), srcIndex.row(), destIndex.parent(), destIndex.row());
     338              :     }
     339            0 :     return false;
     340              : }
     341              : 
     342            0 : async::Promise<UserPaletteController::RemoveAction> UserPaletteController::showHideOrDeleteDialog(const std::string& question) const
     343              : {
     344            0 :     int hideButton = int(IInteractive::Button::CustomButton) + 1;
     345            0 :     int deleteButton = hideButton + 1;
     346              : 
     347            0 :     return interactive()->question(std::string(), question, {
     348            0 :         IInteractive::ButtonData(hideButton, muse::trc("palette", "Hide")),
     349            0 :         IInteractive::ButtonData(deleteButton, muse::trc("palette", "Delete permanently")),
     350            0 :         interactive()->buttonData(IInteractive::Button::Cancel)
     351              :     })
     352            0 :            .then<RemoveAction>(this, [deleteButton, hideButton](const IInteractive::Result& res, auto resolve) {
     353            0 :         RemoveAction action = RemoveAction::NoAction;
     354            0 :         if (res.isButton(deleteButton)) {
     355            0 :             action = RemoveAction::DeletePermanently;
     356            0 :         } else if (res.isButton(hideButton)) {
     357            0 :             action = RemoveAction::Hide;
     358              :         }
     359            0 :         return resolve(action);
     360            0 :     });
     361            0 : }
     362              : 
     363            0 : void UserPaletteController::queryRemove(const QModelIndexList& removeIndices, int customCount)
     364              : {
     365            0 :     if (removeIndices.empty() || !canEdit(removeIndices[0].parent())) {
     366            0 :         return;
     367              :     }
     368              : 
     369            0 :     if (!customCount) {
     370            0 :         remove(removeIndices, RemoveAction::AutoAction);
     371            0 :         return;
     372              :     }
     373              : 
     374              :     // these parameters should not depend on exact index as all they should have the same parent in palette tree
     375            0 :     const bool visible = removeIndices[0].data(PaletteTreeModel::VisibleRole).toBool();
     376            0 :     const bool isCell = bool(removeIndices[0].data(PaletteTreeModel::PaletteCellRole).value<const PaletteCell*>());
     377              : 
     378            0 :     RemoveAction action = RemoveAction::AutoAction;
     379              : 
     380            0 :     if (isCell) {
     381            0 :         if (visible) {
     382              :             std::string question = customCount == 1
     383            0 :                                    ? muse::trc("palette", "Do you want to hide this custom palette cell or permanently delete it?")
     384            0 :                                    : muse::trc("palette", "Do you want to hide these custom palette cells or permanently delete them?");
     385              : 
     386            0 :             showHideOrDeleteDialog(question)
     387            0 :             .onResolve(this, [this, removeIndices](RemoveAction action) {
     388            0 :                 remove(removeIndices, action);
     389            0 :             });
     390            0 :             return;
     391            0 :         } else {
     392              :             std::string question = customCount == 1
     393            0 :                                    ? muse::trc("palette", "Do you want to permanently delete this custom palette cell?")
     394            0 :                                    : muse::trc("palette", "Do you want to permanently delete these custom palette cells?");
     395              : 
     396            0 :             interactive()->question(std::string(), question, {
     397              :                 IInteractive::Button::Yes,
     398              :                 IInteractive::Button::No
     399              :             })
     400            0 :             .onResolve(this, [this, removeIndices](const IInteractive::Result& res) {
     401            0 :                 if (res.isButton(IInteractive::Button::Yes)) {
     402            0 :                     remove(removeIndices, RemoveAction::DeletePermanently);
     403              :                 }
     404            0 :             });
     405            0 :         }
     406              :     } else {
     407            0 :         if (visible) {
     408              :             std::string question = customCount == 1
     409            0 :                                    ? muse::trc("palette", "Do you want to hide this custom palette or permanently delete it?")
     410            0 :                                    : muse::trc("palette", "Do you want to hide these custom palettes or permanently delete them?");
     411              : 
     412            0 :             showHideOrDeleteDialog(question)
     413            0 :             .onResolve(this, [this, removeIndices](RemoveAction action) { remove(removeIndices, action); });
     414            0 :             return;
     415            0 :         } else {
     416            0 :             action = RemoveAction::Hide;
     417              :         }
     418              : 
     419            0 :         remove(removeIndices, action);
     420              :     }
     421              : }
     422              : 
     423            0 : void UserPaletteController::remove(const QModelIndexList& unsortedRemoveIndices,
     424              :                                    AbstractPaletteController::RemoveAction action)
     425              : {
     426            0 :     if (action == RemoveAction::NoAction) {
     427            0 :         return;
     428              :     }
     429              : 
     430            0 :     QModelIndexList removeIndices = unsortedRemoveIndices;
     431            0 :     std::sort(removeIndices.begin(), removeIndices.end(), [](const QModelIndex& a, const QModelIndex& b) {
     432            0 :         return a.row() < b.row();
     433              :     });
     434              : 
     435              :     // remove in reversed order to leave the previous model indices in the list valid
     436            0 :     for (auto i = removeIndices.rbegin(); i != removeIndices.rend(); ++i) {
     437            0 :         const QModelIndex& index = *i;
     438              : 
     439            0 :         RemoveAction indexAction = action;
     440            0 :         const bool custom = model()->data(index, PaletteTreeModel::CustomRole).toBool();
     441              : 
     442            0 :         if (!custom || indexAction == RemoveAction::AutoAction) {
     443              :             const bool isCell
     444            0 :                 = bool(model()->data(index, PaletteTreeModel::PaletteCellRole).value<const PaletteCell*>());
     445              :             indexAction
     446            0 :                 = custom ? RemoveAction::NoAction : (isCell ? RemoveAction::DeletePermanently : RemoveAction::Hide);
     447              :         }
     448              : 
     449            0 :         switch (indexAction) {
     450            0 :         case RemoveAction::NoAction:
     451            0 :             break;
     452            0 :         case RemoveAction::Hide:
     453            0 :             model()->setData(index, false, PaletteTreeModel::VisibleRole);
     454            0 :             break;
     455            0 :         case RemoveAction::DeletePermanently:
     456            0 :             model()->removeRow(index.row(), index.parent());
     457            0 :             break;
     458            0 :         case RemoveAction::AutoAction:
     459              :             // impossible, we have just assigned another action for that case
     460            0 :             Q_ASSERT(false);
     461              :             break;
     462              :         }
     463              :     }
     464            0 : }
     465              : 
     466            0 : void UserPaletteController::remove(const QModelIndex& index)
     467              : {
     468            0 :     if (!canEdit(index.parent())) {
     469            0 :         return;
     470              :     }
     471              : 
     472            0 :     const bool customItem = index.data(PaletteTreeModel::CustomRole).toBool();
     473            0 :     queryRemove({ index }, customItem ? 1 : 0);
     474              : }
     475              : 
     476            0 : void UserPaletteController::removeSelection(const QModelIndexList& selectedIndexes, const QModelIndex& parent)
     477              : {
     478            0 :     if (!canEdit(parent)) {
     479            0 :         return;
     480              :     }
     481              : 
     482            0 :     QModelIndexList removeIndices;
     483            0 :     int customItemsCount = 0;
     484              : 
     485            0 :     for (const QModelIndex& idx : selectedIndexes) {
     486            0 :         if (idx.parent() == parent || !parent.isValid()) {
     487            0 :             removeIndices.push_back(idx);
     488            0 :             const bool custom = idx.data(PaletteTreeModel::CustomRole).toBool();
     489            0 :             if (custom) {
     490            0 :                 ++customItemsCount;
     491              :             }
     492              :         }
     493              :     }
     494              : 
     495            0 :     queryRemove(removeIndices, customItemsCount);
     496            0 : }
     497              : 
     498            0 : void UserPaletteController::editPaletteProperties(const QModelIndex& index)
     499              : {
     500            0 :     if (!canEdit(index)) {
     501            0 :         return;
     502              :     }
     503              : 
     504            0 :     QModelIndex srcIndex = convertProxyIndex(index, _userPalette);
     505            0 :     Palette* palette = _userPalette->findPalette(srcIndex);
     506            0 :     if (!palette) {
     507            0 :         return;
     508              :     }
     509              : 
     510            0 :     configuration()->paletteConfig(palette->id()).ch.onReceive(this,
     511            0 :                                                                [this, srcIndex, palette](
     512              :                                                                    const IPaletteConfiguration::PaletteConfig& config) {
     513            0 :         palette->setName(config.name);
     514            0 :         palette->setGridSize(config.size);
     515            0 :         palette->setMag(config.scale);
     516            0 :         palette->setYOffset(config.elementOffset);
     517            0 :         palette->setDrawGrid(config.showGrid);
     518            0 :         _userPalette->itemDataChanged(srcIndex);
     519            0 :     });
     520              : 
     521            0 :     QVariantMap properties;
     522            0 :     properties["paletteId"] = palette->id();
     523            0 :     properties["name"] = QString(palette->translatedName().toUtf8().toPercentEncoding());
     524            0 :     properties["cellWidth"] = palette->gridSize().width();
     525            0 :     properties["cellHeight"] = palette->gridSize().height();
     526            0 :     properties["scale"] = palette->mag();
     527            0 :     properties["elementOffset"] = palette->yOffset();
     528            0 :     properties["showGrid"] = palette->drawGrid();
     529              : 
     530            0 :     QJsonDocument document = QJsonDocument::fromVariant(properties);
     531            0 :     QString uri = QString("musescore://palette/properties?properties=%1")
     532            0 :                   .arg(QString(document.toJson()));
     533              : 
     534            0 :     interactive()->open(uri.toStdString());
     535            0 : }
     536              : 
     537            0 : void UserPaletteController::editCellProperties(const QModelIndex& index)
     538              : {
     539            0 :     if (!canEdit(index)) {
     540            0 :         return;
     541              :     }
     542              : 
     543            0 :     QModelIndex srcIndex = convertProxyIndex(index, _userPalette);
     544            0 :     PaletteCellPtr cell = _userPalette->findCell(srcIndex);
     545            0 :     if (!cell) {
     546            0 :         return;
     547              :     }
     548              : 
     549            0 :     configuration()->paletteCellConfig(cell->id).ch.onReceive(this,
     550            0 :                                                               [this, srcIndex, cell](
     551              :                                                                   const IPaletteConfiguration::PaletteCellConfig& config) {
     552            0 :         cell->name = config.name;
     553            0 :         cell->mag = config.scale;
     554            0 :         cell->drawStaff = config.drawStaff;
     555            0 :         cell->xoffset = config.xOffset;
     556            0 :         cell->yoffset = config.yOffset;
     557            0 :         _userPalette->itemDataChanged(srcIndex);
     558            0 :     });
     559              : 
     560            0 :     QVariantMap properties;
     561            0 :     properties["cellId"] = cell->id;
     562            0 :     properties["name"] = cell->translatedName();
     563            0 :     properties["xOffset"] = cell->xoffset;
     564            0 :     properties["yOffset"] = cell->yoffset;
     565            0 :     properties["scale"] = cell->mag;
     566            0 :     properties["drawStaff"] = cell->drawStaff;
     567              : 
     568            0 :     QJsonDocument document = QJsonDocument::fromVariant(properties);
     569            0 :     QString uri = QString("musescore://palette/cellproperties?properties=%1")
     570            0 :                   .arg(QString(document.toJson()));
     571              : 
     572            0 :     interactive()->open(uri.toStdString());
     573            0 : }
     574              : 
     575            0 : bool UserPaletteController::canEdit(const QModelIndex& index) const
     576              : {
     577            0 :     if (!userEditable()) {
     578            0 :         return false;
     579              :     }
     580              : 
     581            0 :     return model()->data(index, PaletteTreeModel::EditableRole).toBool();
     582              : }
     583              : 
     584            0 : bool UserPaletteController::applyPaletteElement(const QModelIndex& index, Qt::KeyboardModifiers modifiers)
     585              : {
     586            0 :     const PaletteCell* cell = model()->data(index, PaletteTreeModel::PaletteCellRole).value<const PaletteCell*>();
     587            0 :     if (!cell || !cell->element) {
     588            0 :         return false;
     589              :     }
     590              : 
     591            0 :     auto notation = projectSession()->currentNotation();
     592            0 :     if (!notation) {
     593            0 :         return false;
     594              :     }
     595              : 
     596            0 :     return notation->interaction()->applyPaletteElement(cell->element.get(), modifiers);
     597            0 : }
     598              : 
     599              : // ========================================================
     600              : // PaletteProvider
     601              : // ========================================================
     602              : 
     603            0 : void PaletteProvider::init()
     604              : {
     605            0 :     m_userPaletteModel = new PaletteTreeModel(std::make_shared<PaletteTree>(), iocContext(), this);
     606            0 :     connect(m_userPaletteModel, &PaletteTreeModel::treeChanged, this, &PaletteProvider::notifyAboutUserPaletteChanged);
     607              : 
     608            0 :     m_masterPaletteModel = new PaletteTreeModel(PaletteCreator(iocContext()).newMasterPaletteTree(), iocContext(), this);
     609              : 
     610            0 :     m_searchFilterModel = new PaletteCellFilterProxyModel(this);
     611            0 :     m_searchFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
     612              : 
     613            0 :     m_visibilityFilterModel = new QSortFilterProxyModel(this);
     614            0 :     m_visibilityFilterModel->setFilterRole(PaletteTreeModel::VisibleRole);
     615            0 :     m_visibilityFilterModel->setFilterFixedString("true");
     616            0 :     m_visibilityFilterModel->setSourceModel(m_userPaletteModel);
     617              : 
     618            0 :     configuration()->isSinglePalette().ch.onReceive(this, [this](bool) {
     619            0 :         emit isSinglePaletteChanged();
     620            0 :     });
     621              : 
     622            0 :     configuration()->isSingleClickToOpenPalette().ch.onReceive(this, [this](bool) {
     623            0 :         emit isSingleClickToOpenPaletteChanged();
     624            0 :     });
     625              : 
     626            0 :     configuration()->isPaletteDragEnabled().ch.onReceive(this, [this](bool) {
     627            0 :         emit isPaletteDragEnabledChanged();
     628            0 :     });
     629            0 : }
     630              : 
     631            0 : void PaletteProvider::setFilter(const QString& filter)
     632              : {
     633              :     // Unbind the model when there is no search text so as to return no results
     634              :     // and thus speed up the opening of the palette search. Rebind the model
     635              :     // as soon as the search text is non-empty.
     636              :     // Doing this *trick* also when the search text is non-empty helps
     637              :     // speed up the search in certain other scenarios,
     638              :     // e.g. when deleting search characters (going from fewer to more search results).
     639            0 :     m_searchFilterModel->setSourceModel(nullptr);
     640            0 :     m_searchFilterModel->setFilterFixedString(filter);
     641            0 :     if (!filter.isEmpty()) {
     642            0 :         m_searchFilterModel->setSourceModel(m_masterPaletteModel);
     643              :     }
     644            0 : }
     645              : 
     646            0 : void PaletteProvider::setSearching(bool searching)
     647              : {
     648            0 :     if (m_isSearching == searching) {
     649            0 :         return;
     650              :     }
     651              : 
     652            0 :     m_isSearching = searching;
     653              : 
     654            0 :     m_mainPalette = nullptr;
     655            0 :     m_mainPaletteController = nullptr;
     656            0 :     emit mainPaletteChanged();
     657              : }
     658              : 
     659            0 : bool PaletteProvider::isSinglePalette() const
     660              : {
     661            0 :     return configuration()->isSinglePalette().val;
     662              : }
     663              : 
     664            0 : bool PaletteProvider::isSingleClickToOpenPalette() const
     665              : {
     666            0 :     return configuration()->isSingleClickToOpenPalette().val;
     667              : }
     668              : 
     669            0 : bool PaletteProvider::isPaletteDragEnabled() const
     670              : {
     671            0 :     return configuration()->isPaletteDragEnabled().val;
     672              : }
     673              : 
     674            0 : QAbstractItemModel* PaletteProvider::mainPaletteModel()
     675              : {
     676            0 :     if (m_isSearching) {
     677            0 :         m_mainPalette = m_searchFilterModel;
     678              :     } else {
     679            0 :         m_mainPalette = m_visibilityFilterModel;
     680              :     }
     681              : 
     682            0 :     return m_mainPalette;
     683              : }
     684              : 
     685            0 : AbstractPaletteController* PaletteProvider::mainPaletteController()
     686              : {
     687            0 :     if (!m_mainPaletteController) {
     688            0 :         m_mainPaletteController = new UserPaletteController(mainPaletteModel(), m_userPaletteModel, iocContext(), this);
     689              :     }
     690            0 :     return m_mainPaletteController;
     691              : }
     692              : 
     693            0 : QModelIndex PaletteProvider::customElementsPaletteIndex(const QModelIndex& index)
     694              : {
     695            0 :     const QAbstractItemModel* model = customElementsPaletteModel();
     696            0 :     if (index.model() == m_mainPalette && index.parent() == QModelIndex()) {
     697            0 :         const int row = convertProxyIndex(index, m_userPaletteModel).row();
     698            0 :         return model->index(row, 0);
     699              :     }
     700            0 :     return convertIndex(index, model);
     701              : }
     702              : 
     703            0 : FilterPaletteTreeModel* PaletteProvider::customElementsPaletteModel()
     704              : {
     705            0 :     if (!m_customPoolPalette) {
     706            0 :         PaletteCellFilter* filter = new VisibilityCellFilter(/* acceptedValue */ false);
     707            0 :         filter->addChainedFilter(new CustomizedCellFilter(/* acceptedValue */ true));
     708            0 :         m_customPoolPalette = new FilterPaletteTreeModel(filter, m_userPaletteModel, this);
     709              :     }
     710            0 :     return m_customPoolPalette;
     711              : }
     712              : 
     713            0 : AbstractPaletteController* PaletteProvider::customElementsPaletteController()
     714              : {
     715            0 :     if (!m_customElementsPaletteController) {
     716            0 :         m_customElementsPaletteController = new UserPaletteController(customElementsPaletteModel(), m_userPaletteModel, iocContext(), this);
     717            0 :         m_customElementsPaletteController->setCustom(true);
     718              :     }
     719              : 
     720            0 :     return m_customElementsPaletteController;
     721              : }
     722              : 
     723            0 : QModelIndex PaletteProvider::poolPaletteIndex(const QModelIndex& index, FilterPaletteTreeModel* poolPalette) const
     724              : {
     725            0 :     const QModelIndex poolPaletteIndex = convertIndex(index, poolPalette);
     726            0 :     if (poolPaletteIndex.isValid()) {
     727            0 :         return poolPaletteIndex;
     728              :     }
     729            0 :     const auto contentType = index.data(PaletteTreeModel::PaletteContentTypeRole).value<Palette::Type>();
     730            0 :     return findPaletteIndex(poolPalette, contentType);
     731              : }
     732              : 
     733            0 : FilterPaletteTreeModel* PaletteProvider::poolPaletteModel(const QModelIndex& index) const
     734              : {
     735            0 :     const QModelIndex filterIndex = convertProxyIndex(index, m_userPaletteModel);
     736            0 :     PaletteCellFilter* filter = m_userPaletteModel->getFilter(filterIndex);
     737            0 :     if (!filter) {
     738            0 :         return nullptr;
     739              :     }
     740              : 
     741            0 :     FilterPaletteTreeModel* m = new FilterPaletteTreeModel(filter, m_masterPaletteModel);
     742            0 :     QQmlEngine::setObjectOwnership(m, QQmlEngine::JavaScriptOwnership);
     743            0 :     return m;
     744              : }
     745              : 
     746            0 : AbstractPaletteController* PaletteProvider::poolPaletteController(FilterPaletteTreeModel* poolPaletteModel,
     747              :                                                                   const QModelIndex& rootIndex) const
     748              : {
     749              :     Q_UNUSED(rootIndex);
     750            0 :     UserPaletteController* c = new UserPaletteController(poolPaletteModel, m_userPaletteModel, iocContext());
     751            0 :     c->setVisible(false);
     752            0 :     c->setCustom(false);
     753            0 :     c->setUserEditable(false);
     754              : //       AbstractPaletteController* c = new MasterPaletteController(poolPaletteModel, userPalette, this);
     755            0 :     QQmlEngine::setObjectOwnership(c, QQmlEngine::JavaScriptOwnership);
     756            0 :     return c;
     757              : }
     758              : 
     759            0 : QAbstractItemModel* PaletteProvider::availableExtraPalettesModel() const
     760              : {
     761            0 :     QStandardItemModel* m = new QStandardItemModel;
     762              : 
     763              :     {
     764            0 :         auto roleNames = m->roleNames();
     765            0 :         roleNames[CustomRole] = "custom";
     766            0 :         roleNames[PaletteIndexRole] = "paletteIndex";
     767            0 :         m->setItemRoleNames(roleNames);
     768            0 :     }
     769              : 
     770            0 :     QStandardItem* root = m->invisibleRootItem();
     771              : 
     772            0 :     const int masterRows = m_masterPaletteModel->rowCount();
     773            0 :     for (int row = 0; row < masterRows; ++row) {
     774            0 :         const QModelIndex idx = m_masterPaletteModel->index(row, 0);
     775              :         // add everything that cannot be found in user palette
     776            0 :         if (!convertIndex(idx, m_userPaletteModel).isValid()) {
     777            0 :             const QString name = m_masterPaletteModel->data(idx, Qt::DisplayRole).toString();
     778            0 :             QStandardItem* item = new QStandardItem(name);
     779            0 :             item->setData(false, CustomRole);       // this palette is from master palette, hence not custom
     780            0 :             item->setData(QPersistentModelIndex(idx), PaletteIndexRole);
     781            0 :             root->appendRow(item);
     782            0 :         }
     783              :     }
     784              : 
     785            0 :     const int userRows = m_userPaletteModel->rowCount();
     786            0 :     for (int row = 0; row < userRows; ++row) {
     787            0 :         const QModelIndex idx = m_userPaletteModel->index(row, 0);
     788              :         // add invisible custom palettes
     789            0 :         const bool visible = m_userPaletteModel->data(idx, PaletteTreeModel::VisibleRole).toBool();
     790            0 :         const bool custom = m_userPaletteModel->data(idx, PaletteTreeModel::CustomRole).toBool();
     791            0 :         if (!visible) {
     792            0 :             const QString name = m_userPaletteModel->data(idx, Qt::DisplayRole).toString();
     793            0 :             QStandardItem* item = new QStandardItem(name);
     794            0 :             item->setData(custom, CustomRole);
     795            0 :             item->setData(QPersistentModelIndex(idx), PaletteIndexRole);
     796            0 :             root->appendRow(item);
     797            0 :         }
     798              :     }
     799              : 
     800            0 :     QQmlEngine::setObjectOwnership(m, QQmlEngine::JavaScriptOwnership);
     801            0 :     return m;
     802              : }
     803              : 
     804            0 : bool PaletteProvider::addPalette(const QPersistentModelIndex& index)
     805              : {
     806            0 :     if (!index.isValid()) {
     807            0 :         return false;
     808              :     }
     809              : 
     810            0 :     if (index.model() == m_userPaletteModel) {
     811            0 :         const bool ok = m_userPaletteModel->setData(index, true, PaletteTreeModel::VisibleRole);
     812            0 :         if (!ok) {
     813            0 :             return false;
     814              :         }
     815            0 :         const QModelIndex parent = index.parent();
     816            0 :         m_userPaletteModel->moveRow(parent, index.row(), parent, 0);
     817            0 :         return true;
     818              :     }
     819              : 
     820            0 :     if (index.model() == m_masterPaletteModel) {
     821            0 :         QMimeData* data = m_masterPaletteModel->mimeData({ QModelIndex(index) });
     822            0 :         QModelIndex dropIndex = m_userPaletteModel->index(0, 0);
     823              : 
     824            0 :         const bool success = m_userPaletteModel->dropMimeData(data, Qt::CopyAction, dropIndex.row(), dropIndex.column(), QModelIndex());
     825            0 :         if (success) {
     826            0 :             m_userPaletteModel->setData(dropIndex, true, PaletteTreeModel::VisibleRole);
     827              :         }
     828              : 
     829            0 :         data->deleteLater();
     830            0 :         return success;
     831              :     }
     832              : 
     833            0 :     return false;
     834              : }
     835              : 
     836            0 : void PaletteProvider::resetPalette(const QModelIndex& index)
     837              : {
     838            0 :     if (!index.isValid()) {
     839            0 :         return;
     840              :     }
     841              : 
     842              :     std::string title = muse::trc("palette",
     843            0 :                                   "Do you want to restore this palette to its default state? All changes to this palette will be lost.");
     844              : 
     845            0 :     interactive()->question("", title, {
     846              :         IInteractive::Button::No, IInteractive::Button::Yes
     847              :     })
     848            0 :     .onResolve(this, [this, index](const IInteractive::Result& res) {
     849            0 :         if (res.isButton(IInteractive::Button::Yes)) {
     850            0 :             doResetPalette(index);
     851              :         }
     852            0 :     });
     853            0 : }
     854              : 
     855            0 : void PaletteProvider::doResetPalette(const QModelIndex& index)
     856              : {
     857            0 :     Q_ASSERT(m_defaultPaletteModel != m_userPaletteModel);
     858              : 
     859            0 :     QAbstractItemModel* resetModel = nullptr;
     860            0 :     QModelIndex resetIndex;
     861              : 
     862            0 :     if (m_defaultPaletteModel) {
     863            0 :         resetModel = m_defaultPaletteModel;
     864            0 :         resetIndex = convertIndex(index, m_defaultPaletteModel);
     865              :     }
     866              : 
     867            0 :     if (!resetIndex.isValid()) {
     868            0 :         resetModel = m_masterPaletteModel;
     869            0 :         resetIndex = convertIndex(index, m_masterPaletteModel);
     870              :     }
     871              : 
     872            0 :     const QModelIndex userPaletteIndex = convertProxyIndex(index, m_userPaletteModel);
     873            0 :     const QModelIndex parent = userPaletteIndex.parent();
     874            0 :     const int row = userPaletteIndex.row();
     875            0 :     const int column = userPaletteIndex.column();
     876              : 
     877              :     // restore visibility and expanded state of the palette after restoring its state
     878            0 :     const bool wasVisible = index.data(PaletteTreeModel::VisibleRole).toBool();
     879            0 :     const bool wasExpanded = index.data(PaletteTreeModel::PaletteExpandedRole).toBool();
     880              : 
     881            0 :     if (!m_userPaletteModel->removeRow(row, parent)) {
     882            0 :         return;
     883              :     }
     884              : 
     885            0 :     if (resetIndex.isValid()) {
     886            0 :         QMimeData* data = resetModel->mimeData({ resetIndex });
     887            0 :         m_userPaletteModel->dropMimeData(data, Qt::CopyAction, row, column, parent);
     888            0 :         data->deleteLater();
     889              :     } else {
     890            0 :         m_userPaletteModel->insertRow(row, parent);
     891              :     }
     892              : 
     893            0 :     const QModelIndex newIndex = m_userPaletteModel->index(row, column, parent);
     894            0 :     m_userPaletteModel->setData(newIndex, wasVisible, PaletteTreeModel::VisibleRole);
     895            0 :     m_userPaletteModel->setData(newIndex, wasExpanded, PaletteTreeModel::PaletteExpandedRole);
     896              : }
     897              : 
     898            0 : QString PaletteProvider::getPaletteFilename(bool open, const QString& name) const
     899              : {
     900            0 :     std::string title;
     901            0 :     std::vector<std::string> filter;
     902              : #ifdef WIN_PORTABLE
     903              :     QString wd = QDir::cleanPath(QString("%1/../../../Data/settings").arg(QCoreApplication::applicationDirPath()));
     904              : #else
     905            0 :     QString wd = QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::HomeLocation))
     906            0 :                  .arg(QCoreApplication::applicationName());
     907              : #endif
     908            0 :     if (open) {
     909            0 :         title  = muse::trc("palette", "Load palette");
     910            0 :         filter = { muse::trc("palette", "MuseScore Studio palette") + " (*.mpal)" };
     911              :     } else {
     912            0 :         title  = muse::trc("palette", "Save palette");
     913            0 :         filter = { muse::trc("palette", "MuseScore Studio palette") + " (*.mpal)" };
     914              :     }
     915              : 
     916            0 :     QFileInfo myPalettes(wd);
     917            0 :     QString defaultPath = myPalettes.absoluteFilePath();
     918            0 :     if (!name.isEmpty()) {
     919            0 :         QString fname = muse::io::escapeFileName(name).toQString();
     920            0 :         QFileInfo myName(fname);
     921            0 :         if (myName.isRelative()) {
     922            0 :             myName.setFile(defaultPath, fname);
     923              :         }
     924            0 :         defaultPath = myName.absoluteFilePath();
     925            0 :     }
     926              : 
     927            0 :     muse::io::path_t fn;
     928            0 :     if (open) {
     929            0 :         fn = interactive()->selectOpeningFileSync(title, defaultPath, filter);
     930              :     } else {
     931            0 :         fn = interactive()->selectSavingFileSync(title, defaultPath, filter);
     932              :     }
     933            0 :     return fn.toQString();
     934            0 : }
     935              : 
     936            0 : bool PaletteProvider::savePalette(const QModelIndex& index)
     937              : {
     938            0 :     const QModelIndex srcIndex = convertProxyIndex(index, m_userPaletteModel);
     939            0 :     const Palette* pp = m_userPaletteModel->findPalette(srcIndex);
     940            0 :     if (!pp) {
     941            0 :         return false;
     942              :     }
     943              : 
     944            0 :     const QString path = getPaletteFilename(/* load? */ false, pp->translatedName());
     945              : 
     946            0 :     if (path.isEmpty()) {
     947            0 :         return false;
     948              :     }
     949            0 :     return pp->writeToFile(path);
     950            0 : }
     951              : 
     952            0 : bool PaletteProvider::loadPalette(const QModelIndex& index)
     953              : {
     954            0 :     const QString path = getPaletteFilename(/* load? */ true);
     955            0 :     if (path.isEmpty()) {
     956            0 :         return false;
     957              :     }
     958              : 
     959            0 :     PalettePtr pp = std::make_shared<Palette>(iocContext());
     960            0 :     if (!pp->readFromFile(path)) {
     961            0 :         return false;
     962              :     }
     963            0 :     pp->setType(Palette::Type::Custom);   // mark the loaded palette custom
     964              : 
     965            0 :     const QModelIndex srcIndex = convertProxyIndex(index, m_userPaletteModel);
     966              : 
     967            0 :     const int row = srcIndex.row();
     968            0 :     const QModelIndex parent = srcIndex.parent();
     969              : 
     970            0 :     return m_userPaletteModel->insertPalette(pp, row, parent);
     971            0 : }
     972              : 
     973            0 : void PaletteProvider::setUserPaletteTree(PaletteTreePtr tree)
     974              : {
     975            0 :     if (m_userPaletteModel) {
     976            0 :         disconnect(m_userPaletteModel, &PaletteTreeModel::treeChanged, this, &PaletteProvider::notifyAboutUserPaletteChanged);
     977            0 :         m_userPaletteModel->setPaletteTree(tree);
     978            0 :         connect(m_userPaletteModel, &PaletteTreeModel::treeChanged, this, &PaletteProvider::notifyAboutUserPaletteChanged);
     979              :     } else {
     980            0 :         m_userPaletteModel = new PaletteTreeModel(tree, iocContext(), /* parent */ this);
     981            0 :         connect(m_userPaletteModel, &PaletteTreeModel::treeChanged, this, &PaletteProvider::notifyAboutUserPaletteChanged);
     982              :     }
     983            0 : }
     984              : 
     985            0 : void PaletteProvider::setDefaultPaletteTree(PaletteTreePtr tree)
     986              : {
     987            0 :     if (m_defaultPaletteModel) {
     988            0 :         m_defaultPaletteModel->setPaletteTree(tree);
     989              :     } else {
     990            0 :         m_defaultPaletteModel = new PaletteTreeModel(tree, iocContext(), /* parent */ this);
     991              :     }
     992            0 : }
     993              : 
     994            0 : muse::async::Channel<ElementPtr> PaletteProvider::addCustomItemRequested() const
     995              : {
     996            0 :     return m_addCustomItemRequested;
     997              : }
     998              : 
     999            0 : void PaletteProvider::write(XmlWriter& xml, bool pasteMode) const
    1000              : {
    1001            0 :     if (!m_userPaletteModel) {
    1002            0 :         return;
    1003              :     }
    1004            0 :     if (const PaletteTree* tree = m_userPaletteModel->paletteTree()) {
    1005            0 :         tree->write(xml, pasteMode);
    1006              :     }
    1007              : }
    1008              : 
    1009            0 : bool PaletteProvider::read(XmlReader& e, bool pasteMode)
    1010              : {
    1011            0 :     PaletteTreePtr tree = std::make_shared<PaletteTree>();
    1012            0 :     if (!tree->read(e, pasteMode, iocContext())) {
    1013            0 :         return false;
    1014              :     }
    1015              : 
    1016            0 :     setUserPaletteTree(tree);
    1017              : 
    1018            0 :     return true;
    1019            0 : }
        

Generated by: LCOV version 2.0-1