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
|