MuseScore/src/engraving/libmscore/segment.h

352 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 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 3 as
* published by the Free Software Foundation.
*
* 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __SEGMENT_H__
#define __SEGMENT_H__
#include "engravingitem.h"
#include "shape.h"
#include "mscore.h"
namespace mu::engraving {
class Factory;
}
namespace Ms {
class Measure;
class Segment;
class ChordRest;
class Spanner;
class System;
//------------------------------------------------------------------------
// @@ Segment
// A segment holds all vertical aligned staff elements.
// Segments are typed and contain only Elements of the same type.
//
// All Elements in a segment start at the same tick. The Segment can store one EngravingItem for
// each voice in each staff in the score.
// Some elements (Clef, KeySig, TimeSig etc.) are assumed to always have voice zero
// and can be found in _elist[staffIdx * VOICES];
// Segments are children of Measures and store Clefs, KeySigs, TimeSigs,
// BarLines and ChordRests.
//
// @P annotations array[EngravingItem] the list of annotations (read only)
// @P next Segment the next segment in the whole score; null at last score segment (read-only)
// @P nextInMeasure Segment the next segment in measure; null at last measure segment (read-only)
// @P prev Segment the previous segment in the whole score; null at first score segment (read-only)
// @P prevInMeasure Segment the previous segment in measure; null at first measure segment (read-only)
// @P segmentType enum (Segment.All, .Ambitus, .BarLine, .Breath, .ChordRest, .Clef, .EndBarLine, .Invalid, .KeySig, .KeySigAnnounce, .StartRepeatBarLine, .TimeSig, .TimeSigAnnounce)
// @P tick int midi tick position (read only)
//------------------------------------------------------------------------
class Segment final : public EngravingItem
{
SegmentType _segmentType { SegmentType::Invalid };
Fraction _tick; // { Fraction(0, 1) };
Fraction _ticks; // { Fraction(0, 1) };
Spatium _extraLeadingSpace;
qreal _stretch;
Segment* _next = nullptr; // linked list of segments inside a measure
Segment* _prev = nullptr;
std::vector<EngravingItem*> _annotations;
std::vector<EngravingItem*> _elist; // EngravingItem storage, size = staves * VOICES.
std::vector<Shape> _shapes; // size = staves
std::vector<qreal> _dotPosX; // size = staves
qreal m_spacing{ 0 };
friend class mu::engraving::Factory;
Segment(Measure* m = 0);
Segment(Measure*, SegmentType, const Fraction&);
Segment(const Segment&);
void init();
void checkEmpty() const;
void checkElement(EngravingItem*, track_idx_t track);
void setEmpty(bool val) const { setFlag(ElementFlag::EMPTY, val); }
protected:
EngravingItem* getElement(staff_idx_t staff); //??
public:
~Segment();
void setParent(Measure* parent);
// Score Tree functions
EngravingObject* scanParent() const override;
EngravingObjectList scanChildren() const override;
Segment* clone() const override { return new Segment(*this); }
void setScore(Score*) override;
Segment* next() const { return _next; }
Segment* next(SegmentType) const;
Segment* nextActive() const;
Segment* nextEnabled() const;
Segment* nextInStaff(staff_idx_t staffIdx, SegmentType t = SegmentType::ChordRest) const;
void setNext(Segment* e) { _next = e; }
Segment* prev() const { return _prev; }
Segment* prev(SegmentType) const;
Segment* prevActive() const;
Segment* prevEnabled() const;
void setPrev(Segment* e) { _prev = e; }
// dont stop at measure boundary:
Segment* next1() const;
Segment* next1enabled() const;
Segment* next1MM() const;
Segment* next1MMenabled() const;
Segment* next1(SegmentType) const;
Segment* next1MM(SegmentType) const;
Segment* prev1() const;
Segment* prev1enabled() const;
Segment* prev1MM() const;
Segment* prev1MMenabled() const;
Segment* prev1(SegmentType) const;
Segment* prev1MM(SegmentType) const;
Segment* nextCR(track_idx_t track = mu::nidx, bool sameStaff = false) const;
ChordRest* nextChordRest(track_idx_t track, bool backwards = false) const;
EngravingItem* element(track_idx_t track) const;
// a variant of the above function, specifically designed to be called from QML
//@ returns the element at track 'track' (null if none)
Ms::EngravingItem* elementAt(track_idx_t track) const;
const std::vector<EngravingItem*>& elist() const { return _elist; }
std::vector<EngravingItem*>& elist() { return _elist; }
void removeElement(track_idx_t track);
void setElement(track_idx_t track, EngravingItem* el);
void scanElements(void* data, void (* func)(void*, EngravingItem*), bool all=true) override;
Measure* measure() const { return toMeasure(explicitParent()); }
System* system() const { return toSystem(explicitParent()->explicitParent()); }
qreal x() const override { return ipos().x(); }
void setX(qreal v) { rxpos() = v; }
mu::RectF contentRect() const;
void insertStaff(staff_idx_t staff);
void removeStaff(staff_idx_t staff);
void add(EngravingItem*) override;
void remove(EngravingItem*) override;
void swapElements(track_idx_t i1, track_idx_t i2);
void sortStaves(std::vector<staff_idx_t>& dst);
const char* subTypeName() const;
static const char* subTypeName(SegmentType);
static SegmentType segmentType(ElementType type);
SegmentType segmentType() const { return _segmentType; }
void setSegmentType(SegmentType t);
bool empty() const { return flag(ElementFlag::EMPTY); }
bool written() const { return flag(ElementFlag::WRITTEN); }
void setWritten(bool val) const { setFlag(ElementFlag::WRITTEN, val); }
void fixStaffIdx();
qreal stretch() const { return _stretch; }
void setStretch(qreal v) { _stretch = v; }
Fraction rtick() const override { return _tick; }
void setRtick(const Fraction& v) { Q_ASSERT(v >= Fraction(0, 1)); _tick = v; }
Fraction tick() const override;
Fraction ticks() const { return _ticks; }
void setTicks(const Fraction& v) { _ticks = v; }
qreal widthInStaff(staff_idx_t staffIdx, SegmentType t = SegmentType::ChordRest) const;
Fraction ticksInStaff(staff_idx_t staffIdx) const;
bool splitsTuplet() const;
const std::vector<EngravingItem*>& annotations() const { return _annotations; }
void clearAnnotations();
void removeAnnotation(EngravingItem* e);
bool hasAnnotationOrElement(ElementType type, track_idx_t minTrack, track_idx_t maxTrack) const;
EngravingItem* findAnnotation(ElementType type, track_idx_t minTrack, track_idx_t maxTrack);
std::vector<EngravingItem*> findAnnotations(ElementType type, track_idx_t minTrack, track_idx_t maxTrack);
bool hasElements() const;
bool hasElements(track_idx_t minTrack, track_idx_t maxTrack) const;
bool allElementsInvisible() const;
qreal dotPosX(staff_idx_t staffIdx) const { return _dotPosX[staffIdx]; }
void setDotPosX(staff_idx_t staffIdx, qreal val) { _dotPosX[staffIdx] = val; }
Spatium extraLeadingSpace() const { return _extraLeadingSpace; }
void setExtraLeadingSpace(Spatium v) { _extraLeadingSpace = v; }
void write(XmlWriter&) const override;
void read(XmlReader&) override;
mu::engraving::PropertyValue getProperty(Pid propertyId) const override;
bool setProperty(Pid propertyId, const mu::engraving::PropertyValue&) override;
mu::engraving::PropertyValue propertyDefault(Pid) const override;
bool operator<(const Segment&) const;
bool operator>(const Segment&) const;
virtual QString accessibleExtraInfo() const override;
EngravingItem* firstInNextSegments(staff_idx_t activeStaff); //<
EngravingItem* lastInPrevSegments(staff_idx_t activeStaff); //<
EngravingItem* firstElement(staff_idx_t staff); //< These methods are used for navigation
EngravingItem* lastElement(staff_idx_t staff); //< for next-element and prev-element
EngravingItem* firstElementOfSegment(Segment* s, staff_idx_t activeStaff);
EngravingItem* nextElementOfSegment(Segment* s, EngravingItem* e, staff_idx_t activeStaff);
EngravingItem* prevElementOfSegment(Segment* s, EngravingItem* e, staff_idx_t activeStaff);
EngravingItem* lastElementOfSegment(Segment* s, staff_idx_t activeStaff);
EngravingItem* nextAnnotation(EngravingItem* e);
EngravingItem* prevAnnotation(EngravingItem* e);
EngravingItem* firstAnnotation(Segment* s, staff_idx_t activeStaff);
EngravingItem* lastAnnotation(Segment* s, staff_idx_t activeStaff);
Spanner* firstSpanner(staff_idx_t activeStaff);
Spanner* lastSpanner(staff_idx_t activeStaff);
bool notChordRestType(Segment* s);
using EngravingItem::nextElement;
EngravingItem* nextElement(staff_idx_t activeStaff);
using EngravingItem::prevElement;
EngravingItem* prevElement(staff_idx_t activeStaff);
std::vector<Shape> shapes() { return _shapes; }
const std::vector<Shape>& shapes() const { return _shapes; }
const Shape& staffShape(staff_idx_t staffIdx) const { return _shapes[staffIdx]; }
Shape& staffShape(staff_idx_t staffIdx) { return _shapes[staffIdx]; }
void createShapes();
void createShape(staff_idx_t staffIdx);
qreal minRight() const;
qreal minLeft(const Shape&) const;
qreal minLeft() const;
qreal minHorizontalDistance(Segment*, bool isSystemGap) const;
qreal minHorizontalCollidingDistance(Segment* ns) const;
qreal elementsTopOffsetFromSkyline(staff_idx_t staffIndex) const;
qreal elementsBottomOffsetFromSkyline(staff_idx_t staffIndex) const;
/*! \brief callulate width of segment and additional spacing of segment depends on duration of segment
* \return pair of {spacing, width}
*/
std::pair<qreal, qreal> computeCellWidth(const std::vector<int>& visibleParts) const;
/*! \brief get among all ChordRests of segment the ChordRest with minimum ticks,
* take into account visibleParts
*/
static ChordRest* ChordRestWithMinDuration(const Segment* seg, const std::vector<int>& visibleParts);
//! spacing is additional width of segment, for example accidental needs this spacing to avoid overlapping
void setSpacing(qreal);
qreal spacing() const;
// some helper function
ChordRest* cr(track_idx_t track) const { return toChordRest(_elist[track]); }
bool isType(const SegmentType t) const { return int(_segmentType) & int(t); }
bool isBeginBarLineType() const { return _segmentType == SegmentType::BeginBarLine; }
bool isClefType() const { return _segmentType == SegmentType::Clef; }
bool isHeaderClefType() const { return _segmentType == SegmentType::HeaderClef; }
bool isKeySigType() const { return _segmentType == SegmentType::KeySig; }
bool isAmbitusType() const { return _segmentType == SegmentType::Ambitus; }
bool isTimeSigType() const { return _segmentType == SegmentType::TimeSig; }
bool isStartRepeatBarLineType() const { return _segmentType == SegmentType::StartRepeatBarLine; }
bool isBarLineType() const { return _segmentType == SegmentType::BarLine; }
bool isBreathType() const { return _segmentType == SegmentType::Breath; }
bool isChordRestType() const { return _segmentType == SegmentType::ChordRest; }
bool isEndBarLineType() const { return _segmentType == SegmentType::EndBarLine; }
bool isKeySigAnnounceType() const { return _segmentType == SegmentType::KeySigAnnounce; }
bool isTimeSigAnnounceType() const { return _segmentType == SegmentType::TimeSigAnnounce; }
bool isMMRestSegment() const;
Fraction shortestChordRest() const;
static constexpr SegmentType durationSegmentsMask = SegmentType::ChordRest; // segment types which may have non-zero tick length
};
//---------------------------------------------------------
// nextActive
//---------------------------------------------------------
inline Segment* Segment::nextActive() const
{
Segment* ns = next();
while (ns && !(ns->enabled() && ns->visible())) {
ns = ns->next();
}
return ns;
}
//---------------------------------------------------------
// nextEnabled
//---------------------------------------------------------
inline Segment* Segment::nextEnabled() const
{
Segment* ns = next();
while (ns && !ns->enabled()) {
ns = ns->next();
}
return ns;
}
//---------------------------------------------------------
// prevActive
//---------------------------------------------------------
inline Segment* Segment::prevActive() const
{
Segment* ps = prev();
while (ps && !(ps->enabled() && ps->visible())) {
ps = ps->prev();
}
return ps;
}
//---------------------------------------------------------
// prevEnabled
//---------------------------------------------------------
inline Segment* Segment::prevEnabled() const
{
Segment* ps = prev();
while (ps && !ps->enabled()) {
ps = ps->prev();
}
return ps;
}
} // namespace Ms
Q_DECLARE_METATYPE(Ms::SegmentType);
#endif