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 : #include "expression.h"
23 :
24 : #include "chord.h"
25 : #include "dynamic.h"
26 : #include "dynamichairpingroup.h"
27 : #include "note.h"
28 : #include "segment.h"
29 : #include "score.h"
30 : #include "stafftext.h"
31 :
32 : namespace mu::engraving {
33 : static const ElementStyle expressionStyle {
34 : { Sid::expressionPlacement, Pid::PLACEMENT },
35 : { Sid::expressionMinDistance, Pid::MIN_DISTANCE },
36 : { Sid::snapToDynamics, Pid::SNAP_TO_DYNAMICS },
37 : };
38 :
39 33 : Expression::Expression(DummyParentOr<Segment> parent)
40 33 : : TextBase(ElementType::EXPRESSION, parent, TextStyleType::EXPRESSION, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
41 : {
42 33 : initElementStyle(&expressionStyle);
43 33 : }
44 :
45 1 : Expression::Expression(const Expression& expression)
46 1 : : TextBase(expression)
47 : {
48 1 : _snapToDynamics = expression._snapToDynamics;
49 1 : }
50 :
51 0 : bool Expression::isEditAllowed(EditData& ed) const
52 : {
53 0 : bool ctrlPressed = ed.modifiers & ControlModifier;
54 0 : bool shiftPressed = ed.modifiers & ShiftModifier;
55 0 : bool altPressed = ed.modifiers & AltModifier;
56 0 : if (altPressed && !ctrlPressed && !shiftPressed && (ed.key == Key_Left || ed.key == Key_Right)) {
57 0 : return false;
58 : }
59 :
60 0 : return TextBase::isEditAllowed(ed);
61 : }
62 :
63 272 : PropertyValue Expression::propertyDefault(Pid id) const
64 : {
65 272 : switch (id) {
66 13 : case Pid::TEXT_STYLE:
67 13 : return TextStyleType::EXPRESSION;
68 259 : default:
69 259 : return TextBase::propertyDefault(id);
70 : }
71 : }
72 :
73 2 : double Expression::computeDynamicExpressionDistance(const Dynamic* snappedDyn) const
74 : {
75 2 : IF_ASSERT_FAILED(snappedDyn) {
76 0 : return 0.0;
77 : }
78 : // We are essentially faking the kerning behaviour of dynamic VS expression text
79 : // There's no other way to do this because the dynamic is a different font.
80 2 : String dynamicTextString = snappedDyn->xmlText();
81 2 : String f = String::fromStdString("<sym>dynamicForte</sym>");
82 2 : double distance = (dynamicTextString.endsWith(f) ? 0.2 : 0.5) * spatium();
83 2 : distance *= 0.5 * (snappedDyn->symbolScale() + (size() / 10));
84 2 : return distance;
85 2 : }
86 :
87 0 : std::unique_ptr<ElementGroup> Expression::getDragGroup(std::function<bool(const EngravingItem*)> isDragged)
88 : {
89 0 : if (auto g = DynamicExpressionDragGroup::detectFor(this, isDragged)) {
90 0 : return g;
91 0 : }
92 0 : return TextBase::getDragGroup(isDragged);
93 : }
94 :
95 0 : bool Expression::acceptDrop(EditData& ed) const
96 : {
97 0 : return ed.dropElement->isDynamic() || TextBase::acceptDrop(ed);
98 : }
99 :
100 0 : EngravingItem* Expression::drop(Transaction& tx, EditData& ed)
101 : {
102 0 : EngravingItem* item = ed.dropElement;
103 0 : if (item->isDynamic()) {
104 0 : Dynamic* snappedDyn = snappedDynamic();
105 0 : if (snappedDyn) {
106 0 : return snappedDyn->drop(tx, ed);
107 : }
108 :
109 0 : item->setTrack(track());
110 0 : item->setOwnershipParent(segment());
111 0 : score()->undoAddElement(item);
112 0 : item->undoChangeProperty(Pid::PLACEMENT, placement(), PropertyFlags::UNSTYLED);
113 0 : return item;
114 : }
115 :
116 0 : return TextBase::drop(tx, ed);
117 : }
118 :
119 364 : PropertyValue Expression::getProperty(Pid propertyId) const
120 : {
121 364 : switch (propertyId) {
122 6 : case Pid::SNAP_TO_DYNAMICS:
123 6 : return _snapToDynamics;
124 358 : default:
125 358 : return TextBase::getProperty(propertyId);
126 : }
127 : }
128 :
129 685 : bool Expression::setProperty(Pid propertyId, const PropertyValue& v)
130 : {
131 685 : switch (propertyId) {
132 35 : case Pid::SNAP_TO_DYNAMICS:
133 35 : if (_snapToDynamics == false && v.toBool() == true) {
134 33 : resetProperty(Pid::OFFSET);
135 : }
136 35 : setSnapToDynamics(v.toBool());
137 35 : break;
138 650 : default:
139 650 : if (!TextBase::setProperty(propertyId, v)) {
140 0 : return false;
141 : }
142 650 : break;
143 : }
144 685 : triggerLayout();
145 685 : return true;
146 : }
147 :
148 24 : void Expression::mapPropertiesFromOldExpressions(StaffText* staffText)
149 : {
150 24 : if (staffText->minDistance() != propertyDefault(Pid::MIN_DISTANCE).value<Spatium>()) {
151 0 : setMinDistance(staffText->minDistance());
152 0 : setPropertyFlags(Pid::MIN_DISTANCE, PropertyFlags::UNSTYLED);
153 : }
154 24 : if (staffText->placement() != propertyDefault(Pid::PLACEMENT).value<PlacementV>()) {
155 22 : setPlacement(staffText->placement());
156 22 : setPropertyFlags(Pid::PLACEMENT, PropertyFlags::UNSTYLED);
157 : }
158 24 : if (!staffText->offset().isNull()) {
159 2 : setOffset(staffText->offset());
160 2 : setSnapToDynamics(false);
161 2 : setPropertyFlags(Pid::SNAP_TO_DYNAMICS, PropertyFlags::UNSTYLED);
162 : }
163 24 : if (staffText->frameType() != FrameType::NO_FRAME) {
164 0 : setFrameType(staffText->frameType());
165 0 : setFrameWidth(staffText->frameWidth());
166 0 : setFrameColor(staffText->frameColor());
167 0 : setBgColor(staffText->bgColor());
168 0 : setFrameRound(staffText->frameRound());
169 : }
170 24 : }
171 :
172 0 : Dynamic* Expression::snappedDynamic() const
173 : {
174 0 : EngravingItem* item = ldata()->itemSnappedBefore();
175 0 : return item && item->isDynamic() ? toDynamic(item) : nullptr;
176 : }
177 :
178 0 : void Expression::reset()
179 : {
180 0 : undoResetProperty(Pid::DIRECTION);
181 0 : undoResetProperty(Pid::CENTER_BETWEEN_STAVES);
182 0 : TextBase::reset();
183 0 : Dynamic* snappedDyn = snappedDynamic();
184 0 : if (snappedDyn && !snappedDyn->offset().isNull()) {
185 0 : snappedDyn->reset();
186 : }
187 0 : }
188 : } // namespace mu::engraving
|