Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2025 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 "musesamplercheckstatusscenario.h"
24 :
25 : #include "rcommand/commandtypes.h"
26 : #include "translation.h"
27 : #include "ui/view/iconcodes.h"
28 :
29 : using namespace mu::musesounds;
30 :
31 8 : bool MuseSamplerCheckStatusScenario::alreadyChecked() const
32 : {
33 8 : return m_alreadyChecked;
34 : }
35 :
36 14 : void MuseSamplerCheckStatusScenario::checkAndShowStatusIfNeed()
37 : {
38 14 : if (!multiwindowsProvider()->isFirstWindow()) {
39 10 : return;
40 : }
41 :
42 13 : if (service()->libInitFailed()) {
43 7 : m_alreadyChecked = true;
44 7 : if (configuration()->showLoadingFailedWarning()) {
45 6 : showLoadingFailedNotification();
46 : }
47 7 : return;
48 : }
49 :
50 6 : if (!service()->canCheckForUpdate()) {
51 1 : return;
52 : }
53 :
54 5 : LOGI() << "Checking for MuseSampler update...";
55 5 : m_alreadyChecked = true;
56 :
57 5 : if (service()->incompatibleLocalVersion()) {
58 1 : showCriticalUpdateNotification();
59 1 : return;
60 : }
61 :
62 4 : auto promise = service()->checkForUpdate();
63 4 : promise.onResolve(this, [this](const muse::RetVal<bool>& res) {
64 4 : if (!res.ret) {
65 1 : LOGE() << "Unable to check for MuseSampler update: " << res.ret.toString();
66 1 : return;
67 : }
68 :
69 3 : if (res.val) {
70 2 : showNewVersionNotification();
71 : }
72 : });
73 4 : }
74 :
75 6 : void MuseSamplerCheckStatusScenario::showLoadingFailedNotification()
76 : {
77 6 : muse::IInteractive::ButtonData closeBtn = interactive()->buttonData(muse::IInteractive::Button::Close);
78 : muse::IInteractive::ButtonData troubleshootBtn(int(muse::IInteractive::Button::CustomButton) + 1,
79 6 : muse::trc("musesounds", "Troubleshoot"));
80 6 : troubleshootBtn.icon = int(muse::ui::IconCode::Code::OPEN_LINK);
81 :
82 : #ifdef Q_OS_LINUX
83 : muse::IInteractive::ButtonData openBtn(int(muse::IInteractive::Button::CustomButton) + 2,
84 6 : muse::trc("musesounds", "Open MuseSounds Manager"), true /*accent*/);
85 :
86 : std::string msg = muse::trc("musesounds", "To load MuseSounds, open MuseSounds Manager and check that you’re running the latest version. "
87 : "Update if needed and restart your machine.\n\n"
88 6 : "If the issue persists, follow the steps at the troubleshooting link below.");
89 : #else
90 : muse::IInteractive::ButtonData openBtn(int(muse::IInteractive::Button::CustomButton) + 2,
91 : muse::trc("musesounds", "Open MuseHub"), true /*accent*/);
92 :
93 : std::string msg = muse::trc("musesounds", "To load MuseSounds, open MuseHub and check that you’re running the latest version. "
94 : "Update if needed and restart your machine.\n\n"
95 : "If the issue persists, follow the steps at the troubleshooting link below.");
96 : #endif
97 :
98 : #ifdef Q_OS_WIN
99 : const muse::IInteractive::ButtonDatas buttons { openBtn, troubleshootBtn, closeBtn };
100 : #else
101 30 : const muse::IInteractive::ButtonDatas buttons { closeBtn, troubleshootBtn, openBtn };
102 : #endif
103 :
104 18 : interactive()->warning(muse::trc("musesounds", "MuseSounds could not be loaded"), msg,
105 : buttons, openBtn.btn,
106 12 : muse::IInteractive::Option::WithIcon | muse::IInteractive::Option::WithDontShowAgainCheckBox)
107 6 : .onResolve(this, [this, troubleshootBtn, openBtn](const muse::IInteractive::Result& res) {
108 6 : if (!res.showAgain()) {
109 2 : configuration()->setShowLoadingFailedWarning(false);
110 : }
111 :
112 6 : if (res.isButton(troubleshootBtn.btn)) {
113 2 : platformInteractive()->openUrl(configuration()->museSoundsTroubleshootUrl());
114 4 : } else if (res.isButton(openBtn.btn)) {
115 1 : openMuseHub(false /*quit*/);
116 : }
117 6 : });
118 12 : }
119 :
120 1 : void MuseSamplerCheckStatusScenario::showCriticalUpdateNotification()
121 : {
122 : muse::IInteractive::ButtonData notNowBtn(int(muse::IInteractive::Button::CustomButton) + 1,
123 1 : muse::trc("musesounds", "Not now"));
124 :
125 : #ifdef Q_OS_LINUX
126 : muse::IInteractive::ButtonData launchBtn(int(muse::IInteractive::Button::CustomButton) + 2,
127 1 : muse::trc("musesounds", "Quit & launch MuseSounds Manager"), true /*accent*/);
128 :
129 : std::string msg = muse::trc("musesounds", "To apply this update, MuseScore Studio will need to close briefly and MuseSounds Manager will open. "
130 1 : "Your MuseSounds libraries won’t work until the update is complete.");
131 : #else
132 : muse::IInteractive::ButtonData launchBtn(int(muse::IInteractive::Button::CustomButton) + 2,
133 : muse::trc("musesounds", "Quit & launch MuseHub"), true /*accent*/);
134 :
135 : std::string msg = muse::trc("musesounds", "To apply this update, MuseScore Studio will need to close briefly and MuseHub will open. "
136 : "Your MuseSounds libraries won’t work until the update is complete.");
137 : #endif
138 :
139 6 : interactive()->info(muse::trc("musesounds", "MuseSounds needs an update"), msg,
140 : { notNowBtn, launchBtn }, launchBtn.btn, muse::IInteractive::Option::WithIcon)
141 1 : .onResolve(this, [this, launchBtn](const muse::IInteractive::Result& res) {
142 1 : if (res.isButton(launchBtn.btn)) {
143 0 : openMuseHub(true /*quit*/);
144 : }
145 1 : });
146 2 : }
147 :
148 2 : void MuseSamplerCheckStatusScenario::showNewVersionNotification()
149 : {
150 : muse::IInteractive::ButtonData notNowBtn(int(muse::IInteractive::Button::CustomButton) + 1,
151 2 : muse::trc("musesounds", "Not now"));
152 :
153 : #ifdef Q_OS_LINUX
154 : muse::IInteractive::ButtonData launchBtn(int(muse::IInteractive::Button::CustomButton) + 2,
155 2 : muse::trc("musesounds", "Quit & launch MuseSounds Manager"), true /*accent*/);
156 :
157 : std::string msg = muse::trc("musesounds", "To keep MuseSounds running smoothly, "
158 : "MuseScore Studio needs to close briefly so MuseSounds Manager can apply the update. "
159 2 : "You’ll need to restart MuseScore Studio when the update is complete.");
160 : #else
161 : muse::IInteractive::ButtonData launchBtn(int(muse::IInteractive::Button::CustomButton) + 2,
162 : muse::trc("musesounds", "Quit & launch MuseHub"), true /*accent*/);
163 :
164 : std::string msg = muse::trc("musesounds", "To keep MuseSounds running smoothly, "
165 : "MuseScore Studio needs to close briefly so MuseHub can apply the update. "
166 : "You’ll be prompted to relaunch MuseScore Studio when it’s ready.");
167 : #endif
168 :
169 12 : interactive()->info(muse::trc("musesounds", "An update for MuseSounds is available"), msg,
170 : { notNowBtn, launchBtn }, launchBtn.btn, muse::IInteractive::Option::WithIcon)
171 2 : .onResolve(this, [this, launchBtn](const muse::IInteractive::Result& res) {
172 2 : if (res.isButton(launchBtn.btn)) {
173 0 : openMuseHub(true /*quit*/);
174 : }
175 2 : });
176 4 : }
177 :
178 1 : void MuseSamplerCheckStatusScenario::openMuseHub(bool quit)
179 : {
180 : #ifdef Q_OS_LINUX
181 5 : if (process()->startDetached("muse-sounds-manager",
182 : { "--show-sampler-update", "--show-hidden-statuses" })) {
183 1 : if (quit) {
184 0 : commandDispatcher()->dispatch(muse::rcommand::Command("command://app/quit"));
185 : }
186 : } else {
187 0 : openMuseHubWebsite(quit);
188 : }
189 : #else
190 : const muse::UriQuery MUSEHUB_URI("musehub://requestUpdateCheck?from=musescore-studio");
191 :
192 : #ifdef Q_OS_MACOS
193 : if (!platformInteractive()->canOpenApp(MUSEHUB_URI)) {
194 : openMuseHubWebsite(quit);
195 : return;
196 : }
197 : #endif
198 :
199 : auto promise = platformInteractive()->openApp(MUSEHUB_URI);
200 :
201 : promise.onResolve(this, [this, quit](const muse::Ret& ret) {
202 : if (!ret) {
203 : openMuseHubWebsite(quit);
204 : } else if (quit) {
205 : commandDispatcher()->dispatch(muse::rcommand::Command("command://app/quit"));
206 : }
207 : });
208 :
209 : promise.onReject(this, [this, quit](int, const std::string&) {
210 : openMuseHubWebsite(quit);
211 : });
212 : #endif
213 1 : }
214 :
215 0 : void MuseSamplerCheckStatusScenario::openMuseHubWebsite(bool quit)
216 : {
217 : #ifdef Q_OS_LINUX
218 0 : platformInteractive()->openUrl(updateConfiguration()->appWebSiteUrl());
219 : #else
220 : platformInteractive()->openUrl(globalConfiguration()->museHubWebUrl());
221 : #endif
222 :
223 0 : if (quit) {
224 0 : commandDispatcher()->dispatch(muse::rcommand::Command("command://app/quit"));
225 : }
226 0 : }
|