Skip to content

Commit 91a038c

Browse files
committed
Fix #23792 Add/remove page breaks
1 parent dfe2a47 commit 91a038c

16 files changed

+428
-0
lines changed

src/appshell/view/appmenumodel.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ MenuItem* AppMenuModel::makeFormatMenu()
318318
makeMenuItem("page-settings"),
319319
makeSeparator(),
320320
makeMenuItem("measures-per-system"),
321+
makeMenuItem("add-remove-page-breaks"),
321322
makeMenu(TranslatableString("appshell/menu/format", "Str&etch"), stretchItems, "menu-stretch"),
322323
makeSeparator(),
323324
makeMenuItem("reset-text-style-overrides"),

src/engraving/dom/cmd.cpp

+72
Original file line numberDiff line numberDiff line change
@@ -4407,6 +4407,78 @@ void Score::addRemoveSystemLocks(int interval, bool lock)
44074407
}
44084408
}
44094409

4410+
void Score::addRemovePageBreaks(int interval, bool afterEachPage)
4411+
{
4412+
Segment* startSegment = selection().startSegment();
4413+
if (!startSegment) { // empty score?
4414+
return;
4415+
}
4416+
Segment* endSegment = selection().endSegment();
4417+
Measure* startMeasure = startSegment->measure();
4418+
Measure* endMeasure = endSegment ? endSegment->measure() : lastMeasureMM();
4419+
Measure* lastMeasure = lastMeasureMM();
4420+
4421+
// loop through measures in selection
4422+
// First system
4423+
int sCount = 1;
4424+
for (Measure* mm = startMeasure; mm; mm = mm->nextMeasureMM()) {
4425+
// even though we are counting mmrests as a single measure,
4426+
// we need to find last real measure within mmrest for the actual break
4427+
Measure* m = mm->isMMRest() ? mm->mmRestLast() : mm;
4428+
4429+
if (afterEachPage) {
4430+
// skip last measure of score
4431+
if (mm == lastMeasure) {
4432+
break;
4433+
}
4434+
// skip if it already has a page break
4435+
if (m->pageBreak()) {
4436+
continue;
4437+
}
4438+
// add break if last measure of the last system of the page
4439+
if (mm->system() && mm->system()->lastMeasure() == mm && mm->nextMeasureMM() && mm->nextMeasureMM()->system()
4440+
&& mm->system()->page() != mm->nextMeasureMM()->system()->page()) {
4441+
m->undoSetPageBreak(true);
4442+
}
4443+
} else {
4444+
if (interval == 0) {
4445+
// remove page break if present
4446+
if (m->pageBreak()) {
4447+
m->undoSetPageBreak(false);
4448+
}
4449+
} else {
4450+
if (sCount == interval) {
4451+
// skip last measure of score and measures that aren't the last of the System
4452+
if (mm == lastMeasure) {
4453+
break;
4454+
}
4455+
4456+
// found place for break
4457+
if (mm->system()->lastMeasure() == mm) {
4458+
// add if not already one present
4459+
if (!m->pageBreak()) {
4460+
m->undoSetPageBreak(true);
4461+
}
4462+
// reset count
4463+
sCount = 1;
4464+
}
4465+
} else if (m->pageBreak()) {
4466+
// remove page break if present in wrong place
4467+
m->undoSetPageBreak(false);
4468+
}
4469+
// count if the last Measure of the system
4470+
if (!m->pageBreak() && mm->system() && mm->system()->lastMeasure() == mm) {
4471+
++sCount;
4472+
}
4473+
}
4474+
}
4475+
4476+
if (mm == endMeasure) {
4477+
break;
4478+
}
4479+
}
4480+
}
4481+
44104482
//---------------------------------------------------------
44114483
// cmdRemoveEmptyTrailingMeasures
44124484
//---------------------------------------------------------

src/engraving/dom/score.h

+2
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ class Score : public EngravingObject, public muse::Injectable
405405

406406
void addRemoveSystemLocks(int interval, bool lock);
407407

408+
void addRemovePageBreaks(int interval, bool afterEachPage);
409+
408410
bool transpose(Note* n, Interval, bool useSharpsFlats);
409411
void transposeKeys(staff_idx_t staffStart, staff_idx_t staffEnd, const Fraction& tickStart, const Fraction& tickEnd, bool flip = false);
410412
bool transpose(TransposeMode mode, TransposeDirection, Key transposeKey, int transposeInterval, bool trKeys, bool transposeChordNames,

src/notation/inotationinteraction.h

+1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ class INotationInteraction
219219
virtual void applySystemLock() = 0;
220220

221221
virtual void addRemoveSystemLocks(AddRemoveSystemLockType intervalType, int interval = 0) = 0;
222+
virtual void addRemovePageBreaks(AddRemovePageBreaksType intervalType, int interval = 0) = 0;
222223
virtual bool transpose(const TransposeOptions& options) = 0;
223224
virtual void swapVoices(voice_idx_t voiceIndex1, voice_idx_t voiceIndex2) = 0;
224225
virtual void addIntervalToSelectedNotes(int interval) = 0;

src/notation/internal/notationactioncontroller.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ void NotationActionController::init()
304304
registerAction("staff-properties", &Controller::openStaffProperties);
305305
registerAction("edit-strings", &Controller::openEditStringsDialog);
306306
registerAction("measures-per-system", &Controller::openBreaksDialog);
307+
registerAction("add-remove-page-breaks", &Controller::openPageBreaksDialog);
307308
registerAction("transpose", &Controller::openTransposeDialog);
308309
registerAction("parts", &Controller::openPartsDialog);
309310
registerAction("staff-text-properties", &Controller::openStaffTextPropertiesDialog);
@@ -1810,6 +1811,11 @@ void NotationActionController::openBreaksDialog()
18101811
interactive()->open("musescore://notation/breaks");
18111812
}
18121813

1814+
void NotationActionController::openPageBreaksDialog()
1815+
{
1816+
interactive()->open("musescore://notation/pagebreaks");
1817+
}
1818+
18131819
void NotationActionController::openTransposeDialog()
18141820
{
18151821
interactive()->open("musescore://notation/transpose");

src/notation/internal/notationactioncontroller.h

+1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class NotationActionController : public muse::actions::Actionable, public muse::
152152
void openStaffProperties();
153153
void openEditStringsDialog();
154154
void openBreaksDialog();
155+
void openPageBreaksDialog();
155156
void openTransposeDialog();
156157
void openPartsDialog();
157158
void openTupletOtherDialog();

src/notation/internal/notationinteraction.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -5466,6 +5466,16 @@ void NotationInteraction::addRemoveSystemLocks(AddRemoveSystemLockType intervalT
54665466
apply();
54675467
}
54685468

5469+
void NotationInteraction::addRemovePageBreaks(AddRemovePageBreaksType intervalType, int interval)
5470+
{
5471+
interval = intervalType == AddRemovePageBreaksType::SystemsInterval ? interval : 0;
5472+
bool afterEachPage = intervalType == AddRemovePageBreaksType::AfterEachPage;
5473+
5474+
startEdit(TranslatableString("undoableAction", "Add / remove page breaks"));
5475+
score()->addRemovePageBreaks(interval, afterEachPage);
5476+
apply();
5477+
}
5478+
54695479
bool NotationInteraction::transpose(const TransposeOptions& options)
54705480
{
54715481
startEdit(TranslatableString("undoableAction", "Transposition"));

src/notation/internal/notationinteraction.h

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable
224224
void applySystemLock() override;
225225

226226
void addRemoveSystemLocks(AddRemoveSystemLockType intervalType, int interval = 0) override;
227+
void addRemovePageBreaks(AddRemovePageBreaksType intervalType, int interval = 0) override;
227228
bool transpose(const TransposeOptions& options) override;
228229
void swapVoices(voice_idx_t voiceIndex1, voice_idx_t voiceIndex2) override;
229230
void addIntervalToSelectedNotes(int interval) override;

src/notation/internal/notationuiactions.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,12 @@ const UiActionList NotationUiActions::m_actions = {
563563
TranslatableString("action", "Measure properties…"),
564564
TranslatableString("action", "Measure properties…")
565565
),
566+
UiAction("add-remove-page-breaks",
567+
mu::context::UiCtxProjectOpened,
568+
mu::context::CTX_NOTATION_OPENED,
569+
TranslatableString("action", "Add / remove page breaks…"),
570+
TranslatableString("action", "Add / remove page breaks…")
571+
),
566572
UiAction("measures-per-system",
567573
mu::context::UiCtxProjectOpened,
568574
mu::context::CTX_NOTATION_OPENED,

src/notation/notationmodule.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "view/widgets/editstaff.h"
6363
#include "view/widgets/editstringdata.h"
6464
#include "view/widgets/breaksdialog.h"
65+
#include "view/widgets/pagebreaksdialog.h"
6566
#include "view/widgets/pagesettings.h"
6667
#include "view/widgets/transposedialog.h"
6768
#include "view/widgets/selectnotedialog.h"
@@ -148,6 +149,7 @@ void NotationModule::resolveImports()
148149
ir->registerWidgetUri<PageSettings>(Uri("musescore://notation/pagesettings"));
149150
ir->registerWidgetUri<MeasurePropertiesDialog>(Uri("musescore://notation/measureproperties"));
150151
ir->registerWidgetUri<BreaksDialog>(Uri("musescore://notation/breaks"));
152+
ir->registerWidgetUri<PageBreaksDialog>(Uri("musescore://notation/pagebreaks"));
151153
ir->registerWidgetUri<EditStaff>(Uri("musescore://notation/staffproperties"));
152154
ir->registerWidgetUri<EditStringData>(Uri("musescore://notation/editstrings"));
153155
ir->registerWidgetUri<TransposeDialog>(Uri("musescore://notation/transpose"));

src/notation/notationtypes.h

+7
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ enum class AddRemoveSystemLockType : signed char
197197
MeasuresInterval
198198
};
199199

200+
enum class AddRemovePageBreaksType : signed char
201+
{
202+
AfterEachPage = -1,
203+
None = 0,
204+
SystemsInterval
205+
};
206+
200207
enum class BoxType : unsigned char
201208
{
202209
Unknown,

src/notation/tests/mocks/notationinteractionmock.h

+1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ class NotationInteractionMock : public INotationInteraction
182182
MOCK_METHOD(void, applySystemLock, (), (override));
183183

184184
MOCK_METHOD(void, addRemoveSystemLocks, (AddRemoveSystemLockType, int), (override));
185+
MOCK_METHOD(void, addRemovePageBreaks, (AddRemovePageBreaksType, int), (override));
185186
MOCK_METHOD(bool, transpose, (const TransposeOptions&), (override));
186187
MOCK_METHOD(void, swapVoices, (voice_idx_t, voice_idx_t), (override));
187188
MOCK_METHOD(void, addIntervalToSelectedNotes, (int), (override));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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
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 "pagebreaksdialog.h"
24+
25+
using namespace mu::notation;
26+
27+
static constexpr int DEFAULT_INTERVAL = 4;
28+
29+
//---------------------------------------------------------
30+
// PageBreaksDialog
31+
//---------------------------------------------------------
32+
33+
PageBreaksDialog::PageBreaksDialog(QWidget* parent)
34+
: QDialog(parent), muse::Injectable(muse::iocCtxForQWidget(this))
35+
{
36+
setupUi(this);
37+
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
38+
intervalButton->setChecked(true);
39+
intervalBox->setValue(DEFAULT_INTERVAL);
40+
41+
//: `%1` will be replaced with a number input field.
42+
//: Text before it will appear before that number field, text after will appear after the field.
43+
QString text = muse::qtrc("notation/add-remove-system-breaks", "Break pages every %1 systems");
44+
QStringList pieces = text.split(QStringLiteral("%1"));
45+
46+
IF_ASSERT_FAILED(pieces.size() >= 2) {
47+
return;
48+
}
49+
50+
QString part1 = pieces[0].trimmed();
51+
QString part2 = pieces[1].trimmed();
52+
intervalButton->setText(part1);
53+
intervalLabel2->setText(part2);
54+
}
55+
56+
//---------------------------------------------------------
57+
// accept
58+
//---------------------------------------------------------
59+
60+
void PageBreaksDialog::accept()
61+
{
62+
INotationPtr notation = context()->currentNotation();
63+
if (!notation) {
64+
return;
65+
}
66+
67+
INotationInteractionPtr interaction = notation->interaction();
68+
69+
int interval = intervalButton->isChecked() ? intervalBox->value() : 0;
70+
AddRemovePageBreaksType intervalType = AddRemovePageBreaksType::SystemsInterval;
71+
72+
if (removeButton->isChecked()) {
73+
intervalType = AddRemovePageBreaksType::None;
74+
} else if (pageBreaksButton->isChecked()) {
75+
intervalType = AddRemovePageBreaksType::AfterEachPage;
76+
}
77+
78+
interaction->addRemovePageBreaks(intervalType, interval);
79+
80+
if (_allSelected) {
81+
interaction->clearSelection();
82+
}
83+
84+
QDialog::accept();
85+
}
86+
87+
//---------------------------------------------------------
88+
// showEvent
89+
//---------------------------------------------------------
90+
91+
void PageBreaksDialog::showEvent(QShowEvent* ev)
92+
{
93+
INotationPtr notation = context()->currentNotation();
94+
if (!notation) {
95+
return;
96+
}
97+
98+
INotationInteractionPtr interaction = notation->interaction();
99+
INotationSelectionPtr selection = interaction->selection();
100+
101+
if (!selection->isRange()) {
102+
interaction->selectAll();
103+
_allSelected = true;
104+
} else {
105+
_allSelected = false;
106+
}
107+
108+
QWidget::showEvent(ev);
109+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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
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+
#ifndef MU_NOTATION_PAGEBREAKSDIALOG_H
24+
#define MU_NOTATION_PAGEBREAKSDIALOG_H
25+
26+
#include "ui_pagebreaksdialog.h"
27+
28+
#include "context/iglobalcontext.h"
29+
#include "modularity/ioc.h"
30+
31+
namespace mu::notation {
32+
//---------------------------------------------------------
33+
// PageBreaksDialog
34+
//---------------------------------------------------------
35+
36+
class PageBreaksDialog : public QDialog, public Ui::PageBreaksDialog, public muse::Injectable
37+
{
38+
Q_OBJECT
39+
40+
muse::Inject<context::IGlobalContext> context = { this };
41+
42+
public:
43+
PageBreaksDialog(QWidget* parent = nullptr);
44+
45+
private slots:
46+
void accept() override;
47+
48+
private:
49+
void showEvent(QShowEvent*) override;
50+
51+
bool _allSelected = false;
52+
};
53+
}
54+
55+
#endif // MU_NOTATION_PAGEBREAKSDIALOG_H

0 commit comments

Comments
 (0)