add “a couple” backports from 3.3/3.4/3.5rc
authormirabilos <tg@debian.org>
Thu, 23 Jul 2020 00:18:58 +0000 (02:18 +0200)
committermirabilos <mirabilos@evolvis.org>
Thu, 23 Jul 2020 00:18:58 +0000 (02:18 +0200)
crash/bug fixes and other things we really want in stable

all need quilt-refreshing

some even are good candidates for 2.x…

71 files changed:
debian/copyright
debian/patches/backports/01-playback-muted.diff [new file with mode: 0644]
debian/patches/backports/02-playback-tied.diff [new file with mode: 0644]
debian/patches/backports/03-playback-deleted.diff [new file with mode: 0644]
debian/patches/backports/04-paste-breath.diff [new file with mode: 0644]
debian/patches/backports/05-layout-hairpin-dynamic.diff [new file with mode: 0644]
debian/patches/backports/06-layout-volta.diff [new file with mode: 0644]
debian/patches/backports/07-layout-clef-change.diff [new file with mode: 0644]
debian/patches/backports/08-ui-undo-spacer.diff [new file with mode: 0644]
debian/patches/backports/09-layout-1st-system.diff [new file with mode: 0644]
debian/patches/backports/10-ui-undo-track.diff [new file with mode: 0644]
debian/patches/backports/11-ui-keys-move.diff [new file with mode: 0644]
debian/patches/backports/12-instr-change-name.diff [new file with mode: 0644]
debian/patches/backports/14-crash-segments.diff [new file with mode: 0644]
debian/patches/backports/15-crash-mxml.diff [new file with mode: 0644]
debian/patches/backports/16-crash-workspace.diff [new file with mode: 0644]
debian/patches/backports/16a-crash-qt514.diff [new file with mode: 0644]
debian/patches/backports/17-crash-tremolo.diff [new file with mode: 0644]
debian/patches/backports/18-crash-pianoroll.diff [new file with mode: 0644]
debian/patches/backports/19-synth-state.diff [new file with mode: 0644]
debian/patches/backports/20-tour-loop.diff [new file with mode: 0644]
debian/patches/backports/21-synth-signed-char.diff [new file with mode: 0644]
debian/patches/backports/22-crash-fermata.diff [new file with mode: 0644]
debian/patches/backports/23-hang-startup.diff [new file with mode: 0644]
debian/patches/backports/24-element-size.diff [new file with mode: 0644]
debian/patches/backports/25-relayout-beams.diff [new file with mode: 0644]
debian/patches/backports/26-crash-repeats.diff [new file with mode: 0644]
debian/patches/backports/27-font-style.diff [new file with mode: 0644]
debian/patches/backports/28-crash-del-stafftypechange.diff [new file with mode: 0644]
debian/patches/backports/29-layout-barno-break.diff [new file with mode: 0644]
debian/patches/backports/30-playback-fermata.diff [new file with mode: 0644]
debian/patches/backports/31-ui-enter-slur.diff [new file with mode: 0644]
debian/patches/backports/32-ui-enter-timesig.diff [new file with mode: 0644]
debian/patches/backports/33-playback-articulations.diff [new file with mode: 0644]
debian/patches/backports/33a-playback-articulations.diff [new file with mode: 0644]
debian/patches/backports/34-crash-drag.diff [new file with mode: 0644]
debian/patches/backports/35-ui-mixer-reset.diff [new file with mode: 0644]
debian/patches/backports/36-plugin-state.diff [new file with mode: 0644]
debian/patches/backports/37-crash-parts.diff [new file with mode: 0644]
debian/patches/backports/38-ui-units.diff [new file with mode: 0644]
debian/patches/backports/39-layout-spacer.diff [new file with mode: 0644]
debian/patches/backports/40-crash-del-hbox.diff [new file with mode: 0644]
debian/patches/backports/41-crash-triplet.diff [new file with mode: 0644]
debian/patches/backports/42-crash-no-score.diff [new file with mode: 0644]
debian/patches/backports/43-playback-glitches.diff [new file with mode: 0644]
debian/patches/backports/44-playback-repeats.diff [new file with mode: 0644]
debian/patches/backports/45-pianoroll-xml.diff [new file with mode: 0644]
debian/patches/backports/46-ui-ambitus.diff [new file with mode: 0644]
debian/patches/backports/47-ui-ambitus.diff [new file with mode: 0644]
debian/patches/backports/48-layout-ottava.diff [new file with mode: 0644]
debian/patches/backports/49-pitch-ottava.diff [new file with mode: 0644]
debian/patches/backports/50-layout-divider.diff [new file with mode: 0644]
debian/patches/backports/51-paste-hairpin.diff [new file with mode: 0644]
debian/patches/backports/52-ui-enter-whole-note.diff [new file with mode: 0644]
debian/patches/backports/53-ui-wtf.diff [new file with mode: 0644]
debian/patches/backports/54-pitch-Cb.diff [new file with mode: 0644]
debian/patches/backports/55-layout-timesig.diff [new file with mode: 0644]
debian/patches/backports/56-layout-spatium.diff [new file with mode: 0644]
debian/patches/backports/57-crash-fontname.diff [new file with mode: 0644]
debian/patches/backports/58-blank-lines.diff [new file with mode: 0644]
debian/patches/backports/59-blank-lines.diff [new file with mode: 0644]
debian/patches/backports/60-desktopfile.diff [new file with mode: 0644]
debian/patches/backports/61-desktopfile.diff [new file with mode: 0644]
debian/patches/backports/62-measure-count.diff [new file with mode: 0644]
debian/patches/backports/63-minimise-window.diff [new file with mode: 0644]
debian/patches/backports/boldfonts-1.diff [moved from debian/patches/upstream/boldfonts-1.diff with 100% similarity]
debian/patches/backports/boldfonts-2.diff [moved from debian/patches/upstream/boldfonts-2.diff with 100% similarity]
debian/patches/backports/boldfonts-3.diff [moved from debian/patches/upstream/boldfonts-3.diff with 100% similarity]
debian/patches/backports/qt514-1.diff [moved from debian/patches/upstream/qt514-1.diff with 100% similarity]
debian/patches/backports/workspace-warning.diff [moved from debian/patches/upstream/workspace-warning.diff with 100% similarity]
debian/patches/series

index 0b726c5..0ca3ab2 100644 (file)
@@ -20,6 +20,13 @@ Files: *
 Copyright: 2002-2019, Werner Schweer and others
 License: GPL-2
 
+Files: debian/patches/backports
+       debian/patches/upstream
+Copyright:
+ 2002-2019, Werner Schweer and others
+ 2020, MuseScore BVBA and others
+License: GPL-2
+
 License: GPL-2 with Font exception (MuseScore)
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License version 2 as published
diff --git a/debian/patches/backports/01-playback-muted.diff b/debian/patches/backports/01-playback-muted.diff
new file mode 100644 (file)
index 0000000..73a567a
--- /dev/null
@@ -0,0 +1,120 @@
+Origin: vendor, commit:711b7ab72caa07318a7b086c4372e5a440ae2020
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #282165: Mute voice produces note that doesn't stop when overlap present
+
+--- a/mscore/exportmidi.cpp
++++ b/mscore/exportmidi.cpp
+@@ -293,6 +293,8 @@ bool ExportMidi::write(QIODevice* device, bool midiExpandRepeats, bool exportRPN
+                         for (auto i = events.begin(); i != events.end(); ++i) {
+                               const NPlayEvent& event = i->second;
++                              if (event.isMuted())
++                                    continue;
+                               if (event.discard() == staffIdx + 1 && event.velo() > 0)
+                                     // turn note off so we can restrike it in another track
+                                     track.insert(pauseMap.addPauseTicks(i->first), MidiEvent(ME_NOTEON, channel,
+diff --git a/mscore/seq.cpp b/mscore/seq.cpp
+index 9d4e43b7a..b93718d6c 100644
+--- a/mscore/seq.cpp
++++ b/mscore/seq.cpp
+@@ -515,16 +515,7 @@ void Seq::playEvent(const NPlayEvent& event, unsigned framePos)
+       {
+       int type = event.type();
+       if (type == ME_NOTEON) {
+-            bool mute = false;
+-
+-            const Note* note = event.note();
+-            if (note) {
+-                  Staff* staff      = note->staff();
+-                  Instrument* instr = staff->part()->instrument(note->chord()->tick());
+-                  const Channel* a = instr->playbackChannel(note->subchannel(), cs);
+-                  mute = a->mute() || a->soloMute() || !staff->playbackVoice(note->voice());
+-                  }
+-            if (!mute) {
++            if (!event.isMuted()) {
+                   if (event.discard()) { // ignore noteoff but restrike noteon
+                         if (event.velo() > 0)
+                               putEvent(NPlayEvent(ME_NOTEON, event.channel(), event.pitch(), 0) ,framePos);
+--- a/synthesizer/event.cpp
++++ b/synthesizer/event.cpp
+@@ -14,6 +14,9 @@
+ #include "libmscore/note.h"
+ #include "libmscore/sig.h"
+ #include "event.h"
++#include "libmscore/staff.h"
++#include "libmscore/instrument.h"
++#include "libmscore/part.h"
+ namespace Ms {
+@@ -163,6 +166,23 @@ NPlayEvent::NPlayEvent(BeatType beatType)
+             }
+       }
++//---------------------------------------------------------
++//   isMuted
++//---------------------------------------------------------
++
++bool NPlayEvent::isMuted() const
++      {
++      const Note* n = note();
++      if (n) {
++            MasterScore* cs = n->masterScore();
++            Staff* staff = n->staff();
++            Instrument* instr = staff->part()->instrument(n->tick());
++            const Channel* a = instr->playbackChannel(n->subchannel(), cs);
++            return a->mute() || a->soloMute() || !staff->playbackVoice(n->voice());
++            }
++      return false;
++      }
++
+ //---------------------------------------------------------
+ //   dump
+ //---------------------------------------------------------
+@@ -391,25 +411,26 @@ void EventMap::fixupMIDI()
+       auto it = begin();
+       while (it != end()) {
++            NPlayEvent& event = it->second;
+             /* ME_NOTEOFF is never emitted, no need to check for it */
+-            if (it->second.type() == ME_NOTEON) {
+-                  unsigned short np = info[it->second.channel()].nowPlaying[it->second.pitch()];
+-                  if (it->second.velo() == 0) {
++            if (event.type() == ME_NOTEON && !event.isMuted()) {
++                  unsigned short np = info[event.channel()].nowPlaying[event.pitch()];
++                  if (event.velo() == 0) {
+                         /* already off (should not happen) or still playing? */
+                         if (np == 0 || --np > 0)
+-                              it->second.setDiscard(1);
++                              event.setDiscard(1);
+                         else {
+                               /* hoist NOTEOFF to same track as NOTEON */
+-                              it->second.setOriginatingStaff(info[it->second.channel()].event[it->second.pitch()]->getOriginatingStaff());
++                              event.setOriginatingStaff(info[event.channel()].event[event.pitch()]->getOriginatingStaff());
+                               }
+                         }
+                   else {
+                         if (++np > 1)
+                               /* restrike, possibly on different track */
+-                              it->second.setDiscard(info[it->second.channel()].event[it->second.pitch()]->getOriginatingStaff() + 1);
+-                        info[it->second.channel()].event[it->second.pitch()] = &(it->second);
++                              event.setDiscard(info[event.channel()].event[event.pitch()]->getOriginatingStaff() + 1);
++                        info[event.channel()].event[event.pitch()] = &event;
+                         }
+-                  info[it->second.channel()].nowPlaying[it->second.pitch()] = np;
++                  info[event.channel()].nowPlaying[event.pitch()] = np;
+                   }
+             ++it;
+diff --git a/synthesizer/event.h b/synthesizer/event.h
+index 1c2721100..a16166a80 100644
+--- a/synthesizer/event.h
++++ b/synthesizer/event.h
+@@ -254,6 +254,7 @@ class NPlayEvent : public PlayEvent {
+       void setOriginatingStaff(int i) { _origin = i; }
+       void setDiscard(int d) { _discard = d; }
+       int discard() const { return _discard; }
++      bool isMuted() const;
+       };
+ //---------------------------------------------------------
diff --git a/debian/patches/backports/02-playback-tied.diff b/debian/patches/backports/02-playback-tied.diff
new file mode 100644 (file)
index 0000000..ab4f8eb
--- /dev/null
@@ -0,0 +1,73 @@
+Origin: vendor, commit:7c3fef8a55e434d40f08a04a03b074a3bcbb01d7
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #291146: Swing prevents midi rendering of tied notes
+
+--- a/libmscore/articulation.cpp
++++ b/libmscore/articulation.cpp
+@@ -601,6 +601,30 @@ bool Articulation::isLuteFingering() const
+           || _symId == SymId::luteFingeringRHThird;
+       }
++//---------------------------------------------------------
++//   isOrnament
++//---------------------------------------------------------
++
++bool Articulation::isOrnament() const
++      {
++      return _symId == SymId::ornamentTurn
++          || _symId == SymId::ornamentTurnInverted
++          || _symId == SymId::ornamentTrill
++          || _symId == SymId::brassMuteClosed
++          || _symId == SymId::ornamentMordentInverted
++          || _symId == SymId::ornamentMordent
++          || _symId == SymId::ornamentTremblement
++          || _symId == SymId::ornamentPrallMordent
++          || _symId == SymId::ornamentLinePrall
++          || _symId == SymId::ornamentUpPrall
++          || _symId == SymId::ornamentUpMordent
++          || _symId == SymId::ornamentPrecompMordentUpperPrefix
++          || _symId == SymId::ornamentDownMordent
++          || _symId == SymId::ornamentPrallUp
++          || _symId == SymId::ornamentPrallDown
++          || _symId == SymId::ornamentPrecompSlide;
++      }
++
+ //---------------------------------------------------------
+ //   accessibleInfo
+ //---------------------------------------------------------
+diff --git a/libmscore/articulation.h b/libmscore/articulation.h
+index cf9cc3df2..f151425b8 100644
+--- a/libmscore/articulation.h
++++ b/libmscore/articulation.h
+@@ -139,6 +139,7 @@ class Articulation final : public Element {
+       bool isAccent() const;
+       bool isMarcato() const;
+       bool isLuteFingering() const;
++      bool isOrnament() const;
+       void doAutoplace();
+       };
+diff --git a/libmscore/rendermidi.cpp b/libmscore/rendermidi.cpp
+index 26c0b58b5..69198d2f6 100644
+--- a/libmscore/rendermidi.cpp
++++ b/libmscore/rendermidi.cpp
+@@ -2162,14 +2162,16 @@ void renderChordArticulation(Chord* chord, QList<NoteEventList> & ell, int & gat
+ static bool shouldRenderNote(Note* n)
+       {
+-      int dist = 0;
+       while (n->tieBack()) {
+             n = n->tieBack()->startNote();
+-            ++dist;
+-            if (n && n->playEvents().offtime() > (dist * NoteEvent::NOTE_LENGTH)) {
++            if (findFirstTrill(n->chord()))
+                   // The previous tied note probably has events for this note too.
+                   // That is, we don't need to render this note separately.
+                   return false;
++            for (Articulation* a : n->chord()->articulations()) {
++                  if (a->isOrnament()) {
++                        return false;
++                        }
+                   }
+             }
+       return true;
diff --git a/debian/patches/backports/03-playback-deleted.diff b/debian/patches/backports/03-playback-deleted.diff
new file mode 100644 (file)
index 0000000..66f6e13
--- /dev/null
@@ -0,0 +1,42 @@
+Origin: vendor, commit:cc885c15ae9732fc576f92a382b2271edc649ee0
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #292851: Note still played after having been deleted.
+
+--- a/libmscore/chord.cpp
++++ b/libmscore/chord.cpp
+@@ -516,6 +516,7 @@ void Chord::add(Element* e)
+                   if (voice() && measure() && note->visible())
+                         measure()->setHasVoices(staffIdx(), true);
+                   }
++                  score()->setPlaylistDirty();
+                   break;
+             case ElementType::ARPEGGIO:
+                   _arpeggio = toArpeggio(e);
+@@ -597,6 +598,7 @@ void Chord::remove(Element* e)
+                         qDebug("Chord::remove() note %p not found!", e);
+                   if (voice() && measure() && note->visible())
+                         measure()->checkMultiVoices(staffIdx());
++                  score()->setPlaylistDirty();
+                   }
+                   break;
+diff --git a/libmscore/segment.cpp b/libmscore/segment.cpp
+index 59c8fbf79..e6d91e152 100644
+--- a/libmscore/segment.cpp
++++ b/libmscore/segment.cpp
+@@ -584,6 +584,7 @@ void Segment::add(Element* el)
+ //                  ChordRest* cr = toChordRest(el);
+ //                  if (cr->tuplet() && !cr->tuplet()->elements().empty() && cr->tuplet()->elements().front() == cr && cr->tuplet()->tick() < 0)
+ //                        cr->tuplet()->setTick(cr->tick());
++                  score()->setPlaylistDirty();
+                   }
+                   // fall through
+@@ -639,6 +640,7 @@ void Segment::remove(Element* el)
+                         if (start != s->startElement() || end != s->endElement())
+                               score()->undo(new ChangeStartEndSpanner(s, start, end));
+                         }
++                  score()->setPlaylistDirty();
+                   }
+                   break;
diff --git a/debian/patches/backports/04-paste-breath.diff b/debian/patches/backports/04-paste-breath.diff
new file mode 100644 (file)
index 0000000..bfd71d5
--- /dev/null
@@ -0,0 +1,23 @@
+Origin: vendor, commit:a4a5173e03050bc9d93e30fecab9f170148be135
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #291932: Copy-paste breaths & pauses causes bad layout of the first note/rest in following measures
+ Copying and pasting a breath mark on the last tick of a measure
+ resulted in an empty Breath segment being created in the next measure.
+ This is because the last tick of one measure is equal to the first tick
+ of the next measure, and Score::tick2measure() always returns the
+ second measure in this case. When the Breath mark actually gets added
+ to the score in Score::undoAddElement(), a new segment for it is
+ created in the previous measure. This is the measure in which we should
+ be adding the segment in the first place.
+
+--- a/libmscore/paste.cpp
++++ b/libmscore/paste.cpp
+@@ -395,6 +395,8 @@ bool Score::pasteStaff(XmlReader& e, Segment* dst, int dstStaff, Fraction scale)
+                               breath->setTrack(e.track());
+                               Fraction tick = doScale ? (e.tick() - dstTick) * scale + dstTick : e.tick();
+                               Measure* m = tick2measure(tick);
++                              if (m->tick() == tick)
++                                    m = m->prevMeasure();
+                               Segment* segment = m->undoGetSegment(SegmentType::Breath, tick);
+                               breath->setParent(segment);
+                               undoChangeElement(segment->element(e.track()), breath);
diff --git a/debian/patches/backports/05-layout-hairpin-dynamic.diff b/debian/patches/backports/05-layout-hairpin-dynamic.diff
new file mode 100644 (file)
index 0000000..709c082
--- /dev/null
@@ -0,0 +1,46 @@
+Origin: vendor, commit:922e514e4922338741bb75edafc555523f76d35c
+Author: James Thistlewood <jamesthistlewood@gmail.com>
+Description: fix #292960: hairpin with dynamics at the end is too short
+
+--- a/libmscore/hairpin.cpp
++++ b/libmscore/hairpin.cpp
+@@ -103,11 +103,12 @@ void HairpinSegment::layout()
+       Dynamic* ed = nullptr;
+       qreal dymax = hairpin()->placeBelow() ? -10000.0 : 10000.0;
+       if (autoplace() && !score()->isPalette()) {
++            Segment* start = hairpin()->startSegment();
++            Segment* end = hairpin()->endSegment();
+             // Try to fit between adjacent dynamics
+             qreal minDynamicsDistance = score()->styleP(Sid::autoplaceHairpinDynamicsDistance) * staff()->mag(tick());
+             const System* sys = system();
+             if (isSingleType() || isBeginType()) {
+-                  Segment* start = hairpin()->startSegment();
+                   if (start && start->system() == sys) {
+                         sd = toDynamic(start->findAnnotation(ElementType::DYNAMIC, _trck, _trck));
+                         if (!sd) {
+@@ -130,8 +131,7 @@ void HairpinSegment::layout()
+                         }
+                   }
+             if (isSingleType() || isEndType()) {
+-                  Segment* end = hairpin()->endSegment();
+-                  if (end && end->tick() < sys->endTick()) {
++                  if (end && end->tick() < sys->endTick() && start != end) {
+                         // checking ticks rather than systems
+                         // systems may be unknown at layout stage.
+                         ed = toDynamic(end->findAnnotation(ElementType::DYNAMIC, _trck, _trck));
+@@ -176,14 +176,13 @@ void HairpinSegment::layout()
+             qreal h1 = hairpin()->hairpinHeight().val()     * _spatium * .5;
+             qreal h2 = hairpin()->hairpinContHeight().val() * _spatium * .5;
+-            qreal len;
+             qreal x = pos2().x();
+             if (!_endText->empty())
+                   x -= (_endText->width() + _spatium * .5);       // 0.5 spatium distance
+             if (x < _spatium)             // minimum size of hairpin
+                   x = _spatium;
+             qreal y = pos2().y();
+-            len     = sqrt(x * x + y * y);
++            qreal len = sqrt(x * x + y * y);
+             t.rotateRadians(asin(y/len));
+             drawCircledTip   =  hairpin()->hairpinCircledTip();
diff --git a/debian/patches/backports/06-layout-volta.diff b/debian/patches/backports/06-layout-volta.diff
new file mode 100644 (file)
index 0000000..15d8e84
--- /dev/null
@@ -0,0 +1,35 @@
+Origin: vendor, commit:d2278d6742aebc41e488357f4ce892d06b65780a
+Author: Marc Sabatella <marc@outsideshore.com>
+Description: fix #287871: volta line not drawn before start repeat
+
+--- a/libmscore/line.cpp
++++ b/libmscore/line.cpp
+@@ -668,15 +668,15 @@ QPointF SLine::linePos(Grip grip, System** sys) const
+                         while (seg && seg->segmentType() != SegmentType::EndBarLine)
+                               seg = seg->prev();
+                         if (!seg || !seg->enabled()) {
+-                              // no end bar line; look for BeginBarLine of next measure
++                              // no end bar line; look for BeginBarLine or StartRepeatBarLine of next measure
+                               Measure* nm = m->nextMeasure();
+                               if (nm->system() == m->system())
+-                                    seg = nm->first(SegmentType::BeginBarLine);
++                                    seg = nm->first(SegmentType::BeginBarLine|SegmentType::StartRepeatBarLine);
+                               }
+-                        qreal mwidth = seg ? seg->x() : m->bbox().right();
++                        qreal mwidth = seg && seg->measure() == m ? seg->x() : m->bbox().right();
+                         x = m->pos().x() + mwidth;
+                         // align to barline
+-                        if (seg && seg->isEndBarLineType()) {
++                        if (seg && (seg->segmentType() & SegmentType::BarLineType)) {
+                               Element* e = seg->element(0);
+                               if (e && e->type() == ElementType::BAR_LINE) {
+                                     BarLineType blt = toBarLine(e)->barLineType();
+@@ -985,7 +985,7 @@ void SLine::writeProperties(XmlWriter& xml) const
+       //
+       bool modified = false;
+       for (const SpannerSegment* seg : spannerSegments()) {
+-            if (!seg->autoplace() || !seg->visible() || 
++            if (!seg->autoplace() || !seg->visible() ||
+                (!seg->isStyled(Pid::OFFSET) && (!seg->offset().isNull() || !seg->userOff2().isNull()))) {
+                   modified = true;
+                   break;
diff --git a/debian/patches/backports/07-layout-clef-change.diff b/debian/patches/backports/07-layout-clef-change.diff
new file mode 100644 (file)
index 0000000..28077e4
--- /dev/null
@@ -0,0 +1,35 @@
+Origin: vendor, commit:feffd0f5f7bd2462894031e216c31c60809167fa
+Author: Marc Sabatella <marc@outsideshore.com>
+Description: fix #290061: no space betwene header and clef change
+
+--- a/libmscore/measure.cpp
++++ b/libmscore/measure.cpp
+@@ -4106,7 +4106,8 @@ void Measure::computeMinWidth(Segment* s, qreal x, bool isSystemHeader)
+             qreal w;
+             if (ns) {
+-                  if (isSystemHeader && ns->isChordRestType()) {        // this is the system header gap
++                  if (isSystemHeader && (ns->isChordRestType() || (ns->isClefType() && !ns->header()))) {
++                        // this is the system header gap
+                         w = s->minHorizontalDistance(ns, true);
+                         isSystemHeader = false;
+                         }
+diff --git a/libmscore/segment.cpp b/libmscore/segment.cpp
+index 35982317c..0c99e1398 100644
+--- a/libmscore/segment.cpp
++++ b/libmscore/segment.cpp
+@@ -2079,6 +2079,14 @@ qreal Segment::minHorizontalDistance(Segment* ns, bool systemHeaderGap) const
+             // d = qMax(d, spatium());       // minimum distance is one spatium
+             // w = qMax(w, minRight()) + d;
+             }
++      else if (systemHeaderGap) {
++            // first segment after header is *not* a chordrest
++            // could be a clef
++            if (st == SegmentType::TimeSig)
++                  w += score()->styleP(Sid::systemHeaderTimeSigDistance);
++            else
++                  w += score()->styleP(Sid::systemHeaderDistance);
++            }
+       else if (st & (SegmentType::Clef | SegmentType::HeaderClef)) {
+             if (nst == SegmentType::KeySig || nst == SegmentType::KeySigAnnounce)
+                   w += score()->styleP(Sid::clefKeyDistance);
diff --git a/debian/patches/backports/08-ui-undo-spacer.diff b/debian/patches/backports/08-ui-undo-spacer.diff
new file mode 100644 (file)
index 0000000..1983d24
--- /dev/null
@@ -0,0 +1,35 @@
+Origin: vendor, commit:1ba9e83aa1959e8aa8e895bec616174b8615a832
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #291941: Changing spacer length with a mouse is not undoable
+
+--- a/libmscore/spacer.cpp
++++ b/libmscore/spacer.cpp
+@@ -127,6 +127,16 @@ void Spacer::startEdit(EditData& ed)
+       ed.curGrip = Grip::START;
+       }
++//---------------------------------------------------------
++//   startEditDrag
++//---------------------------------------------------------
++
++void Spacer::startEditDrag(EditData& ed)
++      {
++      ElementEditData* eed = ed.getData(this);
++      eed->pushProperty(Pid::SPACE);
++      }
++
+ //---------------------------------------------------------
+ //   editDrag
+ //---------------------------------------------------------
+diff --git a/libmscore/spacer.h b/libmscore/spacer.h
+index ffa376a59..8c8e16a95 100644
+--- a/libmscore/spacer.h
++++ b/libmscore/spacer.h
+@@ -51,6 +51,7 @@ class Spacer final : public Element {
+       virtual void draw(QPainter*) const;
+       virtual bool isEditable() const { return true; }
+       virtual void startEdit(EditData&) override;
++      virtual void startEditDrag(EditData&) override;
+       virtual void editDrag(EditData&) override;
+       virtual void updateGrips(EditData&) const override;
+       virtual void spatiumChanged(qreal, qreal);
diff --git a/debian/patches/backports/09-layout-1st-system.diff b/debian/patches/backports/09-layout-1st-system.diff
new file mode 100644 (file)
index 0000000..9fcfa8f
--- /dev/null
@@ -0,0 +1,26 @@
+Origin: vendor, commit:ccb8fe0e020b6772b4c5390e830975cd706cd580
+Author: Marc Sabatella <marc@outsideshore.com>
+Description: fix #293531: clef&key appear on second system after layout with gen clef&key disabled
+ In https://github.com/musescore/MuseScore/pull/4866 I added code
+ to better handle the detection of the end of the layout range.
+ Part of this was to re-establish the header in the first measure after the end of the range.
+ But my code mistakenly passed in lc.firstSystem as the firstSystem parameter.
+ Thus it treated the *second* system as if it was the first,
+ since lc.firstSystem was set based on the system we just completed,
+ not the system we are about to consider.
+ Fix is to calculate when this new system is actually the first system of a section or not.
+ I calculate this the same way lc.firstMeasure itself is calculated.
+
+--- a/libmscore/layout.cpp
++++ b/libmscore/layout.cpp
+@@ -3437,8 +3437,9 @@ System* Score::collectSystem(LayoutContext& lc)
+                               if (!s->enabled())
+                                     s->setEnabled(true);
+                               }
++                        bool firstSystem = lc.prevMeasure->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
+                         if (curHeader)
+-                              m->addSystemHeader(lc.firstSystem);
++                              m->addSystemHeader(firstSystem);
+                         else
+                               m->removeSystemHeader();
+                         if (curTrailer)
diff --git a/debian/patches/backports/10-ui-undo-track.diff b/debian/patches/backports/10-ui-undo-track.diff
new file mode 100644 (file)
index 0000000..dd920c5
--- /dev/null
@@ -0,0 +1,15 @@
+Origin: vendor, commit:a418fc96563e2ecc155402f321a61e6a47f1fb5f
+Author: James Thistlewood <jamesthistlewood@gmail.com>
+Description: fix #292024: chord symbols attached to fret diagrams in other staves
+
+--- a/libmscore/edit.cpp
++++ b/libmscore/edit.cpp
+@@ -4420,7 +4420,7 @@ void Score::undoAddElement(Element* element)
+                         // make harmony child of fret diagram if possible
+                         if (ne->isHarmony()) {
+                               for (Element* segel : segment->annotations()) {
+-                                    if (segel->isFretDiagram()) {
++                                    if (segel && segel->isFretDiagram() && segel->track() == ntrack) {
+                                           ne->setTrack(segel->track());
+                                           ne->setParent(segel);
+                                           break;
diff --git a/debian/patches/backports/11-ui-keys-move.diff b/debian/patches/backports/11-ui-keys-move.diff
new file mode 100644 (file)
index 0000000..e445f4b
--- /dev/null
@@ -0,0 +1,19 @@
+Origin: vendor, commit:3fa9fe4c950f59478753724f3df3b1821b97e475
+Author: Marc Sabatella <marc@outsideshore.com>
+Description: fix #290047: require Shift to adjust segment with cursor keys in edit mode
+
+--- a/libmscore/note.cpp
++++ b/libmscore/note.cpp
+@@ -2367,7 +2367,11 @@ void Note::editDrag(EditData& ed)
+       {
+       Chord* ch = chord();
+       Segment* seg = ch->segment();
+-      if (seg) {
++      // adjust segment on plain drag or Shift+cursor,
++      // adjust note/chord for Ctrl+drag or plain cursor
++      if (seg &&
++          (((ed.buttons & Qt::LeftButton) && !(ed.modifiers & Qt::ControlModifier))
++           || (ed.modifiers & Qt::ShiftModifier))) {
+             const Spatium deltaSp = Spatium(ed.delta.x() / spatium());
+             seg->undoChangeProperty(Pid::LEADING_SPACE, seg->extraLeadingSpace() + deltaSp);
+             }
diff --git a/debian/patches/backports/12-instr-change-name.diff b/debian/patches/backports/12-instr-change-name.diff
new file mode 100644 (file)
index 0000000..2dddc14
--- /dev/null
@@ -0,0 +1,83 @@
+Origin: vendor, commit:b5be6f983105ab173d693c61e73da09d80de1e32
+Author: MarcSabatella <marc@outsideshore.com>
+Description: partial fix #88861: show current staff name after instrument change
+
+--- a/libmscore/layout.cpp
++++ b/libmscore/layout.cpp
+@@ -2970,7 +2970,8 @@ System* Score::collectSystem(LayoutContext& lc)
+             lc.startWithLongNames = lc.firstSystem && measure->sectionBreakElement()->startWithLongNames();
+             }
+       System* system = getNextSystem(lc);
+-      system->setInstrumentNames(lc.startWithLongNames);
++      int lcmTick = lc.curMeasure ? lc.curMeasure->tick() : 0;
++      system->setInstrumentNames(lc.startWithLongNames, lcmTick);
+       qreal minWidth    = 0;
+       bool firstMeasure = true;
+diff --git a/libmscore/system.cpp b/libmscore/system.cpp
+index 6e65c9214..09bc94f43 100644
+--- a/libmscore/system.cpp
++++ b/libmscore/system.cpp
+@@ -595,7 +595,7 @@ void System::layout2()
+ //   setInstrumentNames
+ //---------------------------------------------------------
+-void System::setInstrumentNames(bool longName)
++void System::setInstrumentNames(bool longName, int tick)
+       {
+       //
+       // remark: add/remove instrument names is not undo/redoable
+@@ -612,10 +612,6 @@ void System::setInstrumentNames(bool longName)
+             return;
+             }
+-      // TODO: ml is normally empty here, so we are unable to retrieve tick
+-      // thus, staff name does not reflect current instrument
+-
+-      int tick = ml.empty() ? 0 : ml.front()->tick();
+       int staffIdx = 0;
+       for (SysStaff* staff : _staves) {
+             Staff* s = score()->staff(staffIdx);
+diff --git a/libmscore/system.h b/libmscore/system.h
+index 13d13d8b9..fd417045f 100644
+--- a/libmscore/system.h
++++ b/libmscore/system.h
+@@ -128,7 +128,7 @@ class System final : public Element {
+       void removeStaff(int);
+       int y2staff(qreal y) const;
+-      void setInstrumentNames(bool longName);
++      void setInstrumentNames(bool longName, int tick = 0);
+       int snap(int tick, const QPointF p) const;
+       int snapNote(int tick, const QPointF p, int staff) const;
+diff --git a/mscore/continuouspanel.cpp b/mscore/continuouspanel.cpp
+index b9e649c1b..86b8d1609 100644
+--- a/mscore/continuouspanel.cpp
++++ b/mscore/continuouspanel.cpp
+@@ -159,10 +159,10 @@ void ContinuousPanel::paint(const QRect&, QPainter& painter)
+                   Segment* parent = _score->tick2segment(tick);
+                   // Find maximum width for the staff name
+-                  QList<StaffName>& staffNamesLong = currentStaff->part()->instrument()->longNames();
++                  QList<StaffName>& staffNamesLong = currentStaff->part()->instrument(tick)->longNames();
+                   QString staffName = staffNamesLong.isEmpty() ? " " : staffNamesLong[0].name();
+                   if (staffName == "") {
+-                        QList<StaffName>& staffNamesShort = currentStaff->part()->instrument()->shortNames();
++                        QList<StaffName>& staffNamesShort = currentStaff->part()->instrument(tick)->shortNames();
+                         staffName = staffNamesShort.isEmpty() ? "" : staffNamesShort[0].name();
+                         }
+                   Text* newName = new Text(_score);
+@@ -345,10 +345,10 @@ void ContinuousPanel::paint(const QRect&, QPainter& painter)
+                   barLine.draw(&painter);
+                   // Draw the current staff name
+-                  QList<StaffName>& staffNamesLong = currentStaff->part()->instrument()->longNames();
++                  QList<StaffName>& staffNamesLong = currentStaff->part()->instrument(tick)->longNames();
+                   QString staffName = staffNamesLong.isEmpty() ? " " : staffNamesLong[0].name();
+                   if (staffName == "") {
+-                        QList<StaffName>& staffNamesShort = currentStaff->part()->instrument()->shortNames();
++                        QList<StaffName>& staffNamesShort = currentStaff->part()->instrument(tick)->shortNames();
+                         staffName = staffNamesShort.isEmpty() ? "" : staffNamesShort[0].name();
+                         }
diff --git a/debian/patches/backports/14-crash-segments.diff b/debian/patches/backports/14-crash-segments.diff
new file mode 100644 (file)
index 0000000..444f203
--- /dev/null
@@ -0,0 +1,30 @@
+Origin: vendor, commit:32cbcfe03499e05b09099e28c6b994fc53912c1c
+Author: Joachim Schmitz <jojo@schmitz-digital.de>
+Description: Fix #293460: Crash when loading file with orphan Segment tags
+ correct broken files on the fly and don't create them anymore.
+
+--- a/libmscore/measure.cpp
++++ b/libmscore/measure.cpp
+@@ -2368,7 +2368,7 @@ void Measure::readVoice(XmlReader& e, int staffIdx, bool irregular)
+                         }
+                   startingBeam = beam;
+                   }
+-            else if (tag == "Segment")
++            else if (tag == "Segment" && segment)
+                   segment->read(e);
+             else if (tag == "Ambitus") {
+                   Ambitus* range = new Ambitus(score());
+diff --git a/libmscore/scorefile.cpp b/libmscore/scorefile.cpp
+index 55927c885..65fa46baf 100644
+--- a/libmscore/scorefile.cpp
++++ b/libmscore/scorefile.cpp
+@@ -1287,7 +1287,8 @@ void Score::writeSegments(XmlWriter& xml, int strack, int etrack,
+                         cr->writeTupletEnd(xml);
+                         }
+-                  segment->write(xml);    // write only once
++                  if (!(e->isRest() && toRest(e)->isGap()))
++                        segment->write(xml);    // write only once
+                   if (forceTimeSig) {
+                         if (segment->segmentType() == SegmentType::KeySig)
+                               keySigWritten = true;
diff --git a/debian/patches/backports/15-crash-mxml.diff b/debian/patches/backports/15-crash-mxml.diff
new file mode 100644 (file)
index 0000000..1eedf62
--- /dev/null
@@ -0,0 +1,23 @@
+Origin: vendor, commit:4857c4117ae2bf0829ede78c3572801f5c4fee73
+Author: Leon Vinken <leon.vinken@gmail.com>
+Description: fix #287475 - crash on open MusicXML file lacking time signature
+
+--- a/mscore/importmxmlpass1.cpp
++++ b/mscore/importmxmlpass1.cpp
+@@ -2054,11 +2054,15 @@ void MusicXMLParserPass1::measure(const QString& partId,
+       // measure duration fixups
+       mDura.reduce();
+-      // fix for PDFtoMusic Pro v1.3.0d Build BF4E (which sometimes generates empty measures)
++      // fix for PDFtoMusic Pro v1.3.0d Build BF4E and PlayScore / ReadScoreLib Version 3.11
++      // which sometimes generate empty measures
+       // if no valid length found and length according to time signature is known,
+       // use length according to time signature
+       if (mDura.isZero() && _timeSigDura.isValid() && _timeSigDura > Fraction(0, 1))
+             mDura = _timeSigDura;
++      // if no valid length found and time signature is unknown, use default
++      if (mDura.isZero() && !_timeSigDura.isValid())
++            mDura = Fraction(4, 4);
+       // if necessary, round up to an integral number of 1/64s,
+       // to comply with MuseScores actual measure length constraints
diff --git a/debian/patches/backports/16-crash-workspace.diff b/debian/patches/backports/16-crash-workspace.diff
new file mode 100644 (file)
index 0000000..bc8975e
--- /dev/null
@@ -0,0 +1,33 @@
+Origin: vendor, commit:91b4788037003b5fe2c2b99371af5f3049041d0e
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: fix #291986: fix a crash on removing a custom workspace via menu
+ A more accurate version of 6a5a7cbcab30c94d2fcb0801670ba4ea694eb9c7
+
+--- a/mscore/workspace.cpp
++++ b/mscore/workspace.cpp
+@@ -706,8 +706,10 @@ void Workspace::read(XmlReader& e)
+                   QMenuBar* mb = mscore->menuBar();
+                   const QObjectList menus(mb->children()); // need a copy
+                   for (QObject* m : menus) {
+-                        if (qobject_cast<QMenu*>(m))
+-                              delete m;
++                        if (qobject_cast<QMenu*>(m)) {
++                              m->setParent(nullptr);
++                              m->deleteLater();
++                              }
+                         }
+                   mb->clear();
+                   menuToStringList.clear();
+@@ -809,8 +811,10 @@ void Workspace::readGlobalMenuBar()
+                               QMenuBar* mb = mscore->menuBar();
+                               const QObjectList menus(mb->children()); // need a copy
+                               for (QObject* m : menus) {
+-                                    if (qobject_cast<QMenu*>(m))
+-                                          delete m;
++                                    if (qobject_cast<QMenu*>(m)) {
++                                          m->setParent(nullptr);
++                                          m->deleteLater();
++                                          }
+                                     }
+                               mb->clear();
+                               menuToStringList.clear();
diff --git a/debian/patches/backports/16a-crash-qt514.diff b/debian/patches/backports/16a-crash-qt514.diff
new file mode 100644 (file)
index 0000000..5a61023
--- /dev/null
@@ -0,0 +1,35 @@
+Origin: vendor, commit:28a48037911f25981e252b03dce65ba2ed0a49e6
+Author: Joachim Schmitz <jojo@schmitz-digital.de>
+Description: fix #299654: Crash on startup with Qt 5.14
+ due to a failed assertion deep in Qt code
+
+--- a/mscore/workspace.cpp
++++ b/mscore/workspace.cpp
+@@ -823,9 +823,10 @@ void Workspace::read(XmlReader& e)
+                   QMenuBar* mb = mscore->menuBar();
+                   const QObjectList menus(mb->children()); // need a copy
+                   for (QObject* m : menus) {
+-                        if (qobject_cast<QMenu*>(m)) {
+-                              m->setParent(nullptr);
+-                              m->deleteLater();
++                        QMenu* menu = qobject_cast<QMenu*>(m);
++                        if (menu) {
++                              menu->setParent(nullptr);
++                              menu->deleteLater();
+                               }
+                         }
+                   mb->clear();
+@@ -931,9 +932,10 @@ void Workspace::readGlobalMenuBar()
+                               QMenuBar* mb = mscore->menuBar();
+                               const QObjectList menus(mb->children()); // need a copy
+                               for (QObject* m : menus) {
+-                                    if (qobject_cast<QMenu*>(m)) {
+-                                          m->setParent(nullptr);
+-                                          m->deleteLater();
++                                    QMenu* menu = qobject_cast<QMenu*>(m);
++                                    if (menu) {
++                                          menu->setParent(nullptr);
++                                          menu->deleteLater();
+                                           }
+                                     }
+                               mb->clear();
diff --git a/debian/patches/backports/17-crash-tremolo.diff b/debian/patches/backports/17-crash-tremolo.diff
new file mode 100644 (file)
index 0000000..712acb7
--- /dev/null
@@ -0,0 +1,170 @@
+Origin: vendor, commit:754ddfd30733a426e0132f833e8a508fa26ab08d
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: fix #291698: ensure consistent state of two-notes tremolo after file reading
+
+--- a/libmscore/layout.cpp
++++ b/libmscore/layout.cpp
+@@ -2549,52 +2549,6 @@ void layoutDrumsetChord(Chord* c, const Drumset* drumset, const StaffType* st, q
+             }
+       }
+-//---------------------------------------------------------
+-//   connectTremolo
+-//    Connect two-notes tremolo and update duration types
+-//    for the involved chords.
+-//---------------------------------------------------------
+-
+-static void connectTremolo(Measure* m)
+-      {
+-      const int ntracks = m->score()->ntracks();
+-      constexpr SegmentType st = SegmentType::ChordRest;
+-      for (Segment* s = m->first(st); s; s = s->next(st)) {
+-            for (int i = 0; i < ntracks; ++i) {
+-                  Element* e = s->element(i);
+-                  if (!e || !e->isChord())
+-                        continue;
+-
+-                  Chord* c = toChord(e);
+-                  Tremolo* tremolo = c->tremolo();
+-                  if (tremolo && tremolo->twoNotes()) {
+-                        // Ensure correct duration type for chord
+-                        c->setDurationType(tremolo->durationType());
+-
+-                        // If it is the first tremolo's chord, find the second
+-                        // chord for tremolo, if needed.
+-                        if (!tremolo->chord1())
+-                              tremolo->setChords(c, tremolo->chord2());
+-                        else if (tremolo->chord1() != c || tremolo->chord2())
+-                              continue;
+-
+-                        for (Segment* ls = s->next(st); ls; ls = ls->next(st)) {
+-                              if (Element* element = ls->element(i)) {
+-                                    if (!element->isChord()) {
+-                                          qDebug("cannot connect tremolo");
+-                                          continue;
+-                                          }
+-                                    Chord* nc = toChord(element);
+-                                    tremolo->setChords(c, nc);
+-                                    nc->setTremolo(tremolo);
+-                                    break;
+-                                    }
+-                              }
+-                        }
+-                  }
+-            }
+-      }
+-
+ //---------------------------------------------------------
+ //   getNextMeasure
+ //---------------------------------------------------------
+@@ -2670,7 +2624,7 @@ void Score::getNextMeasure(LayoutContext& lc)
+             return;
+             }
+-      connectTremolo(measure);
++      measure->connectTremolo();
+       //
+       // calculate accidentals and note lines,
+diff --git a/libmscore/measure.cpp b/libmscore/measure.cpp
+index 8b7713a45..fa95b6ee2 100644
+--- a/libmscore/measure.cpp
++++ b/libmscore/measure.cpp
+@@ -2054,6 +2054,8 @@ void Measure::read(XmlReader& e, int staffIdx)
+             e.setTick(lm->tick() + lm->ticks());
+             }
+       e.setCurrentMeasure(nullptr);
++
++      connectTremolo();
+       }
+ //---------------------------------------------------------
+@@ -2523,6 +2525,52 @@ void Measure::scanElements(void* data, void (*func)(void*, Element*), bool all)
+             }
+       }
++//---------------------------------------------------------
++//   connectTremolo
++///   Connect two-notes tremolo and update duration types
++///   for the involved chords.
++//---------------------------------------------------------
++
++void Measure::connectTremolo()
++      {
++      const int ntracks = score()->ntracks();
++      constexpr SegmentType st = SegmentType::ChordRest;
++      for (Segment* s = first(st); s; s = s->next(st)) {
++            for (int i = 0; i < ntracks; ++i) {
++                  Element* e = s->element(i);
++                  if (!e || !e->isChord())
++                        continue;
++
++                  Chord* c = toChord(e);
++                  Tremolo* tremolo = c->tremolo();
++                  if (tremolo && tremolo->twoNotes()) {
++                        // Ensure correct duration type for chord
++                        c->setDurationType(tremolo->durationType());
++
++                        // If it is the first tremolo's chord, find the second
++                        // chord for tremolo, if needed.
++                        if (!tremolo->chord1())
++                              tremolo->setChords(c, tremolo->chord2());
++                        else if (tremolo->chord1() != c || tremolo->chord2())
++                              continue;
++
++                        for (Segment* ls = s->next(st); ls; ls = ls->next(st)) {
++                              if (Element* element = ls->element(i)) {
++                                    if (!element->isChord()) {
++                                          qDebug("cannot connect tremolo");
++                                          continue;
++                                          }
++                                    Chord* nc = toChord(element);
++                                    tremolo->setChords(c, nc);
++                                    nc->setTremolo(tremolo);
++                                    break;
++                                    }
++                              }
++                        }
++                  }
++            }
++      }
++
+ //---------------------------------------------------------
+ //   createVoice
+ //    Create a voice on demand by filling the measure
+diff --git a/libmscore/measure.h b/libmscore/measure.h
+index 75ab4be6e..14654a83a 100644
+--- a/libmscore/measure.h
++++ b/libmscore/measure.h
+@@ -194,6 +194,7 @@ class Measure final : public MeasureBase {
+       Segment* undoGetSegment(SegmentType st, const Fraction& f)       { return undoGetSegmentR(st, f - tick()); }
+       Segment* getSegment(SegmentType st,     const Fraction& f)       { return getSegmentR(st, f - tick()); }
++      void connectTremolo();
+       qreal createEndBarLines(bool);
+       void barLinesSetSpan(Segment*);
+diff --git a/libmscore/read114.cpp b/libmscore/read114.cpp
+index e1ac0ff33..871f3c2c7 100644
+--- a/libmscore/read114.cpp
++++ b/libmscore/read114.cpp
+@@ -2171,6 +2171,7 @@ static void readMeasure(Measure* m, int staffIdx, XmlReader& e)
+                   }
+             }
+       e.checkTuplets();
++      m->connectTremolo();
+       }
+ //---------------------------------------------------------
+diff --git a/libmscore/read206.cpp b/libmscore/read206.cpp
+index 977d95a02..7cd15a50d 100644
+--- a/libmscore/read206.cpp
++++ b/libmscore/read206.cpp
+@@ -3360,6 +3360,7 @@ static void readMeasure(Measure* m, int staffIdx, XmlReader& e)
+                   e.unknown();
+             }
+       e.checkTuplets();
++      m->connectTremolo();
+       }
+ //---------------------------------------------------------
diff --git a/debian/patches/backports/18-crash-pianoroll.diff b/debian/patches/backports/18-crash-pianoroll.diff
new file mode 100644 (file)
index 0000000..7e920c7
--- /dev/null
@@ -0,0 +1,16 @@
+Origin: vendor, commit:105f81e6b61b020a1e6eb49f2f019db60d9c5363
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #292310: Crash when adjusting Absolute Velocity in piano roll editor
+
+--- a/mscore/pianolevels.cpp
++++ b/mscore/pianolevels.cpp
+@@ -416,7 +416,8 @@ void PianoLevels::adjustLevelLerp(int tick0, int value0, int tick1, int value1,
+             else {
+                   int tick = noteStartTick(note, 0);
+                   if (tick0 <= tick && tick <= tick1) {
+-                        int value = (value1 - value0) * (tick - tick0) / (tick1 - tick0) + value0;
++                        int value = tick0 == tick1 ? value0
++                              : (value1 - value0) * (tick - tick0) / (tick1 - tick0) + value0;
+                         filter->setValue(_staff, note, 0, value);
+                         hitNote = true;
+                         }
diff --git a/debian/patches/backports/19-synth-state.diff b/debian/patches/backports/19-synth-state.diff
new file mode 100644 (file)
index 0000000..a6f5cb3
--- /dev/null
@@ -0,0 +1,28 @@
+Origin: vendor, commit:c9f84a4f5c1b959326276e88d480a550d2f10b67
+Author: James Thistlewood <jamesthistlewood@gmail.com>
+Description: fix #290323: synthesizer state duplicated when changing score with saved state
+
+--- a/libmscore/synthesizerstate.cpp
++++ b/libmscore/synthesizerstate.cpp
+@@ -42,6 +42,7 @@ void SynthesizerState::write(XmlWriter& xml, bool force /* = false */) const
+ void SynthesizerState::read(XmlReader& e)
+       {
++      std::list<SynthesizerGroup> tempGroups;
+       while (e.readNextStartElement()) {
+             SynthesizerGroup group;
+             group.setName(e.name().toString());
+@@ -54,7 +55,12 @@ void SynthesizerState::read(XmlReader& e)
+                   else
+                         e.unknown();
+                   }
+-            push_back(group);
++            tempGroups.push_back(group);
++            }
++
++      if (!tempGroups.empty()) {
++            // Replace any previously set state if we have read a new state
++            swap(tempGroups);
+             setIsDefault(false);
+             }
+       }
diff --git a/debian/patches/backports/20-tour-loop.diff b/debian/patches/backports/20-tour-loop.diff
new file mode 100644 (file)
index 0000000..3df9c50
--- /dev/null
@@ -0,0 +1,24 @@
+Origin: vendor, commit:936a74322288f31c7c7281ae8dbf5df5a2d880e4
+Author: Eric Fontaine <eric@ericfontainejazz.com>
+Description: fix #291646 initiate inspector tour on mouse press
+ Fixes bug that caused inability to finish the inspector tour if user
+ undocked and dragged the inspector widget before tour was initiated. If
+ the inspector was undocked, then it would interfere with clicking
+ "Next" or "Close" in the tour's popup mbox.
+ .
+ This solution is to start the tour on MouseButtonPress so the tour
+ happens before the main mscore window loses mouse focus.
+
+--- a/share/tours/inspector.tour
++++ b/share/tours/inspector.tour
+@@ -1,8 +1,8 @@
+ <?xml version="1.0" encoding="UTF-8"?>
+ <Tour name="inspector-tour">
+-  <Event objectName="inspector">MouseButtonRelease</Event>
++  <Event objectName="inspector">MouseButtonPress</Event>
+   <Event objectName="inspector">FocusIn</Event>
+-  <Event objectName="InspectorBase">MouseButtonRelease</Event>
++  <Event objectName="InspectorBase">MouseButtonPress</Event>
+   <Event objectName="InspectorBase">FocusIn</Event>
+   <Message>
+     <Text>Welcome to the Inspector, where you can change individual properties for selected elements.
diff --git a/debian/patches/backports/21-synth-signed-char.diff b/debian/patches/backports/21-synth-signed-char.diff
new file mode 100644 (file)
index 0000000..8553317
--- /dev/null
@@ -0,0 +1,40 @@
+Origin: vendor, commit:4253d779cf46b9a72e5b170b6f14dcc332c7d0d7
+Author: Eric Fontaine <eric@ericfontainejazz.com>
+Description: fix #291756 explicitly-signed char fluid pitchadj
+ C standards say that "char" may either be a "signed char" or "unsigned
+ char" but that it is up to the compilers implementation or the platform
+ which is followed. Some non x86 platforms, including PowerPC and ARM,
+ treat unspecified chars as unsigned chars, so it is necessary to
+ explicitly declare them as "signed char" (or to compile with
+ "--signed_chars").
+ .
+ This fix ensures that fluid synth's sample's pitchadj value are
+ correctly read as signed.
+
+--- a/fluid/sfont.cpp
++++ b/fluid/sfont.cpp
+@@ -831,9 +831,9 @@ unsigned char SFont::READB()
+ //   READC
+ //---------------------------------------------------------
+-char SFont::READC()
++signed char SFont::READC()
+       {
+-      char var;
++      signed char var;
+       safe_fread(&var, 1);
+       return var;
+       }
+diff --git a/fluid/sfont.h b/fluid/sfont.h
+index bdae46d85..20765cc18 100644
+--- a/fluid/sfont.h
++++ b/fluid/sfont.h
+@@ -93,7 +93,7 @@ class SFont {
+       void FSKIP(int size)    {  return safe_fseek(size); }
+       void FSKIPW();
+       unsigned char READB();
+-      char READC();
++      signed char READC();
+       void READSTR(char*);
+       void safe_fread(void *buf, int count);
diff --git a/debian/patches/backports/22-crash-fermata.diff b/debian/patches/backports/22-crash-fermata.diff
new file mode 100644 (file)
index 0000000..cdb8c6f
--- /dev/null
@@ -0,0 +1,16 @@
+Origin: vendor, commit:5925ea19adf87b32ea6c4967df6de00ec2bfd23f
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #294156: Incorrect segment type for fermata
+
+--- a/libmscore/measure.cpp
++++ b/libmscore/measure.cpp
+@@ -2396,7 +2396,8 @@ void Measure::readVoice(XmlReader& e, int staffIdx, bool irregular)
+                   }
+             }
+       if (fermata) {
+-            segment = getSegment(SegmentType::EndBarLine, e.tick());
++            SegmentType st = (e.tick() == endTick() ? SegmentType::EndBarLine : SegmentType::ChordRest);
++            segment = getSegment(st, e.tick());
+             segment->add(fermata);
+             fermata = nullptr;
+             }
diff --git a/debian/patches/backports/23-hang-startup.diff b/debian/patches/backports/23-hang-startup.diff
new file mode 100644 (file)
index 0000000..ca064b7
--- /dev/null
@@ -0,0 +1,15 @@
+Origin: vendor, commit:e482050197204b8d1458a37608d37749d2fc30f7
+Author: Peter Hieu Vu <peter.vu8@gmail.com>
+Description: fix #294010: properly calculate dash lengths to stop hanging
+
+--- a/libmscore/textlinebase.cpp
++++ b/libmscore/textlinebase.cpp
+@@ -156,7 +156,7 @@ void TextLineBaseSegment::draw(QPainter* painter) const
+                         painter->drawLines(&points[end-1], 1);
+                         end--;
+                         }
+-                  numPairs = adjustedLineLength / (dash + gap);
++                  numPairs = max(qreal(1), adjustedLineLength / (dash + gap));
+                   nDashes[1] = (adjustedLineLength - dash * (numPairs + 1)) / numPairs;
+                   pen.setDashPattern(nDashes);
+                   }
diff --git a/debian/patches/backports/24-element-size.diff b/debian/patches/backports/24-element-size.diff
new file mode 100644 (file)
index 0000000..74a0dfa
--- /dev/null
@@ -0,0 +1,15 @@
+Origin: vendor, commit:da507a5dc9839a39874801d348244f346dd5517a
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #283319: Time Signature disappears
+
+--- a/libmscore/style.cpp
++++ b/libmscore/style.cpp
+@@ -2415,7 +2415,7 @@ bool MStyle::readProperties(XmlReader& e)
+                         e.readElementText();
+                         }
+                   else if (!strcmp("QSizeF", type)) {
+-                        qreal x = e.intAttribute("w", 0);
++                        qreal x = e.doubleAttribute("w", 0.0);
+                         qreal y = e.doubleAttribute("h", 0.0);
+                         set(idx, QSizeF(x, y));
+                         e.readElementText();
diff --git a/debian/patches/backports/25-relayout-beams.diff b/debian/patches/backports/25-relayout-beams.diff
new file mode 100644 (file)
index 0000000..dd2ee9c
--- /dev/null
@@ -0,0 +1,20 @@
+Origin: vendor, commit:282386da9115b09cfc9682b2ec7105f3cd355084
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #294866: The direction of beamed notes does not immediately change when the beam is broken
+ Resolves: https://musescore.org/en/node/294866.
+ .
+ When a ChordRest is removed from its beam during layout, we need to
+ relayout its old beam. This will cause a recalculation of the stem
+ direction, in case it needs to change.
+
+--- a/libmscore/chordrest.cpp
++++ b/libmscore/chordrest.cpp
+@@ -790,6 +790,8 @@ void ChordRest::removeDeleteBeam(bool beamed)
+             _beam->remove(this);
+             if (b->empty())
+                   score()->undoRemoveElement(b);
++            else
++                  b->layout1();
+             }
+       if (!beamed && isChord())
+             toChord(this)->layoutStem();
diff --git a/debian/patches/backports/26-crash-repeats.diff b/debian/patches/backports/26-crash-repeats.diff
new file mode 100644 (file)
index 0000000..26ec7c8
--- /dev/null
@@ -0,0 +1,15 @@
+Origin: vendor, commit:a5a979a758bae239cfae82421e71cf8087ac708b
+Author: Joachim Schmitz <jojo@schmitz-digital.de>
+Description: Fix #295224: end-start-repeat crashes in timeline
+
+--- a/mscore/timeline.cpp
++++ b/mscore/timeline.cpp
+@@ -1232,7 +1232,7 @@ void Timeline::barline_meta(Segment* seg, int* stagger, int pos)
+                         repeat_text = QString("End repeat");
+                         break;
+                   case BarLineType::END_START_REPEAT:
+-                        repeat_text = QString("End-start repeat");
++                        // actually an end repeat followed by a start repeat, so nothing needs to be done here
+                         break;
+                   case BarLineType::DOUBLE:
+                         repeat_text = QString("Double barline");
diff --git a/debian/patches/backports/27-font-style.diff b/debian/patches/backports/27-font-style.diff
new file mode 100644 (file)
index 0000000..81c8b5f
--- /dev/null
@@ -0,0 +1,19 @@
+Origin: vendor, commit:146f6cf6398192aaab2888e2ee283d12922ae188
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #289908: cannot apply italic or underline to glissando text
+ The font style property is being converted from `FontStyle` to `bool`
+ instead of `int`, so bold, italic and underline are all the same.
+ .
+ This commit fixes that.
+
+--- a/libmscore/glissando.cpp
++++ b/libmscore/glissando.cpp
+@@ -649,7 +649,7 @@ bool Glissando::setProperty(Pid propertyId, const QVariant& v)
+                   setFontSize(v.toReal());
+                   break;
+             case Pid::FONT_STYLE:
+-                  setFontStyle(FontStyle(v.toBool()));
++                  setFontStyle(FontStyle(v.toInt()));
+                   break;
+             default:
+                   if (!SLine::setProperty(propertyId, v))
diff --git a/debian/patches/backports/28-crash-del-stafftypechange.diff b/debian/patches/backports/28-crash-del-stafftypechange.diff
new file mode 100644 (file)
index 0000000..6c39833
--- /dev/null
@@ -0,0 +1,30 @@
+Origin: vendor, commit:c4f477603ae468d911cc56eed5ab7ea1da08c4b0
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #295898: Deleting a StaffTypeChange causes a crash in macOS
+
+--- a/libmscore/measure.cpp
++++ b/libmscore/measure.cpp
+@@ -1002,7 +1002,8 @@ void Measure::remove(Element* e)
+                         // st currently points to an list element that is about to be removed
+                         // make a copy now to use on undo/redo
+                         StaffType* st = new StaffType(*stc->staffType());
+-                        staff->removeStaffType(tick());
++                        if (!tick().isZero())
++                              staff->removeStaffType(tick());
+                         stc->setStaffType(st);
+                         }
+                   MeasureBase::remove(e);
+diff --git a/libmscore/staff.cpp b/libmscore/staff.cpp
+index c4d26a084..d5fe3ad90 100644
+--- a/libmscore/staff.cpp
++++ b/libmscore/staff.cpp
+@@ -1403,8 +1403,7 @@ QVariant Staff::propertyDefault(Pid id) const
+ void Staff::localSpatiumChanged(double oldVal, double newVal, Fraction tick)
+       {
+       Fraction etick;
+-      auto i = _staffTypeList.find(tick.ticks());
+-      ++i;
++      auto i = _staffTypeList.upper_bound(tick.ticks());
+       if (i == _staffTypeList.end())
+             etick = score()->lastSegment()->tick();
+       else
diff --git a/debian/patches/backports/29-layout-barno-break.diff b/debian/patches/backports/29-layout-barno-break.diff
new file mode 100644 (file)
index 0000000..9201221
--- /dev/null
@@ -0,0 +1,28 @@
+Origin: vendor, commit:9f1f5409172e8aa1538b19c755854f5a6bbe9625
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #292343: measure numbers restart from 1 after section break even if the corresponding property got disabled
+ Some calls of `sectionBreak()` should be changed to
+ `sectionBreakElement()` so the property can be accessed by
+ `sectionBreakElement()->startWithMeasureOne()`.
+
+--- a/libmscore/layout.cpp
++++ b/libmscore/layout.cpp
+@@ -2249,7 +2249,7 @@ int LayoutContext::adjustMeasureNo(MeasureBase* m)
+       m->setNo(measureNo);
+       if (!m->irregular())          // don’t count measure
+             ++measureNo;
+-      if (m->sectionBreak())
++      if (m->sectionBreakElement() && m->sectionBreakElement()->startWithMeasureOne())
+             measureNo = 0;
+       return measureNo;
+       }
+@@ -4429,7 +4429,8 @@ void Score::doLayoutRange(const Fraction& st, const Fraction& et)
+                   lc.tick      = Fraction(0,1);
+                   }
+             else {
+-                  if (lc.nextMeasure->prevMeasure()->sectionBreak())
++                  LayoutBreak* sectionBreak = lc.nextMeasure->prevMeasure()->sectionBreakElement();
++                  if (sectionBreak && sectionBreak->startWithMeasureOne())
+                         lc.measureNo = 0;
+                   else
+                         lc.measureNo = lc.nextMeasure->prevMeasure()->no() + 1; // will be adjusted later with respect
diff --git a/debian/patches/backports/30-playback-fermata.diff b/debian/patches/backports/30-playback-fermata.diff
new file mode 100644 (file)
index 0000000..add9fa7
--- /dev/null
@@ -0,0 +1,16 @@
+Origin: vendor, commit:993d705a293f7805d69ad3618317e1a02a71b26e
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #283628: fermata "play" property does not work
+ Resolves: https://musescore.org/node/283628.
+
+--- a/libmscore/score.cpp
++++ b/libmscore/score.cpp
+@@ -512,7 +512,7 @@ void Score::rebuildTempoAndTimeSigMaps(Measure* measure)
+                         continue;
+                   qreal stretch = 0.0;
+                   for (Element* e : segment.annotations()) {
+-                        if (e->isFermata())
++                        if (e->isFermata() && toFermata(e)->play())
+                               stretch = qMax(stretch, toFermata(e)->timeStretch());
+                         else if (e->isTempoText()) {
+                               TempoText* tt = toTempoText(e);
diff --git a/debian/patches/backports/31-ui-enter-slur.diff b/debian/patches/backports/31-ui-enter-slur.diff
new file mode 100644 (file)
index 0000000..6490e4f
--- /dev/null
@@ -0,0 +1,35 @@
+Origin: vendor, commit:d173093c407e96dbebb0b8684f7c0fa5c38e6904
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #166116: Slurs terminate in wrong spot in voices other than 1
+
+--- a/libmscore/cmd.cpp
++++ b/libmscore/cmd.cpp
+@@ -783,6 +783,11 @@ Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction
+             connectTies();
+       if (nr) {
+             if (_is.slur() && nr->type() == ElementType::NOTE) {
++                  // If the start element was the same as the end element when the slur was created,
++                  // the end grip of the front slur segment was given an x-offset of 3.0 * spatium().
++                  // Now that the slur is about to be given a new end element, this should be reset.
++                  if (_is.slur()->endElement() == _is.slur()->startElement())
++                        _is.slur()->frontSegment()->reset();
+                   //
+                   // extend slur
+                   //
+diff --git a/mscore/scoreview.cpp b/mscore/scoreview.cpp
+index 127f27175..6eef6ced6 100644
+--- a/mscore/scoreview.cpp
++++ b/mscore/scoreview.cpp
+@@ -3442,8 +3442,11 @@ void ScoreView::addSlur()
+       InputState& is = _score->inputState();
+       if (noteEntryMode() && is.slur()) {
+             const std::vector<SpannerSegment*>& el = is.slur()->spannerSegments();
+-            if (!el.empty())
++            if (!el.empty()) {
+                   el.front()->setSelected(false);
++                  // Now make sure that the slur segment is redrawn so that it does not *look* selected
++                  update();
++                  }
+             is.setSlur(nullptr);
+             return;
+             }
diff --git a/debian/patches/backports/32-ui-enter-timesig.diff b/debian/patches/backports/32-ui-enter-timesig.diff
new file mode 100644 (file)
index 0000000..e7824b4
--- /dev/null
@@ -0,0 +1,26 @@
+Origin: vendor, commit:72c6b76708d5c1069bcc1630b4ae4803c76da8b3
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #297477: apply alla breve to 4/4 changes to 2/2 instead
+ Resolves: https://musescore.org/en/node/297477.
+ .
+ 4/4 and alla breve have different `_sig` value, so `setSig()` is
+ called, but if it's called after `_timeSigType` is changed, `setSig()`
+ will make the type `NORMAL` again, because if it's called by
+ `setProperty()`, there isn't a `TimeSigType` parameter available
+ (making it available will cause further issues), so `setSig()` uses the
+ default `NORMAL` as the type, thus the new type is overwritten.
+ .
+ If `setSig()` is called before `_timeSigType` is changed, all will be fine.
+
+--- a/libmscore/edit.cpp
++++ b/libmscore/edit.cpp
+@@ -784,8 +784,8 @@ void Score::cmdAddTimeSig(Measure* fm, int staffIdx, TimeSig* ts, bool local)
+             for (int si = 0; si < n; ++si) {
+                   TimeSig* nsig = toTimeSig(seg->element(si * VOICES));
+                   nsig->undoChangeProperty(Pid::SHOW_COURTESY, ts->showCourtesySig());
+-                  nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
+                   nsig->undoChangeProperty(Pid::TIMESIG, QVariant::fromValue(ts->sig()));
++                  nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
+                   nsig->undoChangeProperty(Pid::NUMERATOR_STRING, ts->numeratorString());
+                   nsig->undoChangeProperty(Pid::DENOMINATOR_STRING, ts->denominatorString());
+                   nsig->undoChangeProperty(Pid::TIMESIG_STRETCH, QVariant::fromValue(ts->stretch()));
diff --git a/debian/patches/backports/33-playback-articulations.diff b/debian/patches/backports/33-playback-articulations.diff
new file mode 100644 (file)
index 0000000..f58bb5a
--- /dev/null
@@ -0,0 +1,41 @@
+Origin: vendor, commit:c1365329758d18f6f71583059c007ff31d6b1f7c
+Author: Peter Hieu Vu <peter.vu8@gmail.com>
+Description: fix #288618: play property for articulations
+
+--- a/libmscore/rendermidi.cpp
++++ b/libmscore/rendermidi.cpp
+@@ -507,8 +507,10 @@ static void collectMeasureEventsSimple(EventMap* events, Measure* m, Staff* staf
+                   int channel = instr->channel(chord->upNote()->subchannel())->channel();
+                   events->registerChannel(channel);
+-                  for (Articulation* a : chord->articulations())
+-                        instr->updateVelocity(&velocity,channel, a->articulationName());
++                  for (Articulation* a : chord->articulations()) {
++                        if (a->playArticulation())
++                              instr->updateVelocity(&velocity,channel, a->articulationName());
++                        }
+                   if ( !graceNotesMerged(chord))
+                       for (Chord* c : chord->graceNotesBefore())
+@@ -879,6 +881,8 @@ static void collectMeasureEventsDefault(EventMap* events, Measure* m, Staff* sta
+                         if (singleNoteDynamics || hasArticulations || hasChangingDynamic) {
+                               if (chord != 0 && hasArticulations) {
+                                     for (Articulation* a : chord->articulations()) {
++                                          if (!a->playArticulation())
++                                                continue;
+                                           if (velocityMiddle == -1)
+                                                 velocityMiddle = velocityStart;
+                                           instr->updateVelocity(&velocityStart, channel, a->articulationName());
+@@ -939,8 +943,10 @@ static void collectMeasureEventsDefault(EventMap* events, Measure* m, Staff* sta
+                         } // if instr->singleNoteDynamics()
+                   else {
+                         if (chord != 0) {
+-                              for (Articulation* a : chord->articulations())
+-                                    instr->updateVelocity(&velocity, channel, a->articulationName());
++                              for (Articulation* a : chord->articulations()) {
++                                    if (a->playArticulation())
++                                          instr->updateVelocity(&velocity, channel, a->articulationName());
++                                    }
+                               }
+                         // Add a single expression value to match the velocity, since this instrument should
+                         // not use single note dynamics.
diff --git a/debian/patches/backports/33a-playback-articulations.diff b/debian/patches/backports/33a-playback-articulations.diff
new file mode 100644 (file)
index 0000000..898b00b
--- /dev/null
@@ -0,0 +1,31 @@
+Origin: vendor, commit:9a8d4fa50ef53a9ba8c62dd1e77978c3cdeef763
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: fix #298188: fix not playing a note if its articulation playback is turned off
+ Complements the fix in c1365329758d18f6f71583059c007ff31d6b1f7c to
+ take "play" property of articulations into account in MIDI rendering.
+ .
+ Also fix a test for this to be actually executed and to cover the
+ case of single-note dynamics MIDI rendering.
+
+--- a/libmscore/rendermidi.cpp
++++ b/libmscore/rendermidi.cpp
+@@ -869,10 +869,16 @@ static void collectMeasureEventsDefault(EventMap* events, Measure* m, Staff* sta
+                                     singleNoteDynamics = false;
+                               }
+-                        // Check for articulations
++                        // Check for articulations to be rendered for playback
+                         bool hasArticulations = false;
+-                        if (chord)
+-                              hasArticulations = chord->articulations().count() > 0;
++                        if (chord) {
++                              for (const Articulation* a : chord->articulations()) {
++                                    if (a->playArticulation()) {
++                                          hasArticulations = true;
++                                          break;
++                                          }
++                                    }
++                              }
+                         //
+                         // Add CC events
diff --git a/debian/patches/backports/34-crash-drag.diff b/debian/patches/backports/34-crash-drag.diff
new file mode 100644 (file)
index 0000000..efbd741
--- /dev/null
@@ -0,0 +1,31 @@
+Origin: vendor, commit:484cd7e81e378f0fedf26ecd0a4d6cd5b77d3c61
+Author: MarcSabatella <marc@outsideshore.com>
+Description: fix #297152: crash on Ctrl+Shift+drag on Linux (under ChromeOS)
+ Resolves: https://musescore.org/en/node/297152
+ .
+ This might be unique to Linux apps on ChromeOS,
+ or it might apply to other Debian "stretch systems",
+ but apparently passing in a null or 1x1 pixmap to a QDrag
+ causes it to crash on exec().
+ This PR fixes the problem by making the pixmap 2x2.
+ It also allocates it statically to be sure there is no issue
+ with it coming off the stack.
+ I also replaced the deprecated QDrag::start() call with QDrag::exec(),
+ which we already do when dragging *from* the palette.
+ This is recommended as per Qt guidelines.
+
+--- a/mscore/scoreview.cpp
++++ b/mscore/scoreview.cpp
+@@ -3732,8 +3732,10 @@ void ScoreView::cloneElement(Element* e)
+             e = toSpannerSegment(e)->spanner();
+       mimeData->setData(mimeSymbolFormat, e->mimeData(QPointF()));
+       drag->setMimeData(mimeData);
+-      drag->setPixmap(QPixmap());
+-      drag->start(Qt::CopyAction);
++      static QPixmap pixmap = QPixmap(2, 2);    // null or 1x1 crashes on Linux under ChromeOS?!
++      pixmap.fill(Qt::white);
++      drag->setPixmap(pixmap);
++      drag->exec(Qt::CopyAction);
+       }
+ //---------------------------------------------------------
diff --git a/debian/patches/backports/35-ui-mixer-reset.diff b/debian/patches/backports/35-ui-mixer-reset.diff
new file mode 100644 (file)
index 0000000..66f08af
--- /dev/null
@@ -0,0 +1,14 @@
+Origin: vendor, commit:34982d31c3b8f69416361af0b181bcbc70d89bce
+Author: JoshuaBonn1 <joshuabonn96@gmail.com>
+Description: Fix #279024: double click on mixer's dial and sliders should reset them to their defaults
+
+--- a/mscore/mixertrackpart.cpp
++++ b/mscore/mixertrackpart.cpp
+@@ -124,6 +124,7 @@ MixerTrackPart::MixerTrackPart(QWidget *parent, MixerTrackItemPtr mti, bool expa
+       panSlider->setToolTip(tr("Pan: %1").arg(QString::number(chan->pan())));
+       panSlider->setMaxValue(127);
+       panSlider->setMinValue(0);
++      panSlider->setDclickValue1(64);
+       connect(volumeSlider, SIGNAL(valueChanged(double)),      SLOT(volumeChanged(double)));
+       connect(panSlider,    SIGNAL(valueChanged(double, int)), SLOT(panChanged(double)));
diff --git a/debian/patches/backports/36-plugin-state.diff b/debian/patches/backports/36-plugin-state.diff
new file mode 100644 (file)
index 0000000..632c27a
--- /dev/null
@@ -0,0 +1,14 @@
+Origin: vendor, commit:7470a650db8335e50f5edb153255e10c9c31cca9
+Author: Sonny-Klotz <syk_778@hotmail.fr>
+Description: fix #291085 : cursor.addNote update internal segment reference
+
+--- a/mscore/plugin/api/cursor.cpp
++++ b/mscore/plugin/api/cursor.cpp
+@@ -218,6 +218,7 @@ void Cursor::addNote(int pitch)
+             setDuration(1, 4);
+       NoteVal nval(pitch);
+       _score->addPitch(nval, false);
++      _segment = _score->inputState().segment();
+       }
+ //---------------------------------------------------------
diff --git a/debian/patches/backports/37-crash-parts.diff b/debian/patches/backports/37-crash-parts.diff
new file mode 100644 (file)
index 0000000..f47f0c3
--- /dev/null
@@ -0,0 +1,22 @@
+Origin: vendor, commit:c8a27535c248c357234e9a19bea850112c362793
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: fix #297468: fix a crash on reading a corrupted score with linear layout mode in part
+ In a corrupted score tick values may sometimes be not synchronized
+ between master score and parts. This may lead to incorrect setting
+ of layoutAll flag as ticks from different scores are compared.
+ Ensuring that only master score ticks are compared fixes layoutAll
+ flag for scores corrupted that way and prevents a crash due to not
+ making a full layout on score loading. This change makes no
+ difference for correctly saved scores.
+
+--- a/libmscore/layout.cpp
++++ b/libmscore/layout.cpp
+@@ -4369,7 +4369,7 @@ void Score::doLayoutRange(const Fraction& st, const Fraction& et)
+             }
+ //      if (!_systems.isEmpty())
+ //            return;
+-      bool layoutAll = stick <= Fraction(0,1) && (etick < Fraction(0,1) || etick >= last()->endTick());
++      bool layoutAll = stick <= Fraction(0,1) && (etick < Fraction(0,1) || etick >= masterScore()->last()->endTick());
+       if (stick < Fraction(0,1))
+             stick = Fraction(0,1);
+       if (etick < Fraction(0,1))
diff --git a/debian/patches/backports/38-ui-units.diff b/debian/patches/backports/38-ui-units.diff
new file mode 100644 (file)
index 0000000..ae90005
--- /dev/null
@@ -0,0 +1,25 @@
+Origin: vendor, commit:c7b37d8a26f1ae85136ff3bba9d8ae32134777dd
+Author: Maurizio M. Gavioli <mmg@vistamaresoft.com>
+Description: Fix #296345 : "Offset" suffix for text style fixed to "sp"
+ In the Edit Text Style dlg box, the suffixes of "Offset" values are
+ always "sp", but when "Follow staff size" is unchecked, the values are
+ considered "mm".
+
+--- a/mscore/editstyle.cpp
++++ b/mscore/editstyle.cpp
+@@ -1424,9 +1424,12 @@ void EditStyle::textStyleChanged(int row)
+                         resetTextStyleOffset->setEnabled(cs->styleV(a.sid) != MScore::defaultStyle().value(a.sid));
+                         break;
+-                  case Pid::SIZE_SPATIUM_DEPENDENT:
+-                        textStyleSpatiumDependent->setChecked(cs->styleV(a.sid).toBool());
+-                        resetTextStyleSpatiumDependent->setEnabled(cs->styleV(a.sid) != MScore::defaultStyle().value(a.sid));
++                  case Pid::SIZE_SPATIUM_DEPENDENT: {
++                        QVariant val = cs->styleV(a.sid);
++                        textStyleSpatiumDependent->setChecked(val.toBool());
++                        resetTextStyleSpatiumDependent->setEnabled(val != MScore::defaultStyle().value(a.sid));
++                        textStyleOffset->setSuffix(val.toBool() ? tr("sp") : tr("mm"));
++                        }
+                         break;
+                   case Pid::FRAME_TYPE:
diff --git a/debian/patches/backports/39-layout-spacer.diff b/debian/patches/backports/39-layout-spacer.diff
new file mode 100644 (file)
index 0000000..5cb43d9
--- /dev/null
@@ -0,0 +1,26 @@
+Origin: vendor, commit:1d56d5564bf3079055dbc8fcbd23469045c292b4
+Author: MarcSabatella <marc@outsideshore.com>
+Description: fix #281253: staff spacer down ignored on bottom of page
+ Resolves: https://musescore.org/en/node/281253
+ .
+ A staff spacer on the bottom system of a page does not work correctly:
+ we are looking foir a spacer on the *first* staff rather than the *last*.
+ So a spacer on the last staff is ignored, and one on the first is used
+ when it shouldn't be.
+ .
+ The code to calculate the amount of spacer required below the last
+ system of a page relies on System::lastVisibleSysStaff(),
+ which is return the correct SysStaff, but we are then attempting
+ to access the idx member, which has never been set up and is always 0.
+ Fix is to initialize idx when setting up the list of staves.
+
+--- a/libmscore/system.cpp
++++ b/libmscore/system.cpp
+@@ -162,6 +162,7 @@ Box* System::vbox() const
+ SysStaff* System::insertStaff(int idx)
+       {
+       SysStaff* staff = new SysStaff;
++      staff->idx = idx;
+       if (idx) {
+             // HACK: guess position
+             staff->bbox().setY(_staves[idx-1]->y() + 6 * spatium());
diff --git a/debian/patches/backports/40-crash-del-hbox.diff b/debian/patches/backports/40-crash-del-hbox.diff
new file mode 100644 (file)
index 0000000..0589b85
--- /dev/null
@@ -0,0 +1,20 @@
+Origin: vendor, commit:24e239ed74770e24d2981883d5d435302735567a
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: Fix a crash on removing HBox from a score
+
+--- a/libmscore/box.cpp
++++ b/libmscore/box.cpp
+@@ -458,9 +458,12 @@ void HBox::layout()
+             setPos(x, y);
+             bbox().setRect(0.0, 0.0, w, h);
+             }
+-      else {
++      else if (system()) {
+             bbox().setRect(0.0, 0.0, point(boxWidth()), system()->height());
+             }
++      else {
++            bbox().setRect(0.0, 0.0, 50, 50);
++            }
+       Box::layout();
+       }
diff --git a/debian/patches/backports/41-crash-triplet.diff b/debian/patches/backports/41-crash-triplet.diff
new file mode 100644 (file)
index 0000000..787bc4c
--- /dev/null
@@ -0,0 +1,21 @@
+Origin: vendor, commit:f654f707997610f7de96eeaeadda1ad296034630
+Author: Rory McLeod <mcleodnw@gmail.com>
+Description: fix #285040 crash when changing a triplet's rest's duration
+ - Prevent a null pointer deref when searching a linked tuplet.
+
+--- a/libmscore/edit.cpp
++++ b/libmscore/edit.cpp
+@@ -4801,8 +4801,11 @@ void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
+                                     if (nt == 0)
+                                           qWarning("linked tuplet not found");
+                                     }
+-                              newcr->setTuplet(nt);
+-                              nt->setParent(newcr->measure());
++
++                              if (nt) {
++                                    newcr->setTuplet(nt);
++                                    nt->setParent(newcr->measure());
++                                    }
+                               }
+                         }
diff --git a/debian/patches/backports/42-crash-no-score.diff b/debian/patches/backports/42-crash-no-score.diff
new file mode 100644 (file)
index 0000000..1848424
--- /dev/null
@@ -0,0 +1,17 @@
+Origin: vendor, commit:4118680b975ad3654e6f087cd261fc0b4ed51eca
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #300107: crash after closing a menu bar pop-up window if no score is loaded
+
+--- a/mscore/plugin/qmlpluginengine.cpp
++++ b/mscore/plugin/qmlpluginengine.cpp
+@@ -48,7 +48,9 @@ void QmlPluginEngine::beginEndCmd(MuseScore* ms, bool inUndoRedo)
+             undoRedo = true;
+       const Score* cs = ms->currentScore();
+-      currScoreState = cs->masterScore()->state(); // score and excerpts have united undo stack so we are better to track master score
++
++      // score and excerpts have united undo stack so we are better to track master score
++      currScoreState = cs ? cs->masterScore()->state() : ScoreContentState();
+       // TODO: most of plugins are never deleted so receivers usually never decrease
+       if (!receivers(SIGNAL(endCmd(const QMap<QString, QVariant>&))))
diff --git a/debian/patches/backports/43-playback-glitches.diff b/debian/patches/backports/43-playback-glitches.diff
new file mode 100644 (file)
index 0000000..c4eeec5
--- /dev/null
@@ -0,0 +1,259 @@
+Origin: vendor, commit:39305c9bc214795c7933024d4082105c4600c962
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: fix #280493: fix audio glitches on note input and playback on MacOS
+ Ensure that realtime-context routines in sequencer do not access
+ QSettings directly and use cached preferences for faster access.
+ The exception is a hack with fake JACK transport on count-in: it
+ seems to require working with QSettings-based preferences directly.
+ .
+ This improves general performance of playback so audible glitches
+ which were reported for MacOS seem to not appear with this patch.
+
+--- a/mscore/musescore.cpp
++++ b/mscore/musescore.cpp
+@@ -512,6 +512,9 @@ void MuseScore::preferencesChanged(bool fromWorkspace)
+       newWizard = 0;
+       reloadInstrumentTemplates();
+       updateInstrumentDialog();
++
++      if (seq)
++            seq->preferencesChanged();
+       }
+ //---------------------------------------------------------
+diff --git a/mscore/seq.cpp b/mscore/seq.cpp
+index 0c5338d07..f0239ac26 100644
+--- a/mscore/seq.cpp
++++ b/mscore/seq.cpp
+@@ -234,6 +234,41 @@ void Seq::setScoreView(ScoreView* v)
+             }
+       }
++//---------------------------------------------------------
++//   Seq::CachedPreferences::update
++//---------------------------------------------------------
++
++void Seq::CachedPreferences::update()
++      {
++      portMidiOutputLatencyMilliseconds = preferences.getInt(PREF_IO_PORTMIDI_OUTPUTLATENCYMILLISECONDS);
++      jackTimeBaseMaster = preferences.getBool(PREF_IO_JACK_TIMEBASEMASTER);
++      useJackTransport = preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT);
++      useJackMidi = preferences.getBool(PREF_IO_JACK_USEJACKMIDI);
++      useJackAudio = preferences.getBool(PREF_IO_JACK_USEJACKAUDIO);
++      useAlsaAudio = preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO);
++      usePortAudio = preferences.getBool(PREF_IO_PORTAUDIO_USEPORTAUDIO);
++      usePulseAudio = preferences.getBool(PREF_IO_PULSEAUDIO_USEPULSEAUDIO);
++      }
++
++//---------------------------------------------------------
++//   startTransport
++//---------------------------------------------------------
++
++void Seq::startTransport()
++      {
++      cachedPrefs.update();
++      _driver->startTransport();
++      }
++
++//---------------------------------------------------------
++//   stopTransport
++//---------------------------------------------------------
++
++void Seq::stopTransport()
++      {
++      _driver->stopTransport();
++      }
++
+ //---------------------------------------------------------
+ //   init
+ //    return false on error
+@@ -246,6 +281,7 @@ bool Seq::init(bool hotPlug)
+             running = false;
+             return false;
+             }
++      cachedPrefs.update();
+       running = true;
+       return true;
+       }
+@@ -332,7 +368,7 @@ void Seq::start()
+             useJackTransportSavedFlag    = true;
+             preferences.setPreference(PREF_IO_JACK_USEJACKTRANSPORT, false);
+             }
+-      _driver->startTransport();
++      startTransport();
+       }
+ //---------------------------------------------------------
+@@ -355,7 +391,7 @@ void Seq::stop()
+       if (!_driver)
+             return;
+       if (!preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) || (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && _driver->getState() == Transport::PLAY))
+-            _driver->stopTransport();
++            stopTransport();
+       if (cv)
+             cv->setCursorOn(false);
+       if (midiRenderFuture.isRunning())
+@@ -567,7 +603,7 @@ void Seq::processMessages()
+                               int utick = cs->utime2utick(qreal(playFrame) / qreal(MScore::sampleRate));
+                               cs->tempomap()->setRelTempo(msg.realVal);
+                               playFrame = cs->utick2utime(utick) * MScore::sampleRate;
+-                              if (preferences.getBool(PREF_IO_JACK_TIMEBASEMASTER) && preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT))
++                              if (cachedPrefs.jackTimeBaseMaster && cachedPrefs.useJackTransport)
+                                     _driver->seekTransport(utick + 2 * cs->utime2utick(qreal((_driver->bufferSize()) + 1) / qreal(MScore::sampleRate)));
+                               }
+                         else
+@@ -680,7 +716,7 @@ void Seq::process(unsigned framesPerPeriod, float* buffer)
+       if (driverState != state) {
+             // Got a message from JACK Transport panel: Play
+             if (state == Transport::STOP && driverState == Transport::PLAY) {
+-                  if ((preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_JACK_USEJACKAUDIO)) && !getAction("play")->isChecked()) {
++                  if ((cachedPrefs.useJackMidi || cachedPrefs.useJackAudio) && !getAction("play")->isChecked()) {
+                         // Do not play while editing elements
+                         if (mscore->state() != STATE_NORMAL || !isRunning() || !canStart())
+                               return;
+@@ -689,18 +725,18 @@ void Seq::process(unsigned framesPerPeriod, float* buffer)
+                         // If we just launch MuseScore and press "Play" on JACK Transport with time 0:00
+                         // MuseScore doesn't seek to 0 and guiPos is uninitialized, so let's make it manually
+-                        if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && getCurTick() == 0)
++                        if (cachedPrefs.useJackTransport && getCurTick() == 0)
+                               seekRT(0);
+                         // Switching to fake transport while playing count in
+                         // to prevent playing in other applications with our ticks simultaneously
+-                        if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && mscore->countIn()) {
++                        if (cachedPrefs.useJackTransport && mscore->countIn()) {
+                               // Stopping real JACK Transport
+-                              _driver->stopTransport();
++                              stopTransport();
+                               // Starting fake transport
+                               useJackTransportSavedFlag = preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT);
+                               preferences.setPreference(PREF_IO_JACK_USEJACKTRANSPORT, false);
+-                              _driver->startTransport();
++                              startTransport();
+                               }
+                         }
+                   // Initializing instruments every time we start playback.
+@@ -801,7 +837,7 @@ void Seq::process(unsigned framesPerPeriod, float* buffer)
+                                     if (playPosUTick >= loopOutUTick || cs->repeatList().utick2tick(playPosUTick) < cs->loopInTick().ticks()) {
+                                           qDebug ("Process: playPosUTick = %d, cs->loopInTick().ticks() = %d, cs->loopOutTick().ticks() = %d, getCurTick() = %d, loopOutUTick = %d, playFrame = %d",
+                                                             playPosUTick,      cs->loopInTick().ticks(),      cs->loopOutTick().ticks(),      getCurTick(),      loopOutUTick,    *pPlayFrame);
+-                                          if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT)) {
++                                          if (cachedPrefs.useJackTransport) {
+                                                 int loopInUTick = cs->repeatList().tick2utick(cs->loopInTick().ticks());
+                                                 _driver->seekTransport(loopInUTick);
+                                                 if (loopInUTick != 0) {
+@@ -890,14 +926,14 @@ void Seq::process(unsigned framesPerPeriod, float* buffer)
+                         // Connecting to JACK Transport if MuseScore was temporarily disconnected from it
+                         if (useJackTransportSavedFlag) {
+                               // Stopping fake driver
+-                              _driver->stopTransport();
++                              stopTransport();
+                               preferences.setPreference(PREF_IO_JACK_USEJACKTRANSPORT, true);
+                               // Starting the real JACK Transport. All applications play in sync now
+-                              _driver->startTransport();
++                              startTransport();
+                               }
+                         }
+                   else
+-                        _driver->stopTransport();
++                        stopTransport();
+                   }
+             }
+       else {
+@@ -952,7 +988,7 @@ void Seq::process(unsigned framesPerPeriod, float* buffer)
+ void Seq::initInstruments(bool realTime)
+       {
+       // Add midi out ports if necessary
+-      if (cs && (preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO))) {
++      if (cs && (cachedPrefs.useJackMidi || cachedPrefs.useAlsaAudio)) {
+             // Increase the maximum number of midi ports if user adds staves/instruments
+             int scoreMaxMidiPort = cs->masterScore()->midiPortCount();
+             if (maxMidiOutPort < scoreMaxMidiPort)
+@@ -974,7 +1010,7 @@ void Seq::initInstruments(bool realTime)
+                         sendEvent(event);
+                   }
+             // Setting pitch bend sensitivity to 12 semitones for external synthesizers
+-            if ((preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO)) && mm.channel() != 9) {
++            if ((cachedPrefs.useJackMidi || cachedPrefs.useAlsaAudio) && mm.channel() != 9) {
+                   if (realTime) {
+                         putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_LRPN, 0));
+                         putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HRPN, 0));
+@@ -1205,7 +1241,7 @@ void Seq::seek(int utick)
+ void Seq::seekRT(int utick)
+       {
+-      if (preferences.getBool(PREF_IO_JACK_USEJACKTRANSPORT) && utick > endUTick)
++      if (cachedPrefs.useJackTransport && utick > endUTick)
+                   utick = 0;
+       seekCommon(utick);
+       setPos(utick);
+@@ -1294,7 +1330,7 @@ void Seq::stopNotes(int channel, bool realTime)
+             if (cs->midiChannel(channel) != 9)
+                   send(NPlayEvent(ME_PITCHBEND,  channel, 0, 64));
+             }
+-      if (preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO) || preferences.getBool(PREF_IO_JACK_USEJACKAUDIO) || preferences.getBool(PREF_IO_PULSEAUDIO_USEPULSEAUDIO) || preferences.getBool(PREF_IO_PORTAUDIO_USEPORTAUDIO))
++      if (cachedPrefs.useAlsaAudio || cachedPrefs.useJackAudio || cachedPrefs.usePulseAudio || cachedPrefs.usePortAudio)
+             _synti->allNotesOff(channel);
+       }
+@@ -1510,7 +1546,7 @@ void Seq::putEvent(const NPlayEvent& event, unsigned framePos)
+       _synti->play(event, syntiIdx);
+       // midi
+-      if (_driver != 0 && (preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO) || preferences.getBool(PREF_IO_PORTAUDIO_USEPORTAUDIO)))
++      if (_driver != 0 && (cachedPrefs.useJackMidi || cachedPrefs.useAlsaAudio || cachedPrefs.usePortAudio))
+             _driver->putEvent(event, framePos);
+       }
+@@ -1738,7 +1774,7 @@ void Seq::handleTimeSigTempoChanged()
+ void Seq::setInitialMillisecondTimestampWithLatency()
+       {
+      #ifdef USE_PORTMIDI
+-           initialMillisecondTimestampWithLatency = Pt_Time() + preferences.getInt(PREF_IO_PORTMIDI_OUTPUTLATENCYMILLISECONDS);
++           initialMillisecondTimestampWithLatency = Pt_Time() + cachedPrefs.portMidiOutputLatencyMilliseconds;
+            //qDebug("PortMidi initialMillisecondTimestampWithLatency: %d = %d + %d", initialMillisecondTimestampWithLatency, unsigned(Pt_Time()), preferences.getInt(PREF_IO_PORTMIDI_OUTPUTLATENCYMILLISECONDS));
+      #endif
+      }
+diff --git a/mscore/seq.h b/mscore/seq.h
+index 8de8c85fe..6706b5a9f 100644
+--- a/mscore/seq.h
++++ b/mscore/seq.h
+@@ -165,6 +165,28 @@ class Seq : public QObject, public Sequencer {
+       QTimer* heartBeatTimer;
+       QTimer* noteTimer;
++      /**
++       * Preferences cached for faster access in realtime context.
++       * Using QSettings-based Ms::Preferences directly results in
++       * audible glitches on some systems (esp. MacOS, see #280493).
++       */
++      struct CachedPreferences {
++            int portMidiOutputLatencyMilliseconds = 0;
++            bool jackTimeBaseMaster = false;
++            bool useJackTransport = false;
++            bool useJackMidi = false;
++            bool useJackAudio = false;
++            bool useAlsaAudio = false;
++            bool usePortAudio = false;
++            bool usePulseAudio = false;
++
++            void update();
++            };
++      CachedPreferences cachedPrefs;
++
++      void startTransport();
++      void stopTransport();
++
+       void renderChunk(const MidiRenderer::Chunk&, EventMap*);
+       void updateEventsEnd();
+@@ -266,6 +288,8 @@ class Seq : public QObject, public Sequencer {
+       void setInitialMillisecondTimestampWithLatency();
+       unsigned getCurrentMillisecondTimestampWithLatency(unsigned framePos) const;
++
++      void preferencesChanged() { cachedPrefs.update(); }
+       };
+ extern Seq* seq;
diff --git a/debian/patches/backports/44-playback-repeats.diff b/debian/patches/backports/44-playback-repeats.diff
new file mode 100644 (file)
index 0000000..5dced75
--- /dev/null
@@ -0,0 +1,29 @@
+Origin: vendor, commit:0dcbf2af0f744a985b39ba315185042795c618ab
+Author: James Thistlewood <jamesthistlewood@gmail.com>
+Description: fix #301340: dynamic playback after repeats does not match with score
+
+--- a/libmscore/rendermidi.cpp
++++ b/libmscore/rendermidi.cpp
+@@ -314,6 +314,7 @@ static void collectNote(EventMap* events, int channel, const Note* note, qreal v
+             // Get the velocity used for this note from the staff
+             // This allows correct playback of tremolos even without SND enabled.
+             int velo;
++            Fraction nonUnwoundTick = Fraction::fromTicks(on - tickOffset);
+             if (config.useSND) {
+                   switch (config.method) {
+                         case DynamicsRenderMethod::FIXED_MAX:
+@@ -321,12 +322,12 @@ static void collectNote(EventMap* events, int channel, const Note* note, qreal v
+                               break;
+                         case DynamicsRenderMethod::SEG_START:
+                         default:
+-                              velo = staff->velocities().val(Fraction::fromTicks(on));
++                              velo = staff->velocities().val(nonUnwoundTick);
+                               break;
+                         }
+                   }
+             else {
+-                  velo = staff->velocities().val(Fraction::fromTicks(on));
++                  velo = staff->velocities().val(nonUnwoundTick);
+                   }
+             velo *= velocityMultiplier;
diff --git a/debian/patches/backports/45-pianoroll-xml.diff b/debian/patches/backports/45-pianoroll-xml.diff
new file mode 100644 (file)
index 0000000..6472be5
--- /dev/null
@@ -0,0 +1,19 @@
+Origin: vendor, commit:d83b768f87a9d05a8b33a5b80e7043922ab7c337
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #301116: don't write default note event values along with non-default ones
+
+--- a/libmscore/noteevent.cpp
++++ b/libmscore/noteevent.cpp
+@@ -41,9 +41,9 @@ void NoteEvent::read(XmlReader& e)
+ void NoteEvent::write(XmlWriter& xml) const
+       {
+       xml.stag("Event");
+-      xml.tag("pitch", _pitch);
+-      xml.tag("ontime", _ontime);
+-      xml.tag("len", _len);
++      xml.tag("pitch", _pitch, 0);
++      xml.tag("ontime", _ontime, 0);
++      xml.tag("len", _len, NOTE_LENGTH);
+       xml.etag();
+       }
diff --git a/debian/patches/backports/46-ui-ambitus.diff b/debian/patches/backports/46-ui-ambitus.diff
new file mode 100644 (file)
index 0000000..cf48621
--- /dev/null
@@ -0,0 +1,34 @@
+Origin: vendor, commit:cdd80e419dddb37a39c18ae52827189e8b6b7430
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #284344: ambitus not redrawn on Update Range
+
+--- a/mscore/inspector/inspectorAmbitus.cpp
++++ b/mscore/inspector/inspectorAmbitus.cpp
+@@ -144,18 +144,22 @@ void InspectorAmbitus::valueChanged(int idx)
+             }
+       }
+-}
+-
+ //---------------------------------------------------------
+-//   on updateRange clicked
++//   updateRange
++//    Automatically adjust range based on the score
+ //---------------------------------------------------------
+-void Ms::InspectorAmbitus::updateRange()
++void InspectorAmbitus::updateRange()
+       {
+       Ambitus* range = toAmbitus(inspector->element());
+       range->updateRange();
+-      range->layout();              // redo layout
++
++      range->score()->startCmd();
++      range->triggerLayout();
++      range->score()->endCmd();
++
+       setElement(); // set Inspector values to range properties
+       valueChanged(AmbitusControl::TOPTPC); // force score to notice new range properties
+       }
++}
diff --git a/debian/patches/backports/47-ui-ambitus.diff b/debian/patches/backports/47-ui-ambitus.diff
new file mode 100644 (file)
index 0000000..3004a89
--- /dev/null
@@ -0,0 +1,19 @@
+Origin: vendor, commit:03d2242b8b1537348986f54fc9c5e8baddbee94c
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #305941: changing octaves for ambiti doesn't work correctly
+
+--- a/libmscore/ambitus.cpp
++++ b/libmscore/ambitus.cpp
+@@ -698,10 +698,10 @@ bool Ambitus::setProperty(Pid propertyId, const QVariant& v)
+                   setBottomPitch(v.toInt());
+                   break;
+             case Pid::FBPARENTHESIS3:        // recycled property = octave of _topPitch
+-                  setTopPitch(topPitch() % 12 + v.toInt() * 12);
++                  setTopPitch(topPitch() % 12 + (v.toInt() + 1) * 12);
+                   break;
+             case Pid::FBPARENTHESIS4:        // recycled property = octave of _bottomPitch
+-                  setBottomPitch(bottomPitch() % 12 + v.toInt() * 12);
++                  setBottomPitch(bottomPitch() % 12 + (v.toInt() + 1) * 12);
+                   break;
+             default:
+                   return Element::setProperty(propertyId, v);
diff --git a/debian/patches/backports/48-layout-ottava.diff b/debian/patches/backports/48-layout-ottava.diff
new file mode 100644 (file)
index 0000000..8c10086
--- /dev/null
@@ -0,0 +1,15 @@
+Origin: vendor, commit:5301009829215619953d33f03932b09c3ead5e7b
+Author: Joachim Schmitz <jojo@schmitz-digital.de>
+Description: fix #300738: Ottava (8va/8vb) number is too big
+
+--- a/libmscore/style.cpp
++++ b/libmscore/style.cpp
+@@ -498,7 +498,7 @@ static const StyleType styleTypes[] {
+       { Sid::ottavaLineStyle,         "ottavaLineStyle",         QVariant(int(Qt::DashLine)) },
+       { Sid::ottavaNumbersOnly,       "ottavaNumbersOnly",       true },
+       { Sid::ottavaFontFace,          "ottavaFontFace",          "FreeSerif" },
+-      { Sid::ottavaFontSize,          "ottavaFontSize",          12.0 },
++      { Sid::ottavaFontSize,          "ottavaFontSize",          10.0 },
+       { Sid::ottavaFontSpatiumDependent, "ottavaFontSpatiumDependent", true },
+       { Sid::ottavaFontStyle,         "ottavaFontStyle",         int(FontStyle::Normal) },
+       { Sid::ottavaColor,             "ottavaColor",             QColor(0, 0, 0, 255) },
diff --git a/debian/patches/backports/49-pitch-ottava.diff b/debian/patches/backports/49-pitch-ottava.diff
new file mode 100644 (file)
index 0000000..b9e8f3c
--- /dev/null
@@ -0,0 +1,91 @@
+Origin: vendor, commit:66be5a18fa40a6358b0a78723adcd42f15176fcc
+Author: Niek van den Berg <njvdberg@xs4all.nl>
+Description: fix #293593 - Issues with ottavas
+ Corrects first problem: Status doesn't display the pitch of note that have an
+ ottave line. This is solved in Note::tpcUserName().
+ .
+ Solves the second problem in issue 293593: Accidentals do apply if 8va sign is added.
+ This is solved in Note::updateAccidental(). All calculations are based on the
+ effective pitch of the a note rather than the actual pitch. The solution now
+ takes to ottava signs into account by using the actual pitch.
+ .
+ For easily find out wheather an ottava is applied, a new method ottavaCapoFret()
+ is added which returns the pitch offset by an ottava (or capo fret). To prevent
+ code dublication, ppitch() also use this new ottavaCapoFret() method.
+
+--- a/libmscore/note.cpp
++++ b/libmscore/note.cpp
+@@ -763,7 +763,7 @@ int Note::tpc() const
+ QString Note::tpcUserName(bool explicitAccidental) const
+       {
+       QString pitchName = tpc2name(tpc(), NoteSpellingType::STANDARD, NoteCaseType::AUTO, explicitAccidental);
+-      QString octaveName = QString::number((epitch() / 12) - 1);
++      QString octaveName = QString::number(((epitch() + ottaveCapoFret()) / 12) - 1);
+       return pitchName + (explicitAccidental ? " " : "") + octaveName;
+       }
+@@ -2045,13 +2045,14 @@ void Note::updateAccidental(AccidentalState* as)
+             AccidentalVal accVal = tpc2alter(tpc());
+             bool error = false;
+-            AccidentalVal relLineAccVal = as->accidentalVal(relLine, error);
++            int eRelLine = absStep(tpc(), epitch()+ottaveCapoFret());
++            AccidentalVal relLineAccVal = as->accidentalVal(eRelLine, error);
+             if (error) {
+                   qDebug("error accidetalVal");
+                   return;
+                   }
+-            if ((accVal != relLineAccVal) || hidden() || as->tieContext(relLine)) {
+-                  as->setAccidentalVal(relLine, accVal, _tieBack != 0 && _accidental == 0);
++            if ((accVal != relLineAccVal) || hidden() || as->tieContext(eRelLine)) {
++                  as->setAccidentalVal(eRelLine, accVal, _tieBack != 0 && _accidental == 0);
+                   acci = Accidental::value2subtype(accVal);
+                   // if previous tied note has same tpc, don't show accidental
+                   if (_tieBack && _tieBack->startNote()->tpc1() == tpc1())
+@@ -2256,6 +2257,21 @@ void Note::setHeadGroup(NoteHead::Group val)
+       _headGroup = val;
+       }
++//---------------------------------------------------------
++//   ottaveCapoFret
++//    offset added by Ottava's and Capo Fret.
++//---------------------------------------------------------
++
++int Note::ottaveCapoFret() const
++      {
++      Chord* ch = chord();
++      int capoFretId = staff()->capo(ch->segment()->tick());
++      if (capoFretId != 0)
++            capoFretId -= 1;
++
++      return staff()->pitchOffset(ch->segment()->tick()) + capoFretId;
++      }
++
+ //---------------------------------------------------------
+ //   ppitch
+ //    playback pitch
+@@ -2274,11 +2290,8 @@ int Note::ppitch() const
+                         return div.pitch;
+                   }
+             }
+-      int capoFretId = staff()->capo(ch->segment()->tick());
+-      if (capoFretId != 0)
+-            capoFretId -= 1;
+-      return _pitch + staff()->pitchOffset(ch->segment()->tick()) + capoFretId;
++      return _pitch + ottaveCapoFret();
+       }
+ //---------------------------------------------------------
+diff --git a/libmscore/note.h b/libmscore/note.h
+index b0e69b797..24b42782e 100644
+--- a/libmscore/note.h
++++ b/libmscore/note.h
+@@ -330,6 +330,7 @@ public:
+       void setPitch(int val);
+       void setPitch(int pitch, int tpc1, int tpc2);
+       int pitch() const                   { return _pitch;    }
++      int ottaveCapoFret() const;
+       int ppitch() const;           ///< playback pitch
+       int epitch() const;           ///< effective pitch
+       qreal tuning() const                { return _tuning;   }
diff --git a/debian/patches/backports/50-layout-divider.diff b/debian/patches/backports/50-layout-divider.diff
new file mode 100644 (file)
index 0000000..fec69fe
--- /dev/null
@@ -0,0 +1,89 @@
+Origin: vendor, commit:1c15bb0e49405508fe9803b2e371f42f3a61a1bb
+Author: MarcSabatella <marc@outsideshore.com>
+Description: fix #298273: divider not displayed in some cases
+ Resolves: https://musescore.org/en/node/298273
+ .
+ System dividers were not being displayed in certain cases:
+ if a fixed spacer is used, or in single page view.
+ In addition, dividers were displaying that shouldn't be
+ if layout changes and a system that was formerly not last on page
+ suddenly becomes last on page,
+ This is due to a series of errors in layoutPage()
+ where the dividers are managed.
+ This fix involves a number of aspects:
+ 1) checkDivider now takes an extra boolean parameter to force deletion
+ 2) we always call checkDivider with that parameter set to true
+ for the last system of a page
+ 3) in the case where we don't stretch system distance
+ (the clause checking sList, noVerticalStretch, or System layout mode),
+ don't just remove dividers, but do the normal checkDivider call,
+ which adds or removes dividers as appropriate
+ 4) in the calls to checkDivider at the end of the function
+ (which handle the normal case of non-final systems on the page),
+ dion't skip the checkDivider calls if a system hasFixedDownDistance.
+ I believe that check was added because it is appropriate in other places
+ that also check vBox, so it may have looked like this code should match.
+ But it shouldn't, there is no reason to skip dividers in this case.
+ Only the stretch calculations should be skipped.
+
+--- a/libmscore/layout.cpp
++++ b/libmscore/layout.cpp
+@@ -1471,10 +1471,10 @@ void Score::connectTies(bool silent)
+ //   checkDivider
+ //---------------------------------------------------------
+-static void checkDivider(bool left, System* s, qreal yOffset)
++static void checkDivider(bool left, System* s, qreal yOffset, bool remove = false)
+       {
+       SystemDivider* divider = left ? s->systemDividerLeft() : s->systemDividerRight();
+-      if (s->score()->styleB(left ? Sid::dividerLeft : Sid::dividerRight)) {
++      if ((s->score()->styleB(left ? Sid::dividerLeft : Sid::dividerRight)) && !remove) {
+             if (!divider) {
+                   divider = new SystemDivider(s->score());
+                   divider->setDividerType(left ? SystemDivider::Type::LEFT : SystemDivider::Type::RIGHT);
+@@ -1534,23 +1534,25 @@ static void layoutPage(Page* page, qreal restHeight)
+             sList.push_back(s1);
+             }
++      // last systenm needs no divider
++      System* lastSystem = page->systems().back();
++      checkDivider(true, lastSystem, 0.0, true);      // remove
++      checkDivider(false, lastSystem, 0.0, true);     // remove
++
+       if (sList.empty() || MScore::noVerticalStretch || score->layoutMode() == LayoutMode::SYSTEM) {
+             if (score->layoutMode() == LayoutMode::FLOAT) {
+                   qreal y = restHeight * .5;
+                   for (System* system : page->systems())
+                         system->move(QPointF(0.0, y));
+                   }
+-            // remove system dividers
+-            for (System* s : page->systems()) {
+-                  SystemDivider* sd = s->systemDividerLeft();
+-                  if (sd) {
+-                        s->remove(sd);
+-                        delete sd;
+-                        }
+-                  sd = s->systemDividerRight();
+-                  if (sd) {
+-                        s->remove(sd);
+-                        delete sd;
++            // system dividers
++            for (int i = 0; i < gaps; ++i) {
++                  System* s1 = page->systems().at(i);
++                  System* s2 = page->systems().at(i+1);
++                  if (!(s1->vbox() || s2->vbox())) {
++                        qreal yOffset = s1->height() + (s1->distance()-s1->height()) * .5;
++                        checkDivider(true,  s1, yOffset);
++                        checkDivider(false, s1, yOffset);
+                         }
+                   }
+             return;
+@@ -1603,7 +1605,7 @@ static void layoutPage(Page* page, qreal restHeight)
+             s1->rypos() = y;
+             y          += s1->distance();
+-            if (!(s1->vbox() || s2->vbox() || s1->hasFixedDownDistance())) {
++            if (!(s1->vbox() || s2->vbox())) {
+                   qreal yOffset = s1->height() + (s1->distance()-s1->height()) * .5;
+                   checkDivider(true,  s1, yOffset);
+                   checkDivider(false, s1, yOffset);
diff --git a/debian/patches/backports/51-paste-hairpin.diff b/debian/patches/backports/51-paste-hairpin.diff
new file mode 100644 (file)
index 0000000..eabf207
--- /dev/null
@@ -0,0 +1,55 @@
+Origin: vendor, commit:306cf054b2da5463aa9c83f1b14673d9728b3596
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #299768: Hairpin: when copied, all custom settings are lost and the element resets to default
+ Resolves: https://musescore.org/en/node/299768.
+
+--- a/libmscore/chordrest.cpp
++++ b/libmscore/chordrest.cpp
+@@ -617,16 +617,13 @@ Element* ChordRest::drop(EditData& data)
+             case ElementType::HAIRPIN:
+                   {
+-                  const Hairpin* hairpin = toHairpin(e);
+-                  ChordRest* endCR = this;
+-                  if (hairpin->ticks().isNotZero()) {
+-                        const Fraction tick2 = tick() + hairpin->ticks() - Fraction::eps();
+-                        endCR = score()->findCR(tick2, track());
+-                        }
+-                  score()->addHairpin(hairpin->hairpinType(), this, endCR);
+-                  delete e;
++                  Hairpin* hairpin = toHairpin(e);
++                  hairpin->setTick(tick());
++                  hairpin->setTrack(track());
++                  hairpin->setTrack2(track());
++                  score()->undoAddElement(hairpin);
+                   }
+-                  return nullptr;
++                  return e;
+             default:
+                   qDebug("cannot drop %s", e->name());
+diff --git a/libmscore/paste.cpp b/libmscore/paste.cpp
+index edf7dece4..e9af8af82 100644
+--- a/libmscore/paste.cpp
++++ b/libmscore/paste.cpp
+@@ -796,13 +796,13 @@ void Score::pasteSymbols(XmlReader& e, ChordRest* dst)
+                               undoAddElement(d);
+                               }
+                         else if (tag == "HairPin") {
+-                              Hairpin h(this);
+-                              h.setTrack(destTrack);
+-                              h.read(e);
+-                              h.setTrack(destTrack);
+-                              ChordRest* destCR1 = findCR(destTick, destTrack);
+-                              ChordRest* destCR2 = findCR(destTick + h.ticks() - Fraction::eps(), destTrack);
+-                              addHairpin(h.hairpinType(), destCR1, destCR2);
++                              Hairpin* h = new Hairpin(this);
++                              h->setTrack(destTrack);
++                              h->read(e);
++                              h->setTrack(destTrack);
++                              h->setTrack2(destTrack);
++                              h->setTick(destTick);
++                              undoAddElement(h);
+                               }
+                         else {
+                               //
diff --git a/debian/patches/backports/52-ui-enter-whole-note.diff b/debian/patches/backports/52-ui-enter-whole-note.diff
new file mode 100644 (file)
index 0000000..091dddc
--- /dev/null
@@ -0,0 +1,23 @@
+Origin: vendor, commit:37ccd37db6f1d40c9a744d8b341b9ac59f2529da
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #300926: Problem selecting and entering a whole note
+ Resolves: https://musescore.org/en/node/300926.
+ .
+ This allows a full measure rest to be changed into a "normal" rest even
+ if the actual length of the rest is not being changed.
+
+--- a/libmscore/cmd.cpp
++++ b/libmscore/cmd.cpp
+@@ -1248,8 +1248,11 @@ void Score::changeCRlen(ChordRest* cr, const Fraction& dstF, bool fillWithRest)
+             return;
+             }
+       Fraction srcF(cr->ticks());
+-      if (srcF == dstF)
++      if (srcF == dstF) {
++            if (cr->isFullMeasureRest())
++                  undoChangeChordRestLen(cr, dstF);
+             return;
++            }
+       //keep selected element if any
+       Element* selElement = selection().isSingle() ? getSelectedElement() : 0;
diff --git a/debian/patches/backports/53-ui-wtf.diff b/debian/patches/backports/53-ui-wtf.diff
new file mode 100644 (file)
index 0000000..bfd0496
--- /dev/null
@@ -0,0 +1,184 @@
+Origin: vendor, commit:406b7b6bf23de8248f77015b8c38ec6d876c04cd
+Author: J. Edward Sanchez <Spire42@users.noreply.github.com>
+Description: Fix #304466: The “I/O” tab of the “Preferences” dialog should use radio buttons instead of checkboxes
+ Fixed a UI problem with the “I/O” tab of the “Preferences” dialog that
+ caused mutually exclusive options to be presented to the user as
+ checkboxes instead of radio buttons.
+ .
+ The underlying technical reason for this was that the Qt framework does
+ not allow group boxes to have radio buttons. This has been worked
+ around by subclassing the QGroupBox class and rendering the checkboxes
+ to look like radio buttons. This is sufficient for our purposes because
+ the application already overrides the checkboxes' behavior to work like
+ radio buttons.
+
+--- a/mscore/CMakeLists.txt
++++ b/mscore/CMakeLists.txt
+@@ -175,7 +175,7 @@ add_library(mscoreapp STATIC
+       pathlistdialog.h piano.h  pianotools.h
+       openfilelocation.h
+       playpanel.h preferences.h preferenceslistwidget.h prefsdialog.h
+-      recordbutton.h resourceManager.h revision.h ruler.h scoreaccessibility.h
++      radiobuttongroupbox.h recordbutton.h resourceManager.h revision.h ruler.h scoreaccessibility.h
+       scoreBrowser.h scoreInfo.h scorePreview.h scoretab.h scoreview.h searchComboBox.h
+       selectdialog.h selectionwindow.h selectnotedialog.h selinstrument.h
+       seq.h shortcut.h shortcutcapturedialog.h simplebutton.h splitstaff.h stafftextproperties.h
+@@ -198,7 +198,7 @@ add_library(mscoreapp STATIC
+       preferences.cpp measureproperties.cpp
+       seq.cpp textpalette.cpp
+       timedialog.cpp symboldialog.cpp shortcutcapturedialog.cpp
+-      simplebutton.cpp
++      radiobuttongroupbox.cpp simplebutton.cpp
+       openfilelocation.cpp
+       editdrumset.cpp editstaff.cpp
+       timesigproperties.cpp newwizard.cpp transposedialog.cpp
+diff --git a/mscore/prefsdialog.ui b/mscore/prefsdialog.ui
+index 92520c240..d8a518ed6 100644
+--- a/mscore/prefsdialog.ui
++++ b/mscore/prefsdialog.ui
+@@ -2704,7 +2704,7 @@
+       </attribute>
+       <layout class="QVBoxLayout" name="verticalLayout_8">
+        <item>
+-        <widget class="QGroupBox" name="pulseaudioDriver">
++        <widget class="Ms::RadioButtonGroupBox" name="pulseaudioDriver">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+@@ -2729,7 +2729,7 @@
+         </widget>
+        </item>
+        <item>
+-        <widget class="QGroupBox" name="portaudioDriver">
++        <widget class="Ms::RadioButtonGroupBox" name="portaudioDriver">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+@@ -2909,7 +2909,7 @@ Adjusting latency can help synchronize your MIDI hardware with MuseScore's inter
+         </widget>
+        </item>
+        <item>
+-        <widget class="QGroupBox" name="alsaDriver">
++        <widget class="Ms::RadioButtonGroupBox" name="alsaDriver">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+@@ -3125,7 +3125,7 @@ Adjusting latency can help synchronize your MIDI hardware with MuseScore's inter
+         </widget>
+        </item>
+        <item>
+-        <widget class="QGroupBox" name="jackDriver">
++        <widget class="Ms::RadioButtonGroupBox" name="jackDriver">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+@@ -4280,6 +4280,11 @@ Adjusting latency can help synchronize your MIDI hardware with MuseScore's inter
+    <extends>QToolButton</extends>
+    <header>recordbutton.h</header>
+   </customwidget>
++  <customwidget>
++   <class>Ms::RadioButtonGroupBox</class>
++   <extends>QGroupBox</extends>
++   <header>radiobuttongroupbox.h</header>
++  </customwidget>
+  </customwidgets>
+  <tabstops>
+   <tabstop>General</tabstop>
+diff --git a/mscore/radiobuttongroupbox.cpp b/mscore/radiobuttongroupbox.cpp
+new file mode 100644
+index 000000000..1c773f454
+--- /dev/null
++++ b/mscore/radiobuttongroupbox.cpp
+@@ -0,0 +1,49 @@
++//=============================================================================
++//  MuseScore
++//  Music Composition & Notation
++//
++//  Copyright (C) 2020 MuseScore BVBA and others
++//
++//  This program is free software; you can redistribute it and/or modify
++//  it under the terms of the GNU General Public License version 2.
++//
++//  This program is distributed in the hope that it will be useful,
++//  but WITHOUT ANY WARRANTY; without even the implied warranty of
++//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++//  GNU General Public License for more details.
++//
++//  You should have received a copy of the GNU General Public License
++//  along with this program; if not, write to the Free Software
++//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++//=============================================================================
++
++#include "radiobuttongroupbox.h"
++
++namespace Ms {
++
++void RadioButtonGroupBox::paintEvent(QPaintEvent*)
++      {
++      QStylePainter painter(this);
++      QStyleOptionGroupBox styleOption;
++      initStyleOption(&styleOption);
++
++      // Paint the default QGroupBox-style control, which includes an unwanted checkbox that we'll cover up afterwards.
++      painter.drawComplexControl(QStyle::CC_GroupBox, styleOption);
++
++      // Calculate the background color the same way Qt does in QFusionStylePrivate::tabFrameColor().
++      const QColor& buttonColor = styleOption.palette.button().color();
++      QColor bgColor = buttonColor.lighter(100 + std::max(1, (180 - qGray(buttonColor.rgb())) / 6));
++      bgColor.setHsv(bgColor.hue(), 3 * bgColor.saturation() / 4, bgColor.value());
++      bgColor = bgColor.lighter(104);
++
++      // Adjust the style options to use the checkbox's rectangle.
++      styleOption.rect = style()->subControlRect(QStyle::CC_GroupBox, &styleOption, QStyle::SC_GroupBoxCheckBox, this);
++
++      // Cover up the checkbox, making sure to enlarge the rectangle a bit to cover up any anti-aliasing around the edges.
++      painter.fillRect(styleOption.rect.adjusted(-2, -2, 2, 2), bgColor);
++
++      // Paint the radio button.
++      painter.drawPrimitive(QStyle::PE_IndicatorRadioButton, styleOption);
++      }
++
++}
+diff --git a/mscore/radiobuttongroupbox.h b/mscore/radiobuttongroupbox.h
+new file mode 100644
+index 000000000..24b3bfe04
+--- /dev/null
++++ b/mscore/radiobuttongroupbox.h
+@@ -0,0 +1,37 @@
++//=============================================================================
++//  MuseScore
++//  Music Composition & Notation
++//
++//  Copyright (C) 2020 MuseScore BVBA and others
++//
++//  This program is free software; you can redistribute it and/or modify
++//  it under the terms of the GNU General Public License version 2.
++//
++//  This program is distributed in the hope that it will be useful,
++//  but WITHOUT ANY WARRANTY; without even the implied warranty of
++//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++//  GNU General Public License for more details.
++//
++//  You should have received a copy of the GNU General Public License
++//  along with this program; if not, write to the Free Software
++//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++//=============================================================================
++
++#ifndef RADIOBUTTONGROUPBOX_H
++#define RADIOBUTTONGROUPBOX_H
++
++namespace Ms {
++
++class RadioButtonGroupBox : public QGroupBox {
++      Q_OBJECT
++
++      void paintEvent(QPaintEvent* event) override;
++      
++   public:
++      explicit RadioButtonGroupBox(QWidget* parent = nullptr) : QGroupBox(parent) { }
++      explicit RadioButtonGroupBox(const QString& title, QWidget* parent = nullptr) : QGroupBox(title, parent) { }
++      };
++
++}
++
++#endif
diff --git a/debian/patches/backports/54-pitch-Cb.diff b/debian/patches/backports/54-pitch-Cb.diff
new file mode 100644 (file)
index 0000000..5ed2017
--- /dev/null
@@ -0,0 +1,21 @@
+Origin: vendor, commit:58d603b499ba6d89455a98af382f4150fc4efd5b
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: fix #290987: B# and Cb octave change
+ Resolves: https://musescore.org/en/node/290987.
+ .
+ Make sure that B#, B##, Cb, and Cbb describe themselves as being in the
+ correct octave. This is accomplished by calculating the octave based on
+ what the pitch would be if there were no accidental. Thus, there is no
+ need to special-case certain tpcs.
+
+--- a/libmscore/note.cpp
++++ b/libmscore/note.cpp
+@@ -763,7 +763,7 @@ int Note::tpc() const
+ QString Note::tpcUserName(bool explicitAccidental) const
+       {
+       QString pitchName = tpc2name(tpc(), NoteSpellingType::STANDARD, NoteCaseType::AUTO, explicitAccidental);
+-      QString octaveName = QString::number(((epitch() + ottaveCapoFret()) / 12) - 1);
++      QString octaveName = QString::number(((epitch() + ottaveCapoFret() - int(tpc2alter(tpc()))) / 12) - 1);
+       return pitchName + (explicitAccidental ? " " : "") + octaveName;
+       }
diff --git a/debian/patches/backports/55-layout-timesig.diff b/debian/patches/backports/55-layout-timesig.diff
new file mode 100644 (file)
index 0000000..daff645
--- /dev/null
@@ -0,0 +1,82 @@
+Origin: vendor, commit:cd3b3822e17416e3c7e16d72751083a2d362c606
+Author: Tom Cannon <tom-cannon@playcannon.com>
+Description: fix #282246: "other" appearance in time sig props not working.
+ This bug occurs when the user chooses an "other" time signature
+ property. This fix works by changing the implementation to be closer to
+ MuseScore 2. This works by simply only checking to use the numbers if
+ the numerator string is empty. This way, if the denominator string is
+ empty, the numerator string which will have the custom symbol, will line
+ up in the middle. This was how the implementation worked in MuseScore 2.
+
+--- a/libmscore/timesig.cpp
++++ b/libmscore/timesig.cpp
+@@ -299,8 +299,14 @@ void TimeSig::layout()
+             ds.clear();
+             }
+       else {
+-            ns = toTimeSigString(_numeratorString.isEmpty()   ? QString::number(_sig.numerator())   : _numeratorString);
+-            ds = toTimeSigString(_denominatorString.isEmpty() ? QString::number(_sig.denominator()) : _denominatorString);
++            if (_numeratorString.isEmpty()) {
++                  ns = toTimeSigString(_numeratorString.isEmpty()   ? QString::number(_sig.numerator())   : _numeratorString);
++                  ds = toTimeSigString(_denominatorString.isEmpty() ? QString::number(_sig.denominator()) : _denominatorString);
++                  }
++            else {
++                  ns = toTimeSigString(_numeratorString);
++                  ds = toTimeSigString(_denominatorString);
++                  }
+             ScoreFont* font = score()->scoreFont();
+             QSizeF mag(magS() * _scale);
+diff --git a/mscore/timesigproperties.cpp b/mscore/timesigproperties.cpp
+index 0edbff423..3e21a50d3 100644
+--- a/mscore/timesigproperties.cpp
++++ b/mscore/timesigproperties.cpp
+@@ -114,6 +114,10 @@ TimeSigProperties::TimeSigProperties(TimeSig* t, QWidget* parent)
+                         textButton->setChecked(false);
+                         otherButton->setChecked(true);
+                         otherCombo->setCurrentIndex(idx);
++
++                        // set the custom text fields to empty
++                        zText->setText(QString());
++                        nText->setText(QString());
+                         }
+                   }
+             idx++;
+@@ -134,21 +138,12 @@ TimeSigProperties::TimeSigProperties(TimeSig* t, QWidget* parent)
+ void TimeSigProperties::accept()
+       {
+       TimeSigType ts = TimeSigType::NORMAL;
+-      if (textButton->isChecked())
++      if (textButton->isChecked() || otherButton->isChecked())
+             ts = TimeSigType::NORMAL;
+       else if (fourfourButton->isChecked())
+             ts = TimeSigType::FOUR_FOUR;
+       else if (allaBreveButton->isChecked())
+             ts = TimeSigType::ALLA_BREVE;
+-      else if (otherButton->isChecked()) {
+-            // if other symbol, set as normal text...
+-            ts = TimeSigType::NORMAL;
+-            ScoreFont* scoreFont = timesig->score()->scoreFont();
+-            SymId symId = (SymId)( otherCombo->itemData(otherCombo->currentIndex()).toInt() );
+-            // ...and set numerator to font string for symbol and denominator to empty string
+-            timesig->setNumeratorString(scoreFont->toString(symId));
+-            timesig->setDenominatorString(QString());
+-            }
+       Fraction actual(zActual->value(), nActual->value());
+       Fraction nominal(zNominal->value(), nNominal->value());
+@@ -160,6 +155,14 @@ void TimeSigProperties::accept()
+       if (nText->text() != timesig->denominatorString())
+             timesig->setDenominatorString(nText->text());
++      if (otherButton->isChecked()) {
++            ScoreFont* scoreFont = timesig->score()->scoreFont();
++            SymId symId = (SymId)( otherCombo->itemData(otherCombo->currentIndex()).toInt() );
++            // ...and set numerator to font string for symbol and denominator to empty string
++            timesig->setNumeratorString(scoreFont->toString(symId));
++            timesig->setDenominatorString(QString());
++            }
++
+       Groups g = groups->groups();
+       timesig->setGroups(g);
+       QDialog::accept();
diff --git a/debian/patches/backports/56-layout-spatium.diff b/debian/patches/backports/56-layout-spatium.diff
new file mode 100644 (file)
index 0000000..610d210
--- /dev/null
@@ -0,0 +1,25 @@
+Origin: vendor, commit:71771cd4e3962d0c66fda0321aca883a228f51fa
+Author: Howard-C <howardc@pku.edu.cn>
+Description: fix #188061: values in sp unit don't remain unchanged after changing spatium
+ This is simply because `Score::spatiumChanged()` isn't called after
+ applying changes in Page Settings dialogue. It is called for the
+ preview score in the dialogue though, so the preview score has the
+ correct display, but not the main score.
+
+--- a/mscore/pagesettings.cpp
++++ b/mscore/pagesettings.cpp
+@@ -304,7 +304,13 @@ void PageSettings::applyToScore(Score* s)
+       s->undoChangeStyleVal(Sid::pageOddBottomMargin, oddPageBottomMargin->value() * f);
+       s->undoChangeStyleVal(Sid::pageOddLeftMargin, oddPageLeftMargin->value() * f);
+       s->undoChangeStyleVal(Sid::pageTwosided, twosided->isChecked());
+-      s->undoChangeStyleVal(Sid::spatium, spatiumEntry->value() * f1);
++
++      qreal oldSpatium = s->spatium();
++      qreal newSpatium = spatiumEntry->value() * f1;
++      s->undoChangeStyleVal(Sid::spatium, newSpatium);
++      if (oldSpatium != newSpatium)
++            s->spatiumChanged(oldSpatium, newSpatium);
++
+       s->undoChangePageNumberOffset(pageOffsetEntry->value() - 1);
+       s->endCmd();
diff --git a/debian/patches/backports/57-crash-fontname.diff b/debian/patches/backports/57-crash-fontname.diff
new file mode 100644 (file)
index 0000000..b968cf9
--- /dev/null
@@ -0,0 +1,39 @@
+Origin: vendor, commit:443ead70ac77c655095a67bfc0978145fda0c5de
+Author: Matt McClinch <mattmcclinch@gmail.com>
+Description: Fix #303619: MuseScore crashing when entering font name
+ Resolves: https://musescore.org/en/node/303619.
+ .
+ Passing an empty string to QWidget::setStyleSheet() causes the widget's
+ current style sheet to be removed, thus causing the widget's style to be
+ inherited from its parent. This creates a problem in QComboBox::showPopup(),
+ where it becomes possible that a pointer to a QStyle object could used after
+ the object itself has been destroyed. A style sheet of " " can be used to
+ override any previous style sheet without invalidating the current QStyle
+ object.
+
+--- a/mscore/inspector/inspectorBase.cpp
++++ b/mscore/inspector/inspectorBase.cpp
+@@ -351,7 +351,7 @@ void InspectorBase::checkDifferentValues(const InspectorItem& ii)
+                   if (valuesAreDifferent)
+                         break;
+                   }
+-            ii.w->setStyleSheet(valuesAreDifferent ? QString("* { color: %1; } QToolTip { color: palette(tooltiptext); }").arg(c.name()) : "");
++            ii.w->setStyleSheet(valuesAreDifferent ? QString("* { color: %1; } QToolTip { color: palette(tooltiptext); }").arg(c.name()) : " ");
+             }
+       //deal with reset if only one element, or if values are the same
+@@ -366,12 +366,12 @@ void InspectorBase::checkDifferentValues(const InspectorItem& ii)
+                         enableReset = false;
+                         break;
+                   case PropertyFlags::UNSTYLED:
+-                        ii.w->setStyleSheet("");
++                        ii.w->setStyleSheet(" ");
+                         enableReset = true;
+                         break;
+                   case PropertyFlags::NOSTYLE:
+                         enableReset = !isDefault(ii);
+-                        ii.w->setStyleSheet("");
++                        ii.w->setStyleSheet(" ");
+                         break;
+                   }
+             }
diff --git a/debian/patches/backports/58-blank-lines.diff b/debian/patches/backports/58-blank-lines.diff
new file mode 100644 (file)
index 0000000..2d2ad66
--- /dev/null
@@ -0,0 +1,44 @@
+Origin: vendor, commit:0998fffc6613149fbaea55ccc9a1bb59d071e0c3
+Author: DangWang <megistios@gmail.com>
+Description: fix #292652: Empty lines become smaller
+
+--- a/libmscore/textbase.cpp
++++ b/libmscore/textbase.cpp
+@@ -817,6 +817,16 @@ void TextBlock::insert(TextCursor* cursor, const QString& s)
+             }
+       }
++//---------------------------------------------------------
++//   insertEmptyFragment
++//---------------------------------------------------------
++
++void TextBlock::insertEmptyFragment(TextCursor* cursor)
++      {
++      if (_fragments.size() == 0 || _fragments.at(0).text != "")
++            _fragments.insert(0, TextFragment(cursor, ""));
++      }
++
+ //---------------------------------------------------------
+ //   fragment
+ //    inputs:
+@@ -1248,6 +1258,8 @@ void TextBase::createLayout()
+                   else if (c == '\n') {
+                         if (rows() <= cursor.row())
+                               _layout.append(TextBlock());
++                        if(_layout[cursor.row()].fragments().size() == 0)
++                              _layout[cursor.row()].insertEmptyFragment(&cursor); // used to preserve the Font size of the line (font info is held in TextFragments, see PR #5881)
+                         _layout[cursor.row()].setEol(true);
+                         cursor.setRow(cursor.row() + 1);
+                         cursor.setColumn(0);
+diff --git a/libmscore/textbase.h b/libmscore/textbase.h
+index df9c266f7..fdd2795b3 100644
+--- a/libmscore/textbase.h
++++ b/libmscore/textbase.h
+@@ -193,6 +193,7 @@ class TextBlock {
+       QRectF boundingRect(int col1, int col2, const TextBase*) const;
+       int columns() const;
+       void insert(TextCursor*, const QString&);
++      void insertEmptyFragment(TextCursor*);
+       QString remove(int column);
+       QString remove(int start, int n);
+       int column(qreal x, TextBase*) const;
diff --git a/debian/patches/backports/59-blank-lines.diff b/debian/patches/backports/59-blank-lines.diff
new file mode 100644 (file)
index 0000000..f3d7157
--- /dev/null
@@ -0,0 +1,201 @@
+Origin: vendor, commit:25f92afae6374e1d610efd5b43b8effab36cf21b
+Author: SKefalidis <megistios@gmail.com>
+Description: fix #307721: blank lines ignored at top of text elements
+
+--- a/libmscore/textbase.cpp
++++ b/libmscore/textbase.cpp
+@@ -936,11 +936,33 @@ void TextBlock::layout(TextBase* t)
+                         break;
+                   }
+             }
++
+       if (_fragments.empty()) {
+             QFontMetricsF fm = t->fontMetrics();
+             _bbox.setRect(0.0, -fm.ascent(), 1.0, fm.descent());
+             _lineSpacing = fm.lineSpacing();
+             }
++      else if (_fragments.size() == 1 && _fragments.at(0).text.isEmpty()) {
++            auto fi = _fragments.begin();
++            TextFragment& f = *fi;
++            f.pos.setX(x);
++            QFontMetricsF fm(f.font(t), MScore::paintDevice());
++            if (f.format.valign() != VerticalAlignment::AlignNormal) {
++                  qreal voffset = fm.xHeight() / subScriptSize;   // use original height
++                  if (f.format.valign() == VerticalAlignment::AlignSubScript)
++                        voffset *= subScriptOffset;
++                  else
++                        voffset *= superScriptOffset;
++                  f.pos.setY(voffset);
++                  }
++            else {
++                  f.pos.setY(0.0);
++                  }
++
++            QRectF temp(0.0, -fm.ascent(), 1.0, fm.descent());
++            _bbox |= temp;
++            _lineSpacing = qMax(_lineSpacing, fm.lineSpacing());
++            }
+       else {
+             const auto fiLast = --_fragments.end();
+             for (auto fi = _fragments.begin(); fi != _fragments.end(); ++fi) {
+@@ -955,8 +977,9 @@ void TextBlock::layout(TextBase* t)
+                               voffset *= superScriptOffset;
+                         f.pos.setY(voffset);
+                         }
+-                  else
++                  else {
+                         f.pos.setY(0.0);
++                        }
+                   // Optimization: don't calculate character position
+                   // for the next fragment if there is no next fragment
+@@ -982,6 +1005,24 @@ void TextBlock::layout(TextBase* t)
+       _bbox.translate(rx, 0.0);
+       }
++//---------------------------------------------------------
++//   fragmentsWithoutEmpty
++//---------------------------------------------------------
++
++QList<TextFragment>* TextBlock::fragmentsWithoutEmpty()
++      {
++      QList<TextFragment>* list = new QList<TextFragment>();
++      for (auto x :_fragments) {
++            if (x.text.isEmpty()) {
++                  continue;
++                  }
++            else {
++                  list->append(x);
++                  }
++            }
++      return list;
++      }
++
+ //---------------------------------------------------------
+ //   xpos
+ //---------------------------------------------------------
+@@ -1138,8 +1179,9 @@ void TextBlock::insert(TextCursor* cursor, const QString& s)
+ void TextBlock::insertEmptyFragmentIfNeeded(TextCursor* cursor)
+       {
+-      if (_fragments.size() == 0 || _fragments.at(0).text != "")
++      if (_fragments.size() == 0 || _fragments.at(0).text.isEmpty()) {
+             _fragments.insert(0, TextFragment(cursor, ""));
++            }
+       }
+ //---------------------------------------------------------
+@@ -1148,7 +1190,7 @@ void TextBlock::insertEmptyFragmentIfNeeded(TextCursor* cursor)
+ void TextBlock::removeEmptyFragment()
+       {
+-      if (_fragments.size() > 0 && _fragments.at(0).text == "")
++      if (_fragments.size() > 0 && _fragments.at(0).text.isEmpty())
+             _fragments.removeAt(0);
+       }
+@@ -1375,7 +1417,7 @@ void TextFragment::changeFormat(FormatId id, QVariant data)
+ //   split
+ //---------------------------------------------------------
+-TextBlock TextBlock::split(int column)
++TextBlock TextBlock::split(int column, Ms::TextCursor* cursor)
+       {
+       TextBlock tl;
+@@ -1395,6 +1437,8 @@ TextBlock TextBlock::split(int column)
+                               }
+                         for (; i != _fragments.end(); i = _fragments.erase(i))
+                               tl._fragments.append(*i);
++                        if (_fragments.size() == 0)
++                              insertEmptyFragmentIfNeeded(cursor);
+                         return tl;
+                         }
+                   ++idx;
+@@ -1406,6 +1450,8 @@ TextBlock TextBlock::split(int column)
+       TextFragment tf("");
+       if (_fragments.size() > 0)
+             tf.format = _fragments.last().format;
++      else if (_fragments.size() == 0)
++            insertEmptyFragmentIfNeeded(cursor);
+       tl._fragments.append(tf);
+       return tl;
+       }
+@@ -1566,7 +1612,7 @@ static qreal parseNumProperty(const QString& s)
+ void TextBase::createLayout()
+       {
+-      _layout.clear();
++      _layout.clear();  // deletes the text fragments so we lose all formatting information
+       TextCursor cursor(this);
+       cursor.init();
+@@ -1955,8 +2001,9 @@ void TextBase::genText() const
+       for (const TextBlock& block : _layout) {
+             for (const TextFragment& f : block.fragments()) {
+-                  if (f.text.isEmpty())                     // skip empty fragments, not to
+-                        continue;                           // insert extra HTML formatting
++                  // don't skip, empty text fragments hold information for empty lines
++//                  if (f.text.isEmpty())                     // skip empty fragments, not to
++//                        continue;                           // insert extra HTML formatting
+                   const CharFormat& format = f.format;
+                   if (fmt.bold() != format.bold()) {
+                         if (format.bold())
+diff --git a/libmscore/textbase.h b/libmscore/textbase.h
+index de3cd594b..3356fa9dd 100644
+--- a/libmscore/textbase.h
++++ b/libmscore/textbase.h
+@@ -196,6 +196,7 @@ class TextBlock {
+       void layout(TextBase*);
+       const QList<TextFragment>& fragments() const { return _fragments; }
+       QList<TextFragment>& fragments()             { return _fragments; }
++      QList<TextFragment>* fragmentsWithoutEmpty();
+       const QRectF& boundingRect() const           { return _bbox; }
+       QRectF boundingRect(int col1, int col2, const TextBase*) const;
+       int columns() const;
+@@ -205,7 +206,7 @@ class TextBlock {
+       QString remove(int column, TextCursor*);
+       QString remove(int start, int n, TextCursor*);
+       int column(qreal x, TextBase*) const;
+-      TextBlock split(int column);
++      TextBlock split(int column, TextCursor* cursor);
+       qreal xpos(int col, const TextBase*) const;
+       const CharFormat* formatAt(int) const;
+       const TextFragment* fragment(int col) const;
+diff --git a/libmscore/textedit.cpp b/libmscore/textedit.cpp
+index 5f7898c3a..601ebf8e2 100644
+--- a/libmscore/textedit.cpp
++++ b/libmscore/textedit.cpp
+@@ -521,15 +521,21 @@ void SplitJoinText::join(EditData* ed)
+       CharFormat* charFmt = c.format();         // take current format
+       int col             = t->textBlock(line-1).columns();
+       int eol             = t->textBlock(line).eol();
+-      t->textBlock(line-1).fragments().append(t->textBlock(line).fragments());
++      auto fragmentsList = t->textBlock(line).fragmentsWithoutEmpty();
++      if (fragmentsList->size() > 0)
++            t->textBlock(line-1).removeEmptyFragment();
++      t->textBlock(line-1).fragments().append(*fragmentsList);
++      delete fragmentsList;
+       int lines = t->rows();
+       if (line < lines)
+             t->textBlock(line).setEol(eol);
+       t->textBlockList().removeAt(line);
++
+       c.setRow(line-1);
+       c.setColumn(col);
+       c.setFormat(*charFmt);             // restore orig. format at new line
+       c.clearSelection();
++
+       if (ed)
+             *t->cursor(*ed) = c;
+       c.text()->setTextInvalid();
+@@ -543,7 +549,7 @@ void SplitJoinText::split(EditData* ed)
+       t->triggerLayout();
+       CharFormat* charFmt = c.format();         // take current format
+-      t->textBlockList().insert(line + 1, c.curLine().split(c.column()));
++      t->textBlockList().insert(line + 1, c.curLine().split(c.column(), t->cursor(*ed)));
+       c.curLine().setEol(true);
+       c.setRow(line+1);
diff --git a/debian/patches/backports/60-desktopfile.diff b/debian/patches/backports/60-desktopfile.diff
new file mode 100644 (file)
index 0000000..9454388
--- /dev/null
@@ -0,0 +1,12 @@
+Origin: vendor, commit:745038c30d31db55fb0c6c5cbaca69fa6f2dda4a
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: Add a missing semicolon for MIME types in .desktop file for AppImage
+
+--- a/build/Linux+BSD/mscore.desktop.in
++++ b/build/Linux+BSD/mscore.desktop.in
+@@ -15,4 +15,4 @@ Type=Application
+ Categories=Qt;Audio;Sequencer;Midi;AudioVideoEditing;Music;AudioVideo;
+ Keywords=music;notation;composition;composing;arranging;making;sheet music;music notation software;lead sheet;leadsheet;score;full score;scorewriter;MIDI;musicxml;playback;instrument;
+ Keywords[de]=Musik;Noten;Musiknoten;Komposition;Komponieren;Arrangieren;Notenblatt;Notenblätter;Notationsprogramm;Musiknotationsprogramm;Musiknotation;Tabulatur;MIDI;musicxml;Instrument;
+-MimeType=application/x-musescore;application/x-musescore+xml;application/vnd.recordare.musicxml;application/vnd.recordare.musicxml+xml;audio/midi;application/x-bww;application/x-biab;application/x-capella;audio/x-gtp;application/x-musedata;application/x-overture;audio/x-ptb;application/x-sf2;application/x-sf3
++MimeType=application/x-musescore;application/x-musescore+xml;application/vnd.recordare.musicxml;application/vnd.recordare.musicxml+xml;audio/midi;application/x-bww;application/x-biab;application/x-capella;audio/x-gtp;application/x-musedata;application/x-overture;audio/x-ptb;application/x-sf2;application/x-sf3;
diff --git a/debian/patches/backports/61-desktopfile.diff b/debian/patches/backports/61-desktopfile.diff
new file mode 100644 (file)
index 0000000..2dd7449
--- /dev/null
@@ -0,0 +1,14 @@
+Origin: vendor, commit:0c1e286c8dd6ab0a55b031c886de1a2d394b29d2
+Author: Joachim Schmitz <jojo@schmitz-digital.de>
+Description: fix #307593: prevent duplicated icon on Ubuntu
+
+--- a/build/Linux+BSD/mscore.desktop.in
++++ b/build/Linux+BSD/mscore.desktop.in
+@@ -10,6 +10,7 @@ Comment[fr]=Gravure de partitions musicales
+ Exec=mscore@MSCORE_INSTALL_SUFFIX@ %F
+ Icon=mscore@MSCORE_INSTALL_SUFFIX@
+ StartupNotify=true
++StartupWMClass=mscore@MSCORE_INSTALL_SUFFIX@
+ Terminal=false
+ Type=Application
+ Categories=Qt;Audio;Sequencer;Midi;AudioVideoEditing;Music;AudioVideo;
diff --git a/debian/patches/backports/62-measure-count.diff b/debian/patches/backports/62-measure-count.diff
new file mode 100644 (file)
index 0000000..ef905e0
--- /dev/null
@@ -0,0 +1,21 @@
+Origin: vendor, commit:bb057bb87e7d018dc5057b69de96ee7b644357a6
+Author: Niek van den Berg <njvdberg@xs4all.nl>
+Description: Fix #306333 - Various type of edits change the measure count
+ After a modification, a re-layout is triggered starting at the beginning of the
+ system. The measure number of the first measure of the system is taken adding 1
+ to the measure number of the last measure of previous system. However it was not
+ taken into account whether that measure was excluded from measure count.
+
+--- a/libmscore/layout.cpp
++++ b/libmscore/layout.cpp
+@@ -4530,8 +4530,8 @@ void Score::doLayoutRange(const Fraction& st, const Fraction& et)
+                   if (sectionBreak && sectionBreak->startWithMeasureOne())
+                         lc.measureNo = 0;
+                   else
+-                        lc.measureNo = lc.nextMeasure->prevMeasure()->no() + 1; // will be adjusted later with respect
+-                                                                                // to the user-defined offset.
++                        lc.measureNo = lc.nextMeasure->prevMeasure()->no()                     // will be adjusted later with respect
++                                       + (lc.nextMeasure->prevMeasure()->irregular() ? 0 : 1); // to the user-defined offset.
+                   lc.tick      = lc.nextMeasure->tick();
+                   }
+             }
diff --git a/debian/patches/backports/63-minimise-window.diff b/debian/patches/backports/63-minimise-window.diff
new file mode 100644 (file)
index 0000000..a9a949b
--- /dev/null
@@ -0,0 +1,154 @@
+Origin: vendor, commit:6804243e6671fa6a4961d8d2ded408cefddaf1ce
+Author: Dmitri Ovodok <dmitrio95@yandex.ru>
+Description: fix #298820: fix inability to minimize MuseScore on Linux with Gnome-based desktop environments
+ The issue happens if some QML view is opened, e.g. a plugin dock or,
+ more importantly, Palettes panel. Fixes the issue by leaving the code
+ in Mixer and Play Panel forcing a window to take focus only in cases
+ when it might make sense: when opening the corresponding widget and
+ when unminimizing MuseScore if the widget is in floating state (otherwise
+ it won't take focus). This is enough to avoid triggering the issue
+ which happens only if Play Panel or Mixer is docked.
+
+--- a/mscore/mixer/mixer.cpp
++++ b/mscore/mixer/mixer.cpp
+@@ -240,7 +240,7 @@ void Mixer::retranslate(bool firstTime)
+ void Mixer::closeEvent(QCloseEvent* ev)
+       {
+       emit closed(false);
+-      QWidget::closeEvent(ev);
++      QDockWidget::closeEvent(ev);
+       }
+ //---------------------------------------------------------
+@@ -249,11 +249,18 @@ void Mixer::closeEvent(QCloseEvent* ev)
+ void Mixer::showEvent(QShowEvent* e)
+       {
+-      enablePlay->showEvent(e);
+-      QWidget::showEvent(e);
+-      activateWindow();
+-      setFocus();
+-      getAction("toggle-mixer")->setChecked(true);
++      if (e->spontaneous() && !isFloating()) {
++            QDockWidget::showEvent(e);
++            }
++      else {
++            enablePlay->showEvent(e);
++            QDockWidget::showEvent(e);
++            activateWindow();
++            setFocus();
++            }
++
++      if (!e->spontaneous())
++            getAction("toggle-mixer")->setChecked(true);
+       }
+@@ -263,8 +270,9 @@ void Mixer::showEvent(QShowEvent* e)
+ void Mixer::hideEvent(QHideEvent* e)
+       {
+-      QWidget::hideEvent(e);
+-      getAction("toggle-mixer")->setChecked(false);
++      QDockWidget::hideEvent(e);
++      if (!e->spontaneous())
++            getAction("toggle-mixer")->setChecked(false);
+       }
+@@ -276,7 +284,7 @@ bool Mixer::eventFilter(QObject* obj, QEvent* e)
+       {
+       if (enablePlay->eventFilter(obj, e))
+             return true;
+-      return QWidget::eventFilter(obj, e);
++      return QDockWidget::eventFilter(obj, e);
+       }
+ //---------------------------------------------------------
+@@ -288,7 +296,7 @@ void Mixer::keyPressEvent(QKeyEvent* ev) {
+             close();
+             return;
+             }
+-      QWidget::keyPressEvent(ev);
++      QDockWidget::keyPressEvent(ev);
+       }
+ //---------------------------------------------------------
+@@ -297,7 +305,7 @@ void Mixer::keyPressEvent(QKeyEvent* ev) {
+ void Mixer::changeEvent(QEvent *event)
+       {
+-      QWidget::changeEvent(event);
++      QDockWidget::changeEvent(event);
+       if (event->type() == QEvent::LanguageChange)
+             retranslate();
+       }
+diff --git a/mscore/playpanel.cpp b/mscore/playpanel.cpp
+index 980f31d6c..467850a86 100644
+--- a/mscore/playpanel.cpp
++++ b/mscore/playpanel.cpp
+@@ -136,7 +136,7 @@ void PlayPanel::speedChanged()
+ void PlayPanel::closeEvent(QCloseEvent* ev)
+       {
+       emit closed(false);
+-      QWidget::closeEvent(ev);
++      QDockWidget::closeEvent(ev);
+       }
+ //---------------------------------------------------------
+@@ -151,7 +151,7 @@ void PlayPanel::closeEvent(QCloseEvent* ev)
+ void PlayPanel::hideEvent(QHideEvent* ev)
+       {
+       MuseScore::saveGeometry(this);
+-      QWidget::hideEvent(ev);
++      QDockWidget::hideEvent(ev);
+       }
+ //---------------------------------------------------------
+@@ -160,10 +160,15 @@ void PlayPanel::hideEvent(QHideEvent* ev)
+ void PlayPanel::showEvent(QShowEvent* e)
+       {
+-      enablePlay->showEvent(e);
+-      QWidget::showEvent(e);
+-      activateWindow();
+-      setFocus();
++      if (e->spontaneous() && !isFloating()) {
++            QDockWidget::showEvent(e);
++            }
++      else {
++            enablePlay->showEvent(e);
++            QDockWidget::showEvent(e);
++            activateWindow();
++            setFocus();
++            }
+       }
+ //---------------------------------------------------------
+@@ -174,7 +179,7 @@ bool PlayPanel::eventFilter(QObject* obj, QEvent* e)
+       {
+       if (enablePlay->eventFilter(obj, e))
+             return true;
+-      return QWidget::eventFilter(obj, e);
++      return QDockWidget::eventFilter(obj, e);
+       }
+ void PlayPanel::keyPressEvent(QKeyEvent* ev) {
+@@ -182,7 +187,7 @@ void PlayPanel::keyPressEvent(QKeyEvent* ev) {
+             close();
+             return;
+             }
+-      QWidget::keyPressEvent(ev);
++      QDockWidget::keyPressEvent(ev);
+       }
+ //---------------------------------------------------------
+@@ -453,7 +458,7 @@ void PlayPanel::speedSliderReleased(int)
+ void PlayPanel::changeEvent(QEvent *event)
+       {
+-      QWidget::changeEvent(event);
++      QDockWidget::changeEvent(event);
+       if (event->type() == QEvent::LanguageChange)
+             retranslate();
+       }
index 59ccfe1..34cedd2 100644 (file)
@@ -12,11 +12,75 @@ upstream/fix-cli-audio.diff
 upstream/fix-pluginless-build.diff
 upstream/undoable-spacer-drag-length.diff
 upstream/workspace-crash.diff
-upstream/workspace-warning.diff
-upstream/qt514-1.diff
-upstream/boldfonts-1.diff
-upstream/boldfonts-2.diff
-upstream/boldfonts-3.diff
+backports/workspace-warning.diff
+backports/qt514-1.diff
+backports/boldfonts-1.diff
+backports/boldfonts-2.diff
+backports/boldfonts-3.diff
+backports/01-playback-muted.diff
+backports/02-playback-tied.diff
+backports/03-playback-deleted.diff
+backports/04-paste-breath.diff
+backports/05-layout-hairpin-dynamic.diff
+backports/06-layout-volta.diff
+backports/07-layout-clef-change.diff
+backports/08-ui-undo-spacer.diff
+backports/09-layout-1st-system.diff
+backports/10-ui-undo-track.diff
+backports/11-ui-keys-move.diff
+backports/12-instr-change-name.diff
+backports/14-crash-segments.diff
+backports/15-crash-mxml.diff
+backports/16-crash-workspace.diff
+backports/16a-crash-qt514.diff
+backports/17-crash-tremolo.diff
+backports/18-crash-pianoroll.diff
+backports/19-synth-state.diff
+backports/20-tour-loop.diff
+backports/21-synth-signed-char.diff
+backports/22-crash-fermata.diff
+backports/23-hang-startup.diff
+backports/24-element-size.diff
+backports/25-relayout-beams.diff
+backports/26-crash-repeats.diff
+backports/27-font-style.diff
+backports/28-crash-del-stafftypechange.diff
+backports/29-layout-barno-break.diff
+backports/30-playback-fermata.diff
+backports/31-ui-enter-slur.diff
+backports/32-ui-enter-timesig.diff
+backports/33-playback-articulations.diff
+backports/33a-playback-articulations.diff
+backports/34-crash-drag.diff
+backports/35-ui-mixer-reset.diff
+backports/36-plugin-state.diff
+backports/37-crash-parts.diff
+backports/38-ui-units.diff
+backports/39-layout-spacer.diff
+backports/40-crash-del-hbox.diff
+backports/41-crash-triplet.diff
+backports/42-crash-no-score.diff
+backports/43-playback-glitches.diff
+backports/44-playback-repeats.diff
+backports/45-pianoroll-xml.diff
+backports/46-ui-ambitus.diff
+backports/47-ui-ambitus.diff
+backports/48-layout-ottava.diff
+backports/49-pitch-ottava.diff
+backports/50-layout-divider.diff
+backports/51-paste-hairpin.diff
+backports/52-ui-enter-whole-note.diff
+backports/53-ui-wtf.diff
+backports/54-pitch-Cb.diff
+backports/55-layout-timesig.diff
+backports/56-layout-spatium.diff
+backports/57-crash-fontname.diff
+backports/58-blank-lines.diff
+backports/59-blank-lines.diff
+backports/60-desktopfile.diff
+backports/61-desktopfile.diff
+backports/62-measure-count.diff
+backports/63-minimise-window.diff
 experiments/statusline-concert-transposing-pitch.diff
 experiments/element-barbeat.diff
 #experiments/revert-restriking-patch.diff