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 <QGuiApplication>
24 :
25 : #include "preferencesmodel.h"
26 :
27 : #include "translation.h"
28 : #include "ui/view/iconcodes.h"
29 :
30 : #include "muse_framework_config.h"
31 :
32 : using namespace mu::preferences;
33 : using namespace muse::ui;
34 :
35 0 : PreferencesModel::PreferencesModel(QObject* parent)
36 0 : : QAbstractItemModel(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
37 : {
38 0 : }
39 :
40 0 : PreferencesModel::~PreferencesModel()
41 : {
42 0 : cancel();
43 :
44 0 : delete m_rootItem;
45 0 : m_rootItem = nullptr;
46 0 : }
47 :
48 0 : QModelIndex PreferencesModel::index(int row, int column, const QModelIndex& parent) const
49 : {
50 0 : if (!hasIndex(row, column, parent)) {
51 0 : return QModelIndex();
52 : }
53 :
54 0 : PreferencePageItem* parentItem = nullptr;
55 :
56 0 : if (!parent.isValid()) {
57 0 : parentItem = m_rootItem;
58 : } else {
59 0 : parentItem = modelIndexToItem(parent);
60 : }
61 :
62 0 : if (!parentItem) {
63 0 : return QModelIndex();
64 : }
65 :
66 0 : PreferencePageItem* childItem = parentItem->childAtRow(row);
67 :
68 0 : if (childItem) {
69 0 : return createIndex(row, column, childItem);
70 : }
71 :
72 0 : return QModelIndex();
73 : }
74 :
75 0 : QModelIndex PreferencesModel::parent(const QModelIndex& child) const
76 : {
77 0 : PreferencePageItem* childItem = modelIndexToItem(child);
78 0 : if (!childItem) {
79 0 : return QModelIndex();
80 : }
81 :
82 0 : PreferencePageItem* parentItem = qobject_cast<PreferencePageItem*>(childItem->parentItem());
83 :
84 0 : if (parentItem == m_rootItem) {
85 0 : return QModelIndex();
86 : }
87 :
88 0 : return createIndex(parentItem->row(), 0, parentItem);
89 : }
90 :
91 0 : int PreferencesModel::rowCount(const QModelIndex& parent) const
92 : {
93 0 : PreferencePageItem* parentItem = nullptr;
94 :
95 0 : if (!parent.isValid()) {
96 0 : parentItem = m_rootItem;
97 : } else {
98 0 : parentItem = modelIndexToItem(parent);
99 : }
100 :
101 0 : if (!parentItem) {
102 0 : return 0;
103 : }
104 :
105 0 : return parentItem->childCount();
106 : }
107 :
108 0 : int PreferencesModel::columnCount(const QModelIndex&) const
109 : {
110 0 : return 1;
111 : }
112 :
113 0 : QVariant PreferencesModel::data(const QModelIndex& index, int role) const
114 : {
115 0 : if (!index.isValid() && role != ItemRole) {
116 0 : return QVariant();
117 : }
118 :
119 0 : PreferencePageItem* item = modelIndexToItem(index);
120 :
121 0 : if (!item) {
122 0 : return QVariant();
123 : }
124 :
125 0 : return QVariant::fromValue(qobject_cast<QObject*>(item));
126 : }
127 :
128 0 : QHash<int, QByteArray> PreferencesModel::roleNames() const
129 : {
130 0 : return { { ItemRole, "item" } };
131 0 : }
132 :
133 0 : QString PreferencesModel::currentPageId() const
134 : {
135 0 : return m_currentPageId;
136 : }
137 :
138 0 : void PreferencesModel::load(const QString& currentPageId)
139 : {
140 0 : configuration()->startEditSettings();
141 :
142 0 : beginResetModel();
143 :
144 0 : if (!currentPageId.isEmpty()) {
145 0 : setCurrentPageId(currentPageId);
146 : } else {
147 0 : const QString& lastOpenedPageId = configuration()->preferencesDialogLastOpenedPageId();
148 0 : if (lastOpenedPageId.isEmpty()) {
149 0 : setCurrentPageId("general");
150 : } else {
151 0 : setCurrentPageId(lastOpenedPageId);
152 : }
153 : }
154 :
155 0 : m_rootItem = new PreferencePageItem();
156 :
157 : QList<PreferencePageItem*> items {
158 0 : makeItem("general", QT_TRANSLATE_NOOP("preferences", "General"), IconCode::Code::SETTINGS_COG,
159 : "Preferences/GeneralPreferencesPage.qml"),
160 :
161 0 : makeItem("appearance", QT_TRANSLATE_NOOP("preferences", "Appearance"), IconCode::Code::EYE_OPEN,
162 : "Preferences/AppearancePreferencesPage.qml"),
163 :
164 0 : makeItem("canvas", QT_TRANSLATE_NOOP("preferences", "Canvas"), IconCode::Code::NEW_FILE,
165 : "Preferences/CanvasPreferencesPage.qml"),
166 :
167 0 : makeItem("cloud", QT_TRANSLATE_NOOP("preferences", "Save & publish"), IconCode::Code::CLOUD_FILE,
168 : "Preferences/SaveAndPublishPreferencesPage.qml"),
169 :
170 0 : makeItem("note-input", QT_TRANSLATE_NOOP("preferences", "Note input"), IconCode::Code::EDIT,
171 : "Preferences/NoteInputPreferencesPage.qml"),
172 :
173 0 : makeItem("score", QT_TRANSLATE_NOOP("preferences", "Score"), IconCode::Code::SCORE,
174 : "Preferences/ScorePreferencesPage.qml"),
175 :
176 0 : makeItem("audio-midi", QT_TRANSLATE_NOOP("preferences", "Audio & MIDI"), IconCode::Code::AUDIO,
177 : "Preferences/AudioMidiPreferencesPage.qml"),
178 :
179 : #ifdef MUSE_MODULE_MIDIREMOTE
180 0 : makeItem("midi-device-mapping", QT_TRANSLATE_NOOP("preferences", "MIDI mappings"), IconCode::Code::MIDI_INPUT,
181 : "Preferences/MidiDeviceMappingPreferencesPage.qml"),
182 : #endif
183 :
184 0 : makeItem("video", QT_TRANSLATE_NOOP("preferences", "Video"), IconCode::Code::VIDEO,
185 : "Preferences/VideoPreferencesPage.qml"),
186 :
187 0 : makeItem("percussion", QT_TRANSLATE_NOOP("preferences", "Percussion"), IconCode::Code::PERCUSSION,
188 : "Preferences/PercussionPreferencesPage.qml"),
189 :
190 0 : makeItem("import", QT_TRANSLATE_NOOP("preferences", "Import"), IconCode::Code::IMPORT,
191 : "Preferences/ImportPreferencesPage.qml"),
192 :
193 0 : makeItem("shortcuts", QT_TRANSLATE_NOOP("preferences", "Shortcuts"), IconCode::Code::SHORTCUTS,
194 : "Preferences/ShortcutsPreferencesPage.qml"),
195 :
196 0 : makeItem("general-folders", QT_TRANSLATE_NOOP("preferences", "Folders"), IconCode::Code::OPEN_FILE,
197 : "Preferences/FoldersPreferencesPage.qml"),
198 :
199 0 : makeItem("advanced", QT_TRANSLATE_NOOP("preferences", "Advanced"), IconCode::Code::CONFIGURE,
200 : "Preferences/AdvancedPreferencesPage.qml"),
201 :
202 0 : makeItem("braille", QT_TRANSLATE_NOOP("preferences", "Braille"), IconCode::Code::BRAILLE,
203 : "Preferences/BraillePreferencesPage.qml")
204 0 : };
205 :
206 0 : for (PreferencePageItem* item: items) {
207 0 : m_rootItem->appendChild(item);
208 : }
209 :
210 0 : endResetModel();
211 0 : }
212 :
213 0 : void PreferencesModel::askForConfirmationOfPreferencesReset()
214 : {
215 0 : std::string title = muse::trc("appshell", "Are you sure you want to reset preferences?");
216 : std::string question = muse::trc("appshell", "This action will reset all your app preferences and delete all custom shortcuts. "
217 : "It will not delete any of your scores.\n\n"
218 0 : "This action cannot be undone.");
219 :
220 0 : muse::IInteractive::ButtonData cancelBtn = interactive()->buttonData(muse::IInteractive::Button::Cancel);
221 0 : muse::IInteractive::ButtonData resetBtn = interactive()->buttonData(muse::IInteractive::Button::Reset);
222 0 : cancelBtn.accent = true;
223 :
224 0 : interactive()->warning(title, question, { cancelBtn, resetBtn }, cancelBtn.btn,
225 : { muse::IInteractive::Option::WithIcon },
226 0 : muse::trc("appshell", "Reset preferences"))
227 0 : .onResolve(this, [this](const muse::IInteractive::Result& res) {
228 0 : if (res.isButton(muse::IInteractive::Button::Reset)) {
229 0 : emit userAgreesToResetPreferences();
230 : }
231 0 : });
232 0 : }
233 :
234 0 : void PreferencesModel::resetFactorySettings()
235 : {
236 : static constexpr bool KEEP_DEFAULT_SETTINGS = true;
237 0 : QGuiApplication::setOverrideCursor(Qt::WaitCursor);
238 0 : QGuiApplication::processEvents();
239 0 : configuration()->revertToFactorySettings(KEEP_DEFAULT_SETTINGS);
240 :
241 : // Unreset the "First Launch Completed" setting so the first-time launch wizard does not appear.
242 0 : configuration()->setHasCompletedFirstLaunchSetup(true);
243 :
244 0 : configuration()->startEditSettings();
245 0 : QGuiApplication::restoreOverrideCursor();
246 0 : }
247 :
248 0 : void PreferencesModel::apply()
249 : {
250 0 : configuration()->applySettings();
251 0 : }
252 :
253 0 : void PreferencesModel::cancel()
254 : {
255 0 : configuration()->rollbackSettings();
256 0 : }
257 :
258 0 : void PreferencesModel::selectRow(const QModelIndex& rowIndex)
259 : {
260 0 : QModelIndex parentItemIndex = parent(rowIndex);
261 0 : PreferencePageItem* parentItem = nullptr;
262 0 : if (!parentItemIndex.isValid()) {
263 0 : parentItem = m_rootItem;
264 : } else {
265 0 : parentItem = modelIndexToItem(parentItemIndex);
266 : }
267 :
268 0 : QList<PreferencePageItem*> children = parentItem->childrenItems();
269 0 : for (PreferencePageItem* child: children) {
270 0 : child->setExpanded(false);
271 : }
272 :
273 0 : PreferencePageItem* selectedItem = parentItem->childAtRow(rowIndex.row());
274 0 : if (!selectedItem) {
275 0 : return;
276 : }
277 :
278 0 : selectedItem->setExpanded(true);
279 0 : setCurrentPageId(selectedItem->id());
280 0 : }
281 :
282 0 : QVariantList PreferencesModel::availablePages() const
283 : {
284 0 : std::function<QVariantList(const PreferencePageItem*)> childPages;
285 0 : childPages = [&childPages](const PreferencePageItem* item) {
286 0 : QVariantList result;
287 :
288 0 : for (int i = 0; i < item->childCount(); ++i) {
289 0 : PreferencePageItem* child = item->childAtRow(i);
290 0 : QVariantMap childObj;
291 0 : childObj["id"] = child->id();
292 0 : childObj["path"] = child->path();
293 0 : result << childObj;
294 :
295 0 : QVariantList pages = childPages(child);
296 0 : for (const QVariant& page: pages) {
297 0 : result << page;
298 : }
299 0 : }
300 :
301 0 : return result;
302 0 : };
303 :
304 0 : return childPages(m_rootItem);
305 0 : }
306 :
307 0 : void PreferencesModel::setCurrentPageId(QString currentPageId)
308 : {
309 0 : if (m_currentPageId == currentPageId) {
310 0 : return;
311 : }
312 :
313 0 : m_currentPageId = currentPageId;
314 0 : configuration()->setPreferencesDialogLastOpenedPageId(currentPageId);
315 0 : emit currentPageIdChanged(m_currentPageId);
316 : }
317 :
318 0 : PreferencePageItem* PreferencesModel::makeItem(const QString& id, const QString& title, muse::ui::IconCode::Code icon,
319 : const QString& path,
320 : const QList<PreferencePageItem*>& children) const
321 : {
322 0 : PreferencePageItem* item = new PreferencePageItem();
323 0 : item->setId(id);
324 0 : item->setTitle(title);
325 0 : item->setIcon(icon);
326 0 : item->setPath(path);
327 0 : item->setExpanded(id == currentPageId());
328 :
329 0 : for (PreferencePageItem* child: children) {
330 0 : item->appendChild(child);
331 :
332 0 : if (child->id() == currentPageId()) {
333 0 : item->setExpanded(true);
334 : }
335 : }
336 :
337 0 : return item;
338 : }
339 :
340 0 : PreferencePageItem* PreferencesModel::modelIndexToItem(const QModelIndex& index) const
341 : {
342 0 : return static_cast<PreferencePageItem*>(index.internalPointer());
343 : }
|