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) 2026 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 "singleprocessprovider.h"
24 :
25 : #include <QCoreApplication>
26 :
27 : #include "../../iprojectprovider.h"
28 : #include "ui/imainwindow.h"
29 : #include "rcommand/icommanddispatcher.h"
30 :
31 : #include "multiwindowscommands.h"
32 :
33 : #include "log.h"
34 :
35 : using namespace muse;
36 : using namespace muse::mi;
37 :
38 : static const std::string pname = "mi";
39 :
40 0 : size_t SingleProcessProvider::windowCount() const
41 : {
42 0 : return std::max(static_cast<size_t>(1), application()->contextCount());
43 : }
44 :
45 0 : bool SingleProcessProvider::isFirstWindow() const
46 : {
47 0 : return windowCount() <= 1;
48 : }
49 :
50 0 : std::shared_ptr<IProjectProvider> SingleProcessProvider::projectProvider(const modularity::ContextPtr& ctx) const
51 : {
52 0 : return modularity::ioc(ctx)->resolve<IProjectProvider>(pname);
53 : }
54 :
55 0 : std::shared_ptr<ui::IMainWindow> SingleProcessProvider::mainWindow(const modularity::ContextPtr& ctx) const
56 : {
57 0 : return modularity::ioc(ctx)->resolve<ui::IMainWindow>(pname);
58 : }
59 :
60 0 : std::shared_ptr<rcommand::ICommandDispatcher> SingleProcessProvider::commandDispatcher(const modularity::ContextPtr& ctx) const
61 : {
62 0 : return modularity::ioc(ctx)->resolve<rcommand::ICommandDispatcher>(pname);
63 : }
64 :
65 0 : bool SingleProcessProvider::isProjectAlreadyOpened(const muse::io::path_t& projectPath) const
66 : {
67 0 : for (const auto& ctx : application()->contexts()) {
68 0 : std::shared_ptr<IProjectProvider> pp = projectProvider(ctx);
69 0 : if (!pp) {
70 0 : LOGW() << "Not found implementation of IProjectProvider for context: " << ctx->id;
71 0 : continue;
72 : }
73 :
74 0 : if (pp->isProjectOpened(projectPath)) {
75 0 : return true;
76 : }
77 0 : }
78 0 : return false;
79 : }
80 :
81 0 : void SingleProcessProvider::activateWindowWithProject(const muse::io::path_t& projectPath)
82 : {
83 0 : for (const auto& ctx : application()->contexts()) {
84 0 : std::shared_ptr<IProjectProvider> pp = projectProvider(ctx);
85 0 : if (!pp) {
86 0 : LOGW() << "Not found implementation of IProjectProvider for context: " << ctx->id;
87 0 : continue;
88 : }
89 :
90 0 : if (!pp->isProjectOpened(projectPath)) {
91 0 : continue;
92 : }
93 :
94 0 : std::shared_ptr<ui::IMainWindow> w = mainWindow(ctx);
95 0 : IF_ASSERT_FAILED(w) {
96 0 : continue;
97 : }
98 :
99 0 : w->requestShowOnFront();
100 0 : break;
101 0 : }
102 0 : }
103 :
104 0 : bool SingleProcessProvider::isHasWindowWithoutProject() const
105 : {
106 0 : for (const auto& ctx : application()->contexts()) {
107 0 : std::shared_ptr<IProjectProvider> pp = projectProvider(ctx);
108 0 : if (!pp) {
109 0 : LOGW() << "Not found implementation of IProjectProvider for context: " << ctx->id;
110 0 : continue;
111 : }
112 :
113 0 : if (!pp->isAnyProjectOpened()) {
114 0 : return true;
115 : }
116 0 : }
117 0 : return false;
118 : }
119 :
120 0 : void SingleProcessProvider::activateWindowWithoutProject(const QStringList& args)
121 : {
122 0 : for (const auto& ctx : application()->contexts()) {
123 0 : std::shared_ptr<IProjectProvider> pp = projectProvider(ctx);
124 0 : if (!pp) {
125 0 : LOGW() << "Not found implementation of IProjectProvider for context: " << ctx->id;
126 0 : continue;
127 : }
128 :
129 0 : if (pp->isAnyProjectOpened()) {
130 0 : continue;
131 : }
132 :
133 0 : std::shared_ptr<ui::IMainWindow> w = mainWindow(ctx);
134 0 : IF_ASSERT_FAILED(w) {
135 0 : continue;
136 : }
137 :
138 0 : w->requestShowOnFront();
139 0 : if (args.count() > 0 && !args.at(0).isEmpty()) {
140 0 : std::shared_ptr<rcommand::ICommandDispatcher> d = commandDispatcher(ctx);
141 0 : IF_ASSERT_FAILED(d) {
142 0 : break;
143 : }
144 0 : d->dispatch(rcommand::CommandQuery(args.at(0).toStdString()));
145 0 : }
146 :
147 0 : break;
148 0 : }
149 0 : }
150 :
151 0 : bool SingleProcessProvider::openNewWindow(const QStringList& args)
152 : {
153 0 : LOGDA() << args;
154 :
155 0 : application()->setupNewContext(args);
156 :
157 0 : return true;
158 : }
159 :
160 0 : async::Promise<Ret> SingleProcessProvider::quitForAll(const modularity::ContextPtr& ctx)
161 : {
162 0 : const std::vector<modularity::ContextPtr> all = application()->contexts();
163 :
164 0 : std::vector<modularity::ContextPtr> others;
165 0 : for (auto it = all.crbegin(); it != all.crend(); ++it) {
166 0 : const modularity::ContextPtr& c = *it;
167 0 : if (ctx && c->id == ctx->id) {
168 0 : continue;
169 : }
170 :
171 0 : others.push_back(c);
172 : }
173 :
174 : //! NOTE The current window quits first, then the others one by one
175 0 : quitWindow(ctx);
176 :
177 0 : return quitWindows(others);
178 0 : }
179 :
180 0 : async::Promise<Ret> SingleProcessProvider::quitWindows(const std::vector<modularity::ContextPtr>& ctxs)
181 : {
182 0 : return async::make_promise<Ret>([this, ctxs](auto resolve) {
183 0 : if (ctxs.empty()) {
184 0 : return resolve(make_ok());
185 : }
186 :
187 0 : std::shared_ptr<rcommand::ICommandDispatcher> cd = commandDispatcher(ctxs.front());
188 0 : IF_ASSERT_FAILED(cd) {
189 0 : return resolve(make_ret(Ret::Code::InternalError));
190 : }
191 :
192 0 : std::shared_ptr<ui::IMainWindow> w = mainWindow(ctxs.front());
193 0 : IF_ASSERT_FAILED(w) {
194 0 : return resolve(make_ret(Ret::Code::InternalError));
195 : }
196 :
197 : //! NOTE Bring the window to the front, so that the user is asked about the window they see
198 0 : w->requestShowOnFront();
199 :
200 0 : std::vector<modularity::ContextPtr> rest(ctxs.cbegin() + 1, ctxs.cend());
201 :
202 0 : cd->dispatch(APP_QUIT_COMMAND, { { "all_instances", Val(false) } })
203 0 : .onResolve(this, [this, rest, resolve](const rcommand::Response& response) {
204 0 : if (!response.ret) {
205 0 : LOGD() << "quit for all canceled: " << response.ret.toString();
206 0 : (void)resolve(response.ret);
207 0 : return;
208 : }
209 :
210 0 : quitWindows(rest).onResolve(this, [resolve](const Ret& ret) {
211 0 : (void)resolve(ret);
212 : });
213 : });
214 :
215 0 : return async::Promise<Ret>::dummy_result();
216 0 : });
217 : }
218 :
219 0 : void SingleProcessProvider::quitWindow(const modularity::ContextPtr& ctx)
220 : {
221 0 : if (application()->contextCount() <= 1) {
222 0 : QCoreApplication::exit();
223 : } else {
224 0 : application()->destroyContext(ctx);
225 : }
226 0 : }
|