LCOV - code coverage report
Current view: top level - src/engraving/dom - figuredbass.h Coverage Total Hit
Test: filtered_coverage.info Lines: 93.2 % 44 41
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #ifndef MU_ENGRAVING_FIGUREDBASS_H
      24              : #define MU_ENGRAVING_FIGUREDBASS_H
      25              : 
      26              : #include "textbase.h"
      27              : 
      28              : namespace mu::engraving {
      29              : class Factory;
      30              : class Segment;
      31              : 
      32              : /*---------------------------------------------------------
      33              : NOTE ON ARCHITECTURE
      34              : 
      35              : FiguredBass elements are stored in the annotations of a Segment (like for instance Harmony)
      36              : 
      37              : FiguredBass is rather simple: it contains only _ticks, telling the duration of the element,
      38              : and a list of FiguredBassItem elements which do most of the job. It also maintains a text with the
      39              : normalized (made uniform) version of the text, which is used during editing.
      40              : 
      41              : Normally, a FiguredBass element is assumed to be styled with an internally maintained text style
      42              : (based on the parameters of the general style "Figured Bass") FIGURED_BASS style and it is set
      43              : in this way upon creation and upon layout().
      44              : - - - -
      45              : FiguredBassItem contains the actually f.b. info; it is made of 4 parts (in this order):
      46              : 1) prefix: one of [nothing, doubleflat, flat, natural, sharp, doublesharp, cross]
      47              : 2) digit: one digit from 1 to 9
      48              : 3) suffix: one of [nothing, doubleflat, flat, natural, sharp, doublesharp, cross, backslash, slash]
      49              : 4) contLine: true if the item has a continuation line (whose length is determined by parent's _ticks)
      50              : and 5 parenthesis flags, one for each position before, between and after the four parts above:
      51              : each of them may contain one of [nothing, roundOpen, roundClosed, squaredOpen, squaredClosed].
      52              : 
      53              : There is a number of restrictions, implemented at the end of FiguredBassItem::parse().
      54              : Currently, no attempt is made to ensure that, if multiple parentheses are present, they are consistent
      55              : (matching open and closed parentheses is left to the user).
      56              : 
      57              : If an item cannot be parsed, the whole FiguredBass element is kept as entered, possibly un-styled.
      58              : If all items can be parsed, each item generates a display text from its properties,
      59              : lays it out so that it properly aligns under the chord, draws it at its proper location
      60              : and provides its FiguredBass parent with a normalized text for future editing.
      61              : 
      62              : FiguredBassItem has not use for formats (italics, bold, ...) and it is never edited directly;
      63              : more generally, it is never accessed directly, only via its FiguredBass parent;
      64              : so it is directly derived from EngravingItem and returns INVALID as type.
      65              : 
      66              : FiguredBass might require formatting (discouraged, but might be necessary for very uncommon cases)
      67              : and it is edited (via the normalized text); so it is derived from Text.
      68              : ---------------------------------------------------------*/
      69              : 
      70              : constexpr int FBIDigitNone = -1;
      71              : 
      72              : //---------------------------------------------------------
      73              : //   @@ FiguredBassItem
      74              : ///   One line of a figured bass indication
      75              : //
      76              : //   @P continuationLine   enum (FiguredBassItem.NONE, .SIMPLE, .EXTENDED)  whether item has continuation line or not, and of which type
      77              : //   @P digit              int                              main digit(s) (0 - 9)
      78              : //   @P displayText        string                           text displayed (depends on configured fonts) (read only)
      79              : //   @P normalizedText     string                           conventional textual representation of item properties (= text used during input) (read only)
      80              : //   @P parenthesis1       enum (FiguredBassItem.NONE, .ROUNDOPEN, .ROUNDCLOSED, .SQUAREDOPEN, .SQUAREDCLOSED)  parenthesis before the prefix
      81              : //   @P parenthesis2       enum (FiguredBassItem.NONE, .ROUNDOPEN, .ROUNDCLOSED, .SQUAREDOPEN, .SQUAREDCLOSED)  parenthesis after the prefix / before the digit
      82              : //   @P parenthesis3       enum (FiguredBassItem.NONE, .ROUNDOPEN, .ROUNDCLOSED, .SQUAREDOPEN, .SQUAREDCLOSED)  parenthesis after the digit / before the suffix
      83              : //   @P parenthesis4       enum (FiguredBassItem.NONE, .ROUNDOPEN, .ROUNDCLOSED, .SQUAREDOPEN, .SQUAREDCLOSED)  parenthesis after the suffix / before the cont. line
      84              : //   @P parenthesis5       enum (FiguredBassItem.NONE, .ROUNDOPEN, .ROUNDCLOSED, .SQUAREDOPEN, .SQUAREDCLOSED)  parenthesis after the cont. line
      85              : //   @P prefix             enum (FiguredBassItem.NONE, .DOUBLEFLAT, .FLAT, .NATURAL, .SHARP, .DOUBLESHARP, .PLUS, .BACKSLASH, .SLASH)  accidental before the digit
      86              : //   @P suffix             enum (FiguredBassItem.NONE, .DOUBLEFLAT, .FLAT, .NATURAL, .SHARP, .DOUBLESHARP, .PLUS, .BACKSLASH, .SLASH)  accidental/diacritic after the digit
      87              : //---------------------------------------------------------
      88              : 
      89              : class FiguredBass;
      90              : 
      91              : class FiguredBassItem final : public EngravingItem
      92              : {
      93          252 :     OBJECT_ALLOCATOR(engraving, FiguredBassItem)
      94              :     DECLARE_CLASSOF(ElementType::FIGURED_BASS_ITEM)
      95              : 
      96              : public:
      97              :     enum class Modifier : char {
      98              :         NONE = 0,
      99              :         DOUBLEFLAT,
     100              :         FLAT,
     101              :         NATURAL,
     102              :         SHARP,
     103              :         DOUBLESHARP,
     104              :         CROSS,
     105              :         BACKSLASH,
     106              :         SLASH,
     107              :         NUMOF
     108              :     };
     109              :     enum class Parenthesis : char {
     110              :         NONE = 0,
     111              :         ROUNDOPEN,
     112              :         ROUNDCLOSED,
     113              :         SQUAREDOPEN,
     114              :         SQUAREDCLOSED,
     115              :         NUMOF
     116              :     };
     117              :     enum class ContLine : char {
     118              :         NONE = 0,
     119              :         SIMPLE,                         // cont. line stops at f.b. element end
     120              :         EXTENDED                        // cont. line joins with next element, if possible
     121              :     };
     122              : 
     123              :     enum class Style : char {
     124              :         MODERN = 0,
     125              :         HISTORIC,
     126              :         NUMOF
     127              :     };
     128              :     enum class Combination : char {
     129              :         SIMPLE = 0,
     130              :         CROSSED,
     131              :         BACKSLASHED,
     132              :         SLASHED,
     133              :         NUMOF
     134              :     };
     135              : 
     136              :     ~FiguredBassItem();
     137              : 
     138              :     FiguredBassItem& operator=(const FiguredBassItem&) = delete;
     139              : 
     140              :     // standard re-implemented virtual functions
     141            0 :     FiguredBassItem* clone() const override { return new FiguredBassItem(*this); }
     142              : 
     143              :     bool startsWithParenthesis() const;
     144              : 
     145              :     // specific API
     146           33 :     const FiguredBass* figuredBass() const { return (FiguredBass*)(ownershipParent()); }
     147              :     bool parse(String& text);
     148              : 
     149              :     // getters / setters
     150          211 :     int ord() const { return m_ord; }
     151              : 
     152          322 :     Modifier prefix() const { return m_prefix; }
     153            9 :     void setPrefix(const Modifier& v) { m_prefix = v; }
     154              :     void undoSetPrefix(Modifier pref);
     155              : 
     156          803 :     int digit() const { return m_digit; }
     157          106 :     void setDigit(int val) { m_digit = val; }
     158              :     void undoSetDigit(int digit);
     159              : 
     160          963 :     Modifier suffix() const { return m_suffix; }
     161           13 :     void setSuffix(const Modifier& v) { m_suffix = v; }
     162              :     void undoSetSuffix(Modifier suff);
     163              : 
     164          372 :     ContLine contLine() const { return m_contLine; }
     165           16 :     void setContLine(const ContLine& v) { m_contLine = v; }
     166              :     void undoSetContLine(ContLine val);
     167              : 
     168          225 :     Parenthesis parenth1() const { return m_parenth[0]; }
     169          229 :     Parenthesis parenth2() const { return m_parenth[1]; }
     170          251 :     Parenthesis parenth3() const { return m_parenth[2]; }
     171          225 :     Parenthesis parenth4() const { return m_parenth[3]; }
     172           14 :     Parenthesis parenth5() const { return m_parenth[4]; }
     173              : 
     174           49 :     void setParenth1(Parenthesis v) { m_parenth[0] = v; }
     175           51 :     void setParenth2(Parenthesis v) { m_parenth[1] = v; }
     176           51 :     void setParenth3(Parenthesis v) { m_parenth[2] = v; }
     177           49 :     void setParenth4(Parenthesis v) { m_parenth[3] = v; }
     178           49 :     void setParenth5(Parenthesis v) { m_parenth[4] = v; }
     179              : 
     180              :     void undoSetParenth1(Parenthesis par);
     181              :     void undoSetParenth2(Parenthesis par);
     182              :     void undoSetParenth3(Parenthesis par);
     183              :     void undoSetParenth4(Parenthesis par);
     184              :     void undoSetParenth5(Parenthesis par);
     185              :     String normalizedText() const;
     186              : 
     187              :     PropertyValue getProperty(Pid propertyId) const override;
     188              :     bool setProperty(Pid propertyId, const PropertyValue&) override;
     189              :     PropertyValue propertyDefault(Pid) const override;
     190              : 
     191              :     struct LayoutData : public EngravingItem::LayoutData {
     192              :         String displayText;                       // the constructed display text (read-only)
     193              :         double textWidth = 0.0;                   // the text width (in raster units)
     194              :     };
     195          339 :     DECLARE_LAYOUTDATA_METHODS(FiguredBassItem)
     196              : 
     197              : private:
     198              : 
     199              :     friend class FiguredBass;
     200              : 
     201              :     FiguredBassItem(FiguredBass* parent, int line = 0);
     202              :     FiguredBassItem(const FiguredBassItem&);
     203              : 
     204              :     // part parsing
     205              :     int parseDigit(String& str);
     206              :     int parseParenthesis(String& str, int parenthIdx);
     207              :     int parsePrefixSuffix(String& str, bool bPrefix);
     208              : 
     209              :     void regenerateDisplayText();
     210              : 
     211              :     static const Char NORM_PARENTH_TO_CHAR[int(Parenthesis::NUMOF)];
     212              : 
     213              :     int m_ord = 0;                              // the line ordinal of this element in the FB stack
     214              :                                                 // the parts making a FiguredBassItem up
     215              :     Modifier m_prefix = Modifier::NONE;         // the accidental coming before the body
     216              :     int m_digit = 0;                            // the main digit (if present)
     217              :     Modifier m_suffix = Modifier::NONE;         // the accidental coming after the body
     218              :     ContLine m_contLine = ContLine::NONE;       // whether the item has continuation line or not
     219              :     Parenthesis m_parenth[5];                   // each of the parenthesis: before, between and after parts
     220              : };
     221              : 
     222              : //---------------------------------------------------------
     223              : //   FiguredBassFont
     224              : //---------------------------------------------------------
     225              : 
     226              : struct FiguredBassFont {
     227              :     String family;
     228              :     String displayName;
     229              :     double defPitch = 0.0;
     230              :     double defLineHeight = 0.0;
     231              :     Char displayAccidental[int(FiguredBassItem::Modifier::NUMOF)];
     232              :     Char displayParenthesis[int(FiguredBassItem::Parenthesis::NUMOF)];
     233              :     Char displayDigit[int(FiguredBassItem::Style::NUMOF)][10][int(FiguredBassItem::Combination::NUMOF)];
     234              : 
     235              :     bool read(XmlReader&);
     236              : };
     237              : 
     238              : //---------------------------------------------------------
     239              : //   @@ FiguredBass
     240              : ///    A complete figured bass indication
     241              : //
     242              : //   @P onNote  bool  whether it is placed on a note beginning or between notes (read only)
     243              : //   @P ticks   int   duration in ticks
     244              : //---------------------------------------------------------
     245              : 
     246              : class FiguredBass final : public TextBase
     247              : {
     248          196 :     OBJECT_ALLOCATOR(engraving, FiguredBass)
     249         1866 :     DECLARE_CLASSOF(ElementType::FIGURED_BASS)
     250              : 
     251              : public:
     252              : 
     253              :     static constexpr double FB_CONTLINE_HEIGHT            = 0.875;         // the % of font EM to raise the cont. line at
     254              :                                                                            // (0 = top of font; 1 = bottom of font)
     255              :     static constexpr Spatium FB_CONTLINE_LEFT_PADDING      = 0.1875_sp;        // (3/16sp) the blank space at the left of a cont. line (in sp)
     256              :     static constexpr Spatium FB_CONTLINE_OVERLAP           = 0.125_sp;         // (1/8sp)  the overlap of an extended cont. line (in sp)
     257              :     static constexpr Spatium FB_CONTLINE_THICKNESS         = 0.09375_sp;       // (3/32sp) the thickness of a cont. line (in sp)
     258              : 
     259              :     ~FiguredBass();
     260              : 
     261              :     // a convenience static function to create/retrieve a new FiguredBass into/from its intended parent
     262              :     static FiguredBass* addFiguredBassToSegment(Segment* seg, track_idx_t track, const Fraction& extTicks, bool* pNew);
     263              : 
     264              :     // static functions for font config files
     265              :     static bool readConfigFile(const String& fileName);
     266              :     static std::vector<String> fontNames();
     267              :     static bool fontData(int nIdx, String* pFamily, String* pDisplayName, double* pSize, double* pLineHeight);
     268              : 
     269              :     // standard re-implemented virtual functions
     270            0 :     FiguredBass* clone() const override { return new FiguredBass(*this); }
     271              : 
     272          128 :     FiguredBassItem* createItem(int line) { return new FiguredBassItem(this, line); }
     273              : 
     274              :     void setSelected(bool f) override;
     275              :     void setVisible(bool f) override;
     276              :     void startEdit(EditData& ed) override;
     277              :     bool isEditAllowed(EditData&) const override;
     278              :     void endEdit(EditData&) override;
     279              :     void regenerateText();
     280              : 
     281           12 :     bool onNote() const { return m_onNote; }
     282          119 :     void setOnNote(bool val) { m_onNote = val; }
     283          433 :     Segment* segment() const { return (Segment*)(ownershipParent()); }
     284          568 :     const Fraction& ticks() const { return m_ticks; }
     285          193 :     void setTicks(const Fraction& v) { m_ticks = v; }
     286              : 
     287              :     double additionalContLineX(double pagePosY) const;  // returns the X coord (in page coord) of cont. line at pagePosY, if any
     288              :     FiguredBass* nextFiguredBass() const;                         // returns next *adjacent* f.b. item, if any
     289              : 
     290              :     PropertyValue getProperty(Pid propertyId) const override;
     291              :     bool setProperty(Pid propertyId, const PropertyValue&) override;
     292              :     PropertyValue propertyDefault(Pid) const override;
     293              : 
     294          162 :     bool positionRelativeToNoteheadRest() const override { return true; }
     295              : 
     296            8 :     size_t itemsCount() const { return m_items.size(); }
     297          128 :     void appendItem(FiguredBassItem* item) { m_items.push_back(item); }
     298          467 :     const std::vector<FiguredBassItem*>& items() const { return m_items; }
     299              :     void clearItems();
     300              :     void addItemToLinked(FiguredBassItem* item);
     301              : 
     302              :     // the array of configured fonts
     303              :     static const std::vector<FiguredBassFont>& FBFonts();
     304              : 
     305              :     bool parenthesesMode() const;       // read / write MusicXML support
     306              : 
     307              :     struct LayoutData : public TextBase::LayoutData {
     308              :         std::vector<double> lineLengths;               // lengths of duration indicator lines (in raster units)
     309              :         double printedLineLength = 0.0;                // the length of lines actually printed (i.e. continuation lines)
     310              : 
     311           33 :         double lineLength(size_t idx) const
     312              :         {
     313           33 :             if (idx < lineLengths.size()) {
     314           33 :                 return lineLengths.at(idx);
     315              :             }
     316            0 :             return 0.0;
     317              :         }
     318              :     };
     319          289 :     DECLARE_LAYOUTDATA_METHODS(FiguredBass)
     320              : 
     321              : private:
     322              : 
     323              :     friend class Factory;
     324              :     FiguredBass(DummyParentOr<Segment> parent);
     325              :     FiguredBass(const FiguredBass&);
     326              : 
     327              :     Sid getPropertyStyle(Pid) const override;
     328              : 
     329              :     std::vector<FiguredBassItem*> m_items;       // the individual lines of the F.B.
     330              : 
     331              :     bool m_onNote = true;                        // true if this element is on a staff note | false if it is between notes
     332              :     Fraction m_ticks;                            // the duration (used for cont. lines and for multiple F.B.
     333              :                                                  // under the same note)
     334              : };
     335              : } // namespace mu::engraving
     336              : 
     337              : #ifndef NO_QT_SUPPORT
     338           16 : Q_DECLARE_METATYPE(mu::engraving::FiguredBassItem::Modifier)
     339           16 : Q_DECLARE_METATYPE(mu::engraving::FiguredBassItem::Parenthesis)
     340           16 : Q_DECLARE_METATYPE(mu::engraving::FiguredBassItem::ContLine)
     341              : #endif
     342              : 
     343              : #endif
        

Generated by: LCOV version 2.0-1