Skip to content

Commit e138427

Browse files
Merge pull request #26974 from Jojo-Schmitz/4.5.0-ports
4.5.0 ports
2 parents bdccb18 + 5be32e4 commit e138427

11 files changed

+170
-78
lines changed

src/appshell/qml/NotationPage/NotationPage.qml

+1-1
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ DockPage {
619619
"title": qsTrc("notation", "Note input modes"),
620620
"description": qsTrc("notation", "Discover different ways to input notes in MuseScore Studio."),
621621
"controlUri": "control://NoteInputSection/NoteInputBar/note-input-by-duration",
622-
"videoExplanationUrl": "https://www.youtube.com/watch?v=OUXf7Y2CPQE&t"
622+
"videoExplanationUrl": "https://youtu.be/xm1-XkS9VzA?utm_source=mss-yt&utm_medium=enter-by-duration&utm_campaign=mss-yt-enter-by-duration"
623623
}
624624
]
625625
}

src/appshell/view/notationpagemodel.cpp

+17-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "internal/applicationuiactions.h"
2525
#include "dockwindow/idockwindow.h"
2626

27+
#include "async/async.h"
28+
2729
#include "log.h"
2830

2931
using namespace mu::appshell;
@@ -71,6 +73,10 @@ void NotationPageModel::init()
7173
updateDrumsetPanelVisibility();
7274
updatePercussionPanelVisibility();
7375
});
76+
77+
notationConfiguration()->percussionPanelAutoShowModeChanged().onNotify(this, [this]() {
78+
updatePercussionPanelVisibility();
79+
});
7480
}
7581

7682
QString NotationPageModel::notationToolBarName() const
@@ -209,7 +215,11 @@ void NotationPageModel::updateDrumsetPanelVisibility()
209215
if (open == window->isDockOpenAndCurrentInFrame(DRUMSET_PANEL_NAME)) {
210216
return;
211217
}
212-
dispatcher()->dispatch("dock-set-open", ActionData::make_arg2<QString, bool>(DRUMSET_PANEL_NAME, open));
218+
219+
//! NOTE: ensure we don't dispatch it multiple times in succession
220+
muse::async::Async::call(this, [=]() {
221+
dispatcher()->dispatch("dock-set-open", ActionData::make_arg2<QString, bool>(DRUMSET_PANEL_NAME, open));
222+
});
213223
};
214224

215225
// This should never be open when the new percussion panel is in use...
@@ -245,7 +255,11 @@ void NotationPageModel::updatePercussionPanelVisibility()
245255
if (open == window->isDockOpenAndCurrentInFrame(PERCUSSION_PANEL_NAME)) {
246256
return;
247257
}
248-
dispatcher()->dispatch("dock-set-open", ActionData::make_arg2<QString, bool>(PERCUSSION_PANEL_NAME, open));
258+
259+
//! NOTE: ensure we don't dispatch it multiple times in succession
260+
muse::async::Async::call(this, [=]() {
261+
dispatcher()->dispatch("dock-set-open", ActionData::make_arg2<QString, bool>(PERCUSSION_PANEL_NAME, open));
262+
});
249263
};
250264

251265
// This should never be open when the old drumset panel is in use...
@@ -255,14 +269,13 @@ void NotationPageModel::updatePercussionPanelVisibility()
255269
}
256270

257271
const PercussionPanelAutoShowMode autoShowMode = notationConfiguration()->percussionPanelAutoShowMode();
258-
const bool autoClose = notationConfiguration()->autoClosePercussionPanel();
259-
260272
const INotationPtr notation = globalContext()->currentNotation();
261273
if (!notation || !notation->elements() || autoShowMode == PercussionPanelAutoShowMode::NEVER) {
262274
return;
263275
}
264276

265277
const INotationNoteInputPtr noteInput = notation->interaction()->noteInput();
278+
const bool autoClose = notationConfiguration()->autoClosePercussionPanel();
266279
if (noteInput && !noteInput->isNoteInputMode() && autoShowMode == PercussionPanelAutoShowMode::UNPITCHED_STAFF_NOTE_INPUT) {
267280
if (autoClose) {
268281
setPercussionPanelOpen(false);

src/converter/internal/convertercontroller.cpp

+95-34
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "internal/converterutils.h"
3535

3636
#include "log.h"
37+
#include <iostream>
3738

3839
using namespace mu::converter;
3940
using namespace mu::project;
@@ -44,6 +45,7 @@ using namespace muse::io;
4445
static const std::string PDF_SUFFIX = "pdf";
4546
static const std::string PNG_SUFFIX = "png";
4647
static const std::string SVG_SUFFIX = "svg";
48+
static const std::string MP3_SUFFIX = "mp3";
4749

4850
Ret ConverterController::batchConvert(const muse::io::path_t& batchJobFile, const muse::io::path_t& stylePath, bool forceMode,
4951
const String& soundProfile, const muse::UriQuery& extensionUri, muse::ProgressPtr progress)
@@ -131,12 +133,6 @@ Ret ConverterController::fileConvert(const muse::io::path_t& in, const muse::io:
131133
return make_ret(Err::UnknownError);
132134
}
133135

134-
std::string suffix = io::suffix(out);
135-
auto writer = writers()->writer(suffix);
136-
if (!writer) {
137-
return make_ret(Err::ConvertTypeUnknown);
138-
}
139-
140136
Ret ret = notationProject->load(in, stylePath, forceMode);
141137
if (!ret) {
142138
LOGE() << "failed load notation, err: " << ret.toString() << ", path: " << in;
@@ -158,6 +154,19 @@ Ret ConverterController::fileConvert(const muse::io::path_t& in, const muse::io:
158154

159155
globalContext()->setCurrentProject(notationProject);
160156

157+
// Check if this is a part conversion job
158+
QString baseName = QString::fromStdString(io::completeBasename(out).toStdString());
159+
if (baseName.contains('*')) {
160+
return convertScoreParts(in, out, stylePath, forceMode);
161+
}
162+
163+
std::string suffix = io::suffix(out);
164+
165+
auto writer = writers()->writer(suffix);
166+
if (!writer) {
167+
return make_ret(Err::ConvertTypeUnknown);
168+
}
169+
161170
// use a extension for convert
162171
if (extensionUri.isValid()) {
163172
ret = convertByExtension(writer, notationProject->masterNotation()->notation(), out, extensionUri);
@@ -215,6 +224,8 @@ Ret ConverterController::convertScoreParts(const muse::io::path_t& in, const mus
215224
ret = convertScorePartsToPdf(writer, notationProject->masterNotation(), out);
216225
} else if (suffix == PNG_SUFFIX) {
217226
ret = convertScorePartsToPngs(writer, notationProject->masterNotation(), out);
227+
} else if (suffix == MP3_SUFFIX) {
228+
ret = convertScorePartsToMp3(writer, notationProject->masterNotation(), out);
218229
} else {
219230
ret = make_ret(Ret::Code::NotSupported);
220231
}
@@ -252,7 +263,6 @@ RetVal<ConverterController::BatchJob> ConverterController::parseBatchJob(const m
252263

253264
Job job;
254265
job.in = correctUserInputPath(obj["in"].toString());
255-
job.out = correctUserInputPath(obj["out"].toString());
256266

257267
QJsonObject transposeOptionsObj = obj["transpose"].toObject();
258268
if (!transposeOptionsObj.isEmpty()) {
@@ -261,12 +271,27 @@ RetVal<ConverterController::BatchJob> ConverterController::parseBatchJob(const m
261271
rv.ret = transposeOptions.ret;
262272
return rv;
263273
}
264-
265274
job.transposeOptions = transposeOptions.val;
266275
}
267276

268-
if (!job.in.empty() && !job.out.empty()) {
277+
QJsonValue outValue = obj["out"];
278+
if (outValue.isString()) {
279+
job.out = correctUserInputPath(outValue.toString());
269280
rv.val.push_back(std::move(job));
281+
} else if (outValue.isArray()) {
282+
QJsonArray outArray = outValue.toArray();
283+
for (const QJsonValue& outItem : outArray) {
284+
Job partJob = job; // Copy the input path
285+
if (outItem.isString()) {
286+
partJob.out = correctUserInputPath(outItem.toString());
287+
} else if (outItem.isArray() && outItem.toArray().size() == 2) {
288+
QJsonArray partOutArray = outItem.toArray();
289+
QString prefix = correctUserInputPath(partOutArray[0].toString());
290+
QString suffix = partOutArray[1].toString();
291+
partJob.out = prefix + "*" + suffix; // Use "*" as a placeholder for part names
292+
}
293+
rv.val.push_back(std::move(partJob));
294+
}
270295
}
271296
}
272297

@@ -368,28 +393,32 @@ Ret ConverterController::convertScorePartsToPdf(INotationWriterPtr writer, IMast
368393
TRACEFUNC;
369394

370395
INotationPtrList notations;
371-
notations.push_back(masterNotation->notation());
372-
373396
for (IExcerptNotationPtr e : masterNotation->excerpts()) {
374397
notations.push_back(e->notation());
375398
}
376399

377-
File file(out);
378-
if (!file.open(File::WriteOnly)) {
379-
return make_ret(Err::OutFileFailedOpen);
380-
}
400+
for (size_t i = 0; i < notations.size(); ++i) {
401+
QString partName = notations[i]->name();
402+
QString baseName = QString::fromStdString(io::completeBasename(out).toStdString());
403+
muse::io::path_t partOut = io::dirpath(out) + "/" + baseName.replace("*", partName).toStdString() + ".pdf";
381404

382-
INotationWriter::Options options {
383-
{ INotationWriter::OptionKey::UNIT_TYPE, Val(INotationWriter::UnitType::MULTI_PART) },
384-
};
405+
File file(partOut);
406+
if (!file.open(File::WriteOnly)) {
407+
return make_ret(Err::OutFileFailedOpen);
408+
}
385409

386-
Ret ret = writer->writeList(notations, file, options);
387-
if (!ret) {
388-
LOGE() << "failed write, err: " << ret.toString() << ", path: " << out;
389-
return make_ret(Err::OutFileFailedWrite);
390-
}
410+
INotationWriter::Options options {
411+
{ INotationWriter::OptionKey::UNIT_TYPE, Val(INotationWriter::UnitType::PER_PART) },
412+
};
391413

392-
file.close();
414+
Ret ret = writer->write(notations[i], file, options);
415+
if (!ret) {
416+
LOGE() << "failed write, err: " << ret.toString() << ", path: " << partOut;
417+
return make_ret(Err::OutFileFailedWrite);
418+
}
419+
420+
file.close();
421+
}
393422

394423
return make_ret(Ret::Code::Ok);
395424
}
@@ -399,23 +428,55 @@ Ret ConverterController::convertScorePartsToPngs(INotationWriterPtr writer, mu::
399428
{
400429
TRACEFUNC;
401430

402-
Ret ret = convertPageByPage(writer, masterNotation->notation(), out);
403-
if (!ret) {
404-
return ret;
431+
INotationPtrList notations;
432+
for (IExcerptNotationPtr e : masterNotation->excerpts()) {
433+
notations.push_back(e->notation());
405434
}
406435

407-
INotationPtrList excerpts;
436+
for (size_t i = 0; i < notations.size(); i++) {
437+
QString partName = notations[i]->name();
438+
QString baseName = QString::fromStdString(io::completeBasename(out).toStdString());
439+
muse::io::path_t pngFilePath = io::dirpath(out) + "/" + baseName.replace("*", partName).toStdString() + ".png";
440+
Ret ret2 = convertPageByPage(writer, notations[i], pngFilePath);
441+
if (!ret2) {
442+
return ret2;
443+
}
444+
}
445+
446+
return make_ret(Ret::Code::Ok);
447+
}
448+
449+
Ret ConverterController::convertScorePartsToMp3(INotationWriterPtr writer, IMasterNotationPtr masterNotation,
450+
const muse::io::path_t& out) const
451+
{
452+
TRACEFUNC;
453+
454+
INotationPtrList notations;
408455
for (IExcerptNotationPtr e : masterNotation->excerpts()) {
409-
excerpts.push_back(e->notation());
456+
notations.push_back(e->notation());
410457
}
411458

412-
muse::io::path_t pngFilePath = io::dirpath(out) + "/" + muse::io::path_t(io::completeBasename(out) + "-excerpt.png");
459+
for (size_t i = 0; i < notations.size(); ++i) {
460+
QString partName = notations[i]->name();
461+
QString baseName = QString::fromStdString(io::completeBasename(out).toStdString());
462+
muse::io::path_t partOut = io::dirpath(out) + "/" + baseName.replace("*", partName).toStdString() + ".mp3";
413463

414-
for (size_t i = 0; i < excerpts.size(); i++) {
415-
Ret ret2 = convertPageByPage(writer, excerpts[i], pngFilePath);
416-
if (!ret2) {
417-
return ret2;
464+
File file(partOut);
465+
if (!file.open(File::WriteOnly)) {
466+
return make_ret(Err::OutFileFailedOpen);
467+
}
468+
469+
INotationWriter::Options options {
470+
{ INotationWriter::OptionKey::UNIT_TYPE, Val(INotationWriter::UnitType::PER_PART) },
471+
};
472+
file.setMeta("file_path", partOut.toStdString());
473+
Ret ret = writer->write(notations[i], file, options);
474+
if (!ret) {
475+
LOGE() << "failed write, err: " << ret.toString() << ", path: " << partOut;
476+
return make_ret(Err::OutFileFailedWrite);
418477
}
478+
479+
file.close();
419480
}
420481

421482
return make_ret(Ret::Code::Ok);

src/converter/internal/convertercontroller.h

+2
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,7 @@ class ConverterController : public IConverterController, public muse::Injectable
103103
const muse::io::path_t& out) const;
104104
muse::Ret convertScorePartsToPngs(project::INotationWriterPtr writer, notation::IMasterNotationPtr masterNotation,
105105
const muse::io::path_t& out) const;
106+
muse::Ret convertScorePartsToMp3(project::INotationWriterPtr writer, notation::IMasterNotationPtr masterNotation,
107+
const muse::io::path_t& out) const;
106108
};
107109
}

src/engraving/dom/paste.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -500,9 +500,6 @@ std::vector<EngravingItem*> Score::cmdPaste(const IMimeData* ms, MuseScoreView*
500500
}
501501
}
502502
}
503-
if (!droppedElements.empty()) {
504-
select(droppedElements.back());
505-
}
506503
} else if (ms->hasFormat(mimeStaffListFormat)) {
507504
ChordRest* cr = 0;
508505
if (m_selection.isRange()) {

src/framework/shortcuts/internal/platform/macos/macosshortcutsinstancemodel.mm

+16-14
Original file line numberDiff line numberDiff line change
@@ -379,27 +379,29 @@ static QKeySequence translateToCurrentKeyboardLayout(const QKeySequence& sequenc
379379

380380
// Ensure standard order of modifiers by converting to/from QKeySequence
381381
QKeySequence untranslatedSequence = QKeySequence::fromString(untranslatedSequenceStr, QKeySequence::PortableText);
382-
QString untranslatedSequenceStrNormalised = untranslatedSequence.toString(QKeySequence::PortableText);
383-
384-
// Always record the untranslated sequence
385-
// Map to non-normalised, because that's what ShortcutsInstanceModel::doActivate expects
386-
recordMapping(untranslatedSequenceStrNormalised, untranslatedSequenceStr, true);
387-
recordAutoRepeat(untranslatedSequenceStrNormalised, sc.autoRepeat);
388382

389383
// Attempt to translate from combination of keys to character, e.g., `Shift+.` becomes `>`, in the case of a QWERTY layout
390384
QKeySequence translatedSequence
391385
= translateToCurrentKeyboardLayout(untranslatedSequence);
392-
if (translatedSequence.isEmpty()) {
393-
LOGW() << "Failed to translate sequence " << untranslatedSequenceStr;
394-
continue;
386+
if (translatedSequence.isEmpty() || !(untranslatedSequence[0].key() & 0xff) || untranslatedSequence[0].key() == Qt::Key_A) {
387+
QString untranslatedSequenceStrNormalised = untranslatedSequence.toString(QKeySequence::PortableText);
388+
389+
// Record the untranslated sequence
390+
// Map to non-normalised, because that's what ShortcutsInstanceModel::doActivate expects
391+
recordMapping(untranslatedSequenceStrNormalised, untranslatedSequenceStr, true);
392+
recordAutoRepeat(untranslatedSequenceStrNormalised, sc.autoRepeat);
395393
}
396394

397-
QString translatedSequenceStrNormalised = translatedSequence.toString(QKeySequence::PortableText);
395+
if (translatedSequence.isEmpty()) {
396+
LOGW() << "Failed to translate sequence " << untranslatedSequenceStr;
397+
} else {
398+
QString translatedSequenceStrNormalised = translatedSequence.toString(QKeySequence::PortableText);
398399

399-
// If it was successful, record the translated sequence too, and map it to the untranslated sequence
400-
// Again, map to non-normalised
401-
recordMapping(translatedSequenceStrNormalised, untranslatedSequenceStr, false);
402-
recordAutoRepeat(translatedSequenceStrNormalised, sc.autoRepeat);
400+
// If it was successful, record the translated sequence too, and map it to the untranslated sequence
401+
// Again, map to non-normalised
402+
recordMapping(translatedSequenceStrNormalised, untranslatedSequenceStr, false);
403+
recordAutoRepeat(translatedSequenceStrNormalised, sc.autoRepeat);
404+
}
403405
}
404406
}
405407

src/notation/internal/notationactioncontroller.cpp

+9-9
Original file line numberDiff line numberDiff line change
@@ -2397,9 +2397,9 @@ void NotationActionController::registerAction(const ActionCode& code,
23972397
{
23982398
registerAction(code, [this, handler, playMode]()
23992399
{
2400-
auto interaction = currentNotationInteraction().get();
2401-
if (interaction) {
2402-
(interaction->*handler)();
2400+
INotationPtr notation = currentNotation();
2401+
if (notation) {
2402+
(notation->interaction().get()->*handler)();
24032403
if (playMode != PlayMode::NoPlay) {
24042404
playSelectedElement(playMode == PlayMode::PlayChord);
24052405
}
@@ -2427,9 +2427,9 @@ void NotationActionController::registerAction(const ActionCode& code, void (INot
24272427
{
24282428
registerAction(code, [this, handler, param1, playMode]()
24292429
{
2430-
auto interaction = currentNotationInteraction().get();
2431-
if (interaction) {
2432-
(interaction->*handler)(param1);
2430+
INotationPtr notation = currentNotation();
2431+
if (notation) {
2432+
(notation->interaction().get()->*handler)(param1);
24332433
if (playMode != PlayMode::NoPlay) {
24342434
playSelectedElement(playMode == PlayMode::PlayChord);
24352435
}
@@ -2451,9 +2451,9 @@ void NotationActionController::registerAction(const ActionCode& code, void (INot
24512451
{
24522452
registerAction(code, [this, handler, param1, param2, playMode]()
24532453
{
2454-
auto interaction = currentNotationInteraction().get();
2455-
if (interaction) {
2456-
(interaction->*handler)(param1, param2);
2454+
INotationPtr notation = currentNotation();
2455+
if (notation) {
2456+
(notation->interaction().get()->*handler)(param1, param2);
24572457
if (playMode != PlayMode::NoPlay) {
24582458
playSelectedElement(playMode == PlayMode::PlayChord);
24592459
}

0 commit comments

Comments
 (0)