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 : #include "selection.h"
24 :
25 : #include "engraving/editing/transaction/undostack.h"
26 :
27 : // api
28 : #include "score.h"
29 :
30 : using namespace mu::engraving::apiv1;
31 :
32 : //---------------------------------------------------------
33 : // QmlPlayEventsListAccess::append
34 : //---------------------------------------------------------
35 :
36 0 : Selection* mu::engraving::apiv1::selectionWrap(mu::engraving::Selection* selection)
37 : {
38 0 : Selection* w = new Selection(selection);
39 : // All wrapper objects should belong to JavaScript code.
40 0 : QQmlEngine::setObjectOwnership(w, QQmlEngine::JavaScriptOwnership);
41 0 : return w;
42 : }
43 :
44 : //---------------------------------------------------------
45 : // Selection::checkSelectionIsNotLocked
46 : //---------------------------------------------------------
47 :
48 0 : bool Selection::checkSelectionIsNotLocked() const
49 : {
50 0 : if (m_selection->isLocked()) {
51 0 : LOGW("Cannot change selection: %s", qPrintable(m_selection->lockReason()));
52 0 : return false;
53 : }
54 0 : return true;
55 : }
56 :
57 : //---------------------------------------------------------
58 : // Selection::select
59 : /// Selects the given element. At this point only a
60 : /// limited number of element types is supported, like
61 : /// notes, rests and most of text elements.
62 : /// \param e element to select
63 : /// \param add if \p true, appends an element to already
64 : /// existing selection. If \p false (default), deselects
65 : /// all other elements and selects this element.
66 : /// \return \p true on success, \p false if selection
67 : /// cannot be changed, e.g. due to the ongoing operation
68 : /// on a score (like dragging elements) or incorrect
69 : /// arguments to this function.
70 : /// \since MuseScore 3.5
71 : //---------------------------------------------------------
72 :
73 0 : bool Selection::select(EngravingItem* elWrapper, bool add)
74 : {
75 0 : if (!checkSelectionIsNotLocked()) {
76 0 : return false;
77 : }
78 :
79 0 : if (!elWrapper) {
80 0 : return false;
81 : }
82 :
83 0 : mu::engraving::EngravingItem* e = elWrapper->element();
84 :
85 : // Check whether it's safe to select this element:
86 : // use types list from UndoableTransaction for now
87 0 : if (!mu::engraving::UndoableTransaction::canRecordSelectedElement(e)) {
88 0 : LOGW("Cannot select element of type %s", e->typeName());
89 0 : return false;
90 : }
91 :
92 0 : if (e->score() != m_selection->score() || elWrapper->ownership() != Ownership::SCORE) {
93 0 : LOGW("Selection::select: element does not belong to score");
94 0 : return false;
95 : }
96 :
97 0 : const SelectType selType = add ? SelectType::ADD : SelectType::SINGLE;
98 0 : e->score()->select(e, selType);
99 :
100 0 : return true;
101 : }
102 :
103 : //---------------------------------------------------------
104 : // Selection::selectRange
105 : /// Selects a range in a score
106 : /// \param startTick start tick to be included in selection
107 : /// \param endTick end tick of selection, excluded from selection
108 : /// \param startStaff start staff index, included in selection
109 : /// \param endStaff end staff index, excluded from selection
110 : /// \param startBox the Box at startTick to be included in selection
111 : /// \param endBox the Box at endTick to be included in selection
112 : /// \return \p true on success, \p false if selection
113 : /// cannot be changed, e.g. due to the ongoing operation
114 : /// on a score (like dragging elements) or incorrect
115 : /// arguments to this function.
116 : /// \since MuseScore 3.5
117 : //---------------------------------------------------------
118 :
119 0 : bool Selection::selectRange(int startTick, int endTick, int startStaff, int endStaff,
120 : apiv1::MeasureBase* startBox, apiv1::MeasureBase* endBox)
121 : {
122 0 : if (!checkSelectionIsNotLocked()) {
123 0 : return false;
124 : }
125 :
126 0 : const int nstaves = static_cast<int>(m_selection->score()->nstaves());
127 :
128 0 : startStaff = qBound(0, startStaff, nstaves - 1);
129 0 : endStaff = qBound(1, endStaff, nstaves);
130 :
131 0 : if (startStaff >= endStaff) {
132 0 : return false;
133 : }
134 :
135 0 : mu::engraving::Segment* segStart = m_selection->score()->tick2leftSegmentMM(mu::engraving::Fraction::fromTicks(startTick));
136 0 : mu::engraving::Segment* segEnd = m_selection->score()->tick2leftSegmentMM(mu::engraving::Fraction::fromTicks(endTick));
137 0 : if (!segStart) {
138 0 : return false;
139 : }
140 :
141 0 : if (segEnd && *segEnd < *segStart) {
142 0 : return false;
143 : }
144 :
145 0 : mu::engraving::Box* startBoxUnwrapped = nullptr;
146 0 : if (mu::engraving::EngravingItem* startBoxItem = startBox ? startBox->element() : nullptr) {
147 0 : startBoxUnwrapped = startBoxItem->isBox() ? toBox(startBoxItem) : nullptr;
148 0 : if (!startBoxUnwrapped) {
149 0 : LOGW() << "startBox has incorrect type";
150 : }
151 : }
152 :
153 0 : mu::engraving::Box* endBoxUnwrapped = nullptr;
154 0 : if (mu::engraving::EngravingItem* endBoxItem = endBox ? endBox->element() : nullptr) {
155 0 : endBoxUnwrapped = endBoxItem->isBox() ? toBox(endBoxItem) : nullptr;
156 0 : if (!endBoxUnwrapped) {
157 0 : LOGW() << "endBox has incorrect type";
158 : }
159 : }
160 :
161 0 : if (segEnd && m_selection->score()->undoStack()->hasActiveTransaction()) {
162 0 : m_selection->setRangeTicks(segStart->tick(), segEnd->tick(),
163 : startStaff, endStaff,
164 : startBoxUnwrapped, endBoxUnwrapped);
165 : } else {
166 0 : m_selection->setRange(segStart, segEnd,
167 : startStaff, endStaff,
168 : startBoxUnwrapped, endBoxUnwrapped);
169 : }
170 :
171 0 : return true;
172 : }
173 :
174 : //---------------------------------------------------------
175 : // Selection::deselect
176 : /// Deselects the given element.
177 : /// \return \p true on success, \p false if selection
178 : /// cannot be changed, e.g. due to the ongoing operation
179 : /// on a score (like dragging elements).
180 : /// \since MuseScore 3.5
181 : //---------------------------------------------------------
182 :
183 0 : bool Selection::deselect(EngravingItem* elWrapper)
184 : {
185 0 : if (!checkSelectionIsNotLocked()) {
186 0 : return false;
187 : }
188 :
189 0 : if (!elWrapper) {
190 0 : return false;
191 : }
192 :
193 0 : m_selection->score()->deselect(elWrapper->element());
194 0 : return true;
195 : }
196 :
197 : //---------------------------------------------------------
198 : // Selection::clear
199 : /// Clears the selection.
200 : /// \return \p true on success, \p false if selection
201 : /// cannot be changed, e.g. due to the ongoing operation
202 : /// on a score (like dragging elements).
203 : /// \since MuseScore 3.5
204 : //---------------------------------------------------------
205 :
206 0 : bool Selection::clear()
207 : {
208 0 : if (!checkSelectionIsNotLocked()) {
209 0 : return false;
210 : }
211 :
212 0 : m_selection->deselectAll();
213 0 : return true;
214 : }
|