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 : #include "propertiespanelabstractproxymodel.h"
23 :
24 : #include "propertiespanelabstractmodel.h"
25 : #include "propertiespanelmodelfactory.h"
26 :
27 : using namespace mu::propertiespanel;
28 :
29 0 : PropertiesPanelAbstractProxyModel::PropertiesPanelAbstractProxyModel(QObject* parent, const muse::modularity::ContextPtr& iocCtx,
30 0 : IElementRepositoryService* repository)
31 0 : : PropertiesPanelAbstractModel(parent, iocCtx, repository)
32 : {
33 0 : }
34 :
35 0 : QVariantList PropertiesPanelAbstractProxyModel::models() const
36 : {
37 0 : QVariantList objects;
38 :
39 0 : for (PropertiesPanelAbstractModel* model : modelList()) {
40 0 : objects << QVariant::fromValue(qobject_cast<QObject*>(model));
41 0 : }
42 :
43 0 : return objects;
44 0 : }
45 :
46 0 : PropertiesPanelAbstractModel* PropertiesPanelAbstractProxyModel::modelByType(PropertiesPanelModelType type) const
47 : {
48 0 : return m_models.value(type);
49 : }
50 :
51 0 : PropertiesPanelAbstractModel* PropertiesPanelAbstractProxyModel::firstModel() const
52 : {
53 0 : if (m_models.empty()) {
54 0 : return nullptr;
55 : }
56 :
57 0 : return m_models.values().first();
58 : }
59 :
60 0 : PropertiesPanelModelType PropertiesPanelAbstractProxyModel::defaultSubModelType() const
61 : {
62 0 : return m_defaultSubModelType;
63 : }
64 :
65 0 : void PropertiesPanelAbstractProxyModel::setDefaultSubModelType(PropertiesPanelModelType modelType)
66 : {
67 0 : if (m_defaultSubModelType == modelType) {
68 0 : return;
69 : }
70 :
71 0 : m_defaultSubModelType = modelType;
72 0 : emit defaultSubModelTypeChanged();
73 : }
74 :
75 0 : bool PropertiesPanelAbstractProxyModel::isMultiModel() const
76 : {
77 0 : return m_models.count() > 1;
78 : }
79 :
80 0 : void PropertiesPanelAbstractProxyModel::requestElements()
81 : {
82 0 : for (PropertiesPanelAbstractModel* model : modelList()) {
83 0 : model->requestElements();
84 0 : }
85 0 : }
86 :
87 0 : void PropertiesPanelAbstractProxyModel::disconnectAll()
88 : {
89 0 : for (PropertiesPanelAbstractModel* model : modelList()) {
90 0 : model->disconnectAll();
91 0 : }
92 :
93 0 : PropertiesPanelAbstractModel::disconnectAll();
94 0 : }
95 :
96 0 : bool PropertiesPanelAbstractProxyModel::isEmpty() const
97 : {
98 0 : for (const PropertiesPanelAbstractModel* model : modelList()) {
99 0 : if (!model->isEmpty()) {
100 0 : return false;
101 : }
102 0 : }
103 :
104 0 : return true;
105 : }
106 :
107 0 : void PropertiesPanelAbstractProxyModel::setModels(const QList<PropertiesPanelAbstractModel*>& models)
108 : {
109 0 : QList<PropertiesPanelAbstractModel*> currentModels = modelList();
110 :
111 0 : for (PropertiesPanelAbstractModel* model : currentModels) {
112 0 : if (models.contains(model)) {
113 0 : continue;
114 : }
115 :
116 0 : auto oldModel = m_models.take(model->modelType());
117 :
118 : //! NOTE: may run synchronously from a model's own property-change callback;
119 : //! deleting immediately would destroy "this" mid-call, so defer it.
120 : //! Disconnect now so it (and its submodels) doesn't react to further updates
121 : //! while it awaits destruction
122 0 : oldModel->disconnectAll();
123 0 : oldModel->deleteLater();
124 : }
125 :
126 0 : for (PropertiesPanelAbstractModel* model : models) {
127 0 : if (!model) {
128 0 : continue;
129 : }
130 :
131 0 : PropertiesPanelModelType modelType = model->modelType();
132 :
133 0 : if (m_models.contains(modelType)) {
134 0 : continue;
135 : }
136 :
137 0 : connect(model, &PropertiesPanelAbstractModel::isEmptyChanged, this, [this]() {
138 0 : emit isEmptyChanged();
139 0 : });
140 :
141 0 : connect(model, &PropertiesPanelAbstractModel::requestReloadPropertiesPanelListModel, this,
142 0 : &PropertiesPanelAbstractModel::requestReloadPropertiesPanelListModel);
143 :
144 0 : m_models[modelType] = model;
145 : }
146 :
147 0 : emit modelsChanged();
148 0 : }
149 :
150 0 : void PropertiesPanelAbstractProxyModel::onCurrentNotationChanged()
151 : {
152 0 : for (PropertiesPanelAbstractModel* model : modelList()) {
153 0 : model->onCurrentNotationChanged();
154 0 : }
155 :
156 0 : PropertiesPanelAbstractModel::onCurrentNotationChanged();
157 0 : }
158 :
159 0 : void PropertiesPanelAbstractProxyModel::onNotationChanged(const engraving::PropertyIdSet& changedPropertyIdSet,
160 : const engraving::StyleIdSet& changedStyleIdSet)
161 : {
162 0 : for (PropertiesPanelAbstractModel* model : modelList()) {
163 0 : if (!model->shouldUpdateOnScoreChange()) {
164 0 : continue;
165 : }
166 :
167 0 : if (!model->shouldUpdateWhenEmpty() && model->isEmpty()) {
168 0 : continue;
169 : }
170 :
171 0 : if (!model->shouldUpdateOnEmptyPropertyAndStyleIdSets()) {
172 0 : if (changedPropertyIdSet.empty() && changedStyleIdSet.empty()) {
173 0 : continue;
174 : }
175 : }
176 :
177 0 : mu::engraving::PropertyIdSet expandedPropertyIdSet = model->propertyIdSetFromStyleIdSet(changedStyleIdSet);
178 0 : expandedPropertyIdSet.insert(changedPropertyIdSet.cbegin(), changedPropertyIdSet.cend());
179 0 : model->onNotationChanged(expandedPropertyIdSet, changedStyleIdSet);
180 0 : }
181 :
182 0 : PropertiesPanelAbstractModel::onNotationChanged(changedPropertyIdSet, changedStyleIdSet);
183 0 : }
184 :
185 0 : void PropertiesPanelAbstractProxyModel::updateModels(const ElementKeySet& newElementKeySet)
186 : {
187 0 : QList<PropertiesPanelAbstractModel*> models;
188 :
189 0 : for (const ElementKey& elementKey : newElementKeySet) {
190 0 : PropertiesPanelModelType modelType = PropertiesPanelAbstractModel::modelTypeByElementKey(elementKey);
191 :
192 0 : if (modelType == PropertiesPanelModelType::TYPE_UNDEFINED) {
193 0 : continue;
194 : }
195 :
196 0 : auto model = dynamic_cast<PropertiesPanelAbstractModel*>(modelByType(modelType));
197 :
198 0 : if (!model) {
199 0 : model = PropertiesPanelModelFactory::newPropertiesPanelListModel(modelType, this, iocContext(), m_repository);
200 : }
201 :
202 0 : if (model) {
203 0 : models << model;
204 : }
205 : }
206 :
207 0 : setModels(models);
208 0 : }
209 :
210 0 : bool PropertiesPanelAbstractProxyModel::shouldUpdateWhenEmpty() const
211 : {
212 0 : for (const PropertiesPanelAbstractModel* model : modelList()) {
213 0 : if (model->shouldUpdateWhenEmpty()) {
214 0 : return true;
215 : }
216 0 : }
217 0 : return false;
218 : }
219 :
220 0 : bool PropertiesPanelAbstractProxyModel::shouldUpdateOnEmptyPropertyAndStyleIdSets() const
221 : {
222 0 : for (const PropertiesPanelAbstractModel* model : modelList()) {
223 0 : if (model->shouldUpdateOnEmptyPropertyAndStyleIdSets()) {
224 0 : return true;
225 : }
226 0 : }
227 0 : return false;
228 : }
229 :
230 0 : QList<PropertiesPanelAbstractModel*> PropertiesPanelAbstractProxyModel::modelList() const
231 : {
232 0 : return m_models.values();
233 : }
|