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 "projectmigrator.h"
23 :
24 : #include <QVersionNumber>
25 :
26 : #include "io/path.h"
27 : #include "mdlmigrator.h"
28 :
29 : #include "engraving/dom/excerpt.h"
30 : #include "engraving/dom/masterscore.h"
31 : #include "engraving/editing/editscoreproperties.h"
32 : #include "engraving/editing/editstyle.h"
33 : #include "engraving/editing/transaction/transaction.h"
34 : #include "engraving/rw/compat/readstyle.h"
35 : #include "engraving/types/constants.h"
36 :
37 : #include "io/file.h"
38 :
39 : #include "muse_framework_config.h"
40 :
41 : #include "log.h"
42 :
43 : using namespace mu;
44 : using namespace mu::project;
45 : using namespace mu::engraving;
46 : using namespace mu::engraving::compat;
47 : using namespace muse;
48 :
49 : static const Uri MIGRATION_DIALOG_URI("musescore://project/migration");
50 : static const io::path_t LELAND_STYLE_PATH(":/engraving/styles/migration-306-style-Leland.mss");
51 : static const io::path_t EDWIN_STYLE_PATH(":/engraving/styles/migration-306-style-Edwin.mss");
52 :
53 0 : static MigrationType migrationTypeFromMscVersion(int mscVersion)
54 : {
55 0 : if (mscVersion < 302) {
56 0 : return MigrationType::Pre_3_6;
57 : }
58 :
59 0 : if (mscVersion < 400) {
60 0 : return MigrationType::Ver_3_6;
61 : }
62 :
63 0 : UNREACHABLE;
64 : return MigrationType::Unknown;
65 : }
66 :
67 0 : Ret ProjectMigrator::migrateEngravingProjectIfNeed(engraving::EngravingProjectPtr project)
68 : {
69 0 : if (project->mscVersion() >= 400) {
70 0 : return true;
71 : }
72 : //! NOTE If the migration is not done, then the default style for the score is determined by the version.
73 : //! When migrating, the version becomes the current one, so remember the version of the default style before migrating
74 0 : project->masterScore()->style().setDefaultStyleVersion(ReadStyleHook::styleDefaultByMscVersion(project->mscVersion()));
75 0 : MigrationType migrationType = migrationTypeFromMscVersion(project->mscVersion());
76 :
77 0 : MigrationOptions migrationOptions = configuration()->migrationOptions(migrationType);
78 0 : if (migrationOptions.isAskAgain) {
79 0 : Ret ret = askAboutMigration(migrationOptions, project->appVersion(), migrationType);
80 :
81 0 : if (!ret) {
82 0 : return ret;
83 : }
84 :
85 0 : configuration()->setMigrationOptions(migrationType, migrationOptions);
86 0 : }
87 :
88 0 : if (!migrationOptions.isApplyMigration) {
89 0 : return true;
90 : }
91 :
92 0 : Ret ret = migrateProject(project, migrationOptions);
93 0 : if (!ret) {
94 0 : LOGE() << "failed migration";
95 : } else {
96 0 : LOGI() << "success migration";
97 : }
98 :
99 0 : return ret;
100 0 : }
101 :
102 0 : Ret ProjectMigrator::askAboutMigration(MigrationOptions& out, const QString& appVersion, MigrationType migrationType)
103 : {
104 0 : UriQuery query(MIGRATION_DIALOG_URI);
105 0 : query.addParam("appVersion", Val(appVersion));
106 0 : query.addParam("migrationType", Val(migrationType));
107 0 : query.addParam("isApplyLeland", Val(out.isApplyLeland));
108 0 : query.addParam("isApplyEdwin", Val(out.isApplyEdwin));
109 0 : query.addParam("isRemapPercussion", Val(out.isRemapPercussion));
110 :
111 : #ifndef MUSE_MODULE_INTERACTIVE_SYNC_SUPPORTED
112 : //! NOTE If there is no support for synchronous interactivity (web)
113 : //! Then we will migrate without questions
114 :
115 : out.appVersion = mu::engraving::Constants::MSC_VERSION;
116 : out.isApplyMigration = true;
117 : out.isAskAgain = false;
118 : out.isApplyLeland = true;
119 : out.isApplyEdwin = true;
120 : out.isRemapPercussion = true;
121 : #else
122 0 : RetVal<Val> rv = interactive()->openSync(query);
123 0 : if (!rv.ret) {
124 0 : return rv.ret;
125 : }
126 :
127 0 : QVariantMap vals = rv.val.toQVariant().toMap();
128 0 : out.appVersion = mu::engraving::Constants::MSC_VERSION;
129 0 : out.isApplyMigration = vals.value("isApplyMigration").toBool();
130 0 : out.isAskAgain = vals.value("isAskAgain").toBool();
131 0 : out.isApplyLeland = vals.value("isApplyLeland").toBool();
132 0 : out.isApplyEdwin = vals.value("isApplyEdwin").toBool();
133 0 : out.isRemapPercussion = vals.value("isRemapPercussion").toBool();
134 : #endif
135 :
136 0 : return true;
137 0 : }
138 :
139 0 : Ret ProjectMigrator::migrateProject(engraving::EngravingProjectPtr project, const MigrationOptions& opt)
140 : {
141 0 : TRACEFUNC;
142 :
143 0 : mu::engraving::MasterScore* score = project->masterScore();
144 0 : IF_ASSERT_FAILED(score) {
145 0 : return make_ret(Ret::Code::InternalError);
146 : }
147 :
148 0 : score->startCmd(TranslatableString("undoableAction", "Migrate project"));
149 :
150 0 : bool ok = true;
151 0 : if (opt.isApplyLeland) {
152 0 : ok = applyLelandStyle(score);
153 : }
154 :
155 0 : if (ok && opt.isApplyEdwin) {
156 0 : ok = applyEdwinStyle(score);
157 : }
158 :
159 0 : if (ok && opt.isRemapPercussion) {
160 0 : MdlMigrator(score).remapPercussion();
161 : }
162 :
163 0 : score->endCmd();
164 :
165 0 : return ok ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
166 0 : }
167 :
168 0 : bool ProjectMigrator::applyLelandStyle(mu::engraving::MasterScore* score)
169 : {
170 0 : muse::io::File styleFile(LELAND_STYLE_PATH);
171 0 : mu::engraving::Transaction& tx = score->transactionManager()->currentOrDummyTransaction();
172 0 : for (mu::engraving::Excerpt* excerpt : score->excerpts()) {
173 0 : if (!mu::engraving::EditStyle::loadStyle(tx, excerpt->excerptScore(), styleFile, /*ign*/ false, /*overlap*/ true)) {
174 0 : return false;
175 : }
176 : }
177 :
178 0 : return mu::engraving::EditStyle::loadStyle(tx, score, styleFile, /*ign*/ false, /*overlap*/ true);
179 0 : }
180 :
181 0 : bool ProjectMigrator::applyEdwinStyle(mu::engraving::MasterScore* score)
182 : {
183 0 : muse::io::File styleFile(EDWIN_STYLE_PATH);
184 0 : mu::engraving::Transaction& tx = score->transactionManager()->currentOrDummyTransaction();
185 0 : for (mu::engraving::Excerpt* excerpt : score->excerpts()) {
186 0 : if (!mu::engraving::EditStyle::loadStyle(tx, excerpt->excerptScore(), styleFile, /*ign*/ false, /*overlap*/ true)) {
187 0 : return false;
188 : }
189 : }
190 :
191 0 : return mu::engraving::EditStyle::loadStyle(tx, score, styleFile, /*ign*/ false, /*overlap*/ true);
192 0 : }
|