1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2018 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "connector.h"
14
15 #include "element.h"
16 #include "score.h"
17 #include "scoreElement.h"
18 #include "xml.h"
19
20 namespace Ms {
21
22 //---------------------------------------------------------
23 // ConnectorInfo
24 //---------------------------------------------------------
25
ConnectorInfo(const Element * current,int track,Fraction frac)26 ConnectorInfo::ConnectorInfo(const Element* current, int track, Fraction frac)
27 : _current(current), _score(current->score()), _currentLoc(Location::absolute())
28 {
29 if (!current)
30 qFatal("ConnectorInfo::ConnectorInfo(): invalid argument: %p", current);
31 // It is not always possible to determine the track number correctly from
32 // the current element (for example, in case of a Segment).
33 // If the caller does not know the track number and passes -1
34 // it may be corrected later.
35 if (track >= 0)
36 _currentLoc.setTrack(track);
37 if (frac >= Fraction(0,1))
38 _currentLoc.setFrac(frac);
39 }
40
41 //---------------------------------------------------------
42 // ConnectorInfo
43 //---------------------------------------------------------
44
ConnectorInfo(const Score * score,const Location & currentLocation)45 ConnectorInfo::ConnectorInfo(const Score* score, const Location& currentLocation)
46 : _score(score), _currentLoc(currentLocation)
47 {}
48
49 //---------------------------------------------------------
50 // ConnectorInfo::updateLocation
51 //---------------------------------------------------------
52
updateLocation(const Element * e,Location & l,bool clipboardmode)53 void ConnectorInfo::updateLocation(const Element* e, Location& l, bool clipboardmode)
54 {
55 l.fillForElement(e, clipboardmode);
56 }
57
58 //---------------------------------------------------------
59 // ConnectorInfo::updateCurrentInfo
60 //---------------------------------------------------------
61
updateCurrentInfo(bool clipboardmode)62 void ConnectorInfo::updateCurrentInfo(bool clipboardmode)
63 {
64 if (!currentUpdated() && _current)
65 updateLocation(_current, _currentLoc, clipboardmode);
66 setCurrentUpdated(true);
67 }
68
69 //---------------------------------------------------------
70 // ConnectorInfo::connect
71 //---------------------------------------------------------
72
connect(ConnectorInfo * other)73 bool ConnectorInfo::connect(ConnectorInfo* other)
74 {
75 if (!other || (this == other))
76 return false;
77 if (_type != other->_type || _score != other->_score)
78 return false;
79 if (hasPrevious() && _prev == nullptr
80 && other->hasNext() && other->_next == nullptr
81 ) {
82 if ((_prevLoc == other->_currentLoc)
83 && (_currentLoc == other->_nextLoc)
84 ) {
85 _prev = other;
86 other->_next = this;
87 return true;
88 }
89 }
90 if (hasNext() && _next == nullptr
91 && other->hasPrevious() && other->_prev == nullptr
92 ) {
93 if ((_nextLoc == other->_currentLoc)
94 && (_currentLoc == other->_prevLoc)
95 ) {
96 _next = other;
97 other->_prev = this;
98 return true;
99 }
100 }
101 return false;
102 }
103
104 //---------------------------------------------------------
105 // ConnectorInfo::forceConnect
106 //---------------------------------------------------------
107
forceConnect(ConnectorInfo * other)108 void ConnectorInfo::forceConnect(ConnectorInfo* other)
109 {
110 if (!other || (this == other))
111 return;
112 _next = other;
113 other->_prev = this;
114 }
115
116 //---------------------------------------------------------
117 // distance
118 //---------------------------------------------------------
119
distance(const Location & l1,const Location & l2)120 static int distance(const Location& l1, const Location& l2)
121 {
122 constexpr int commonDenominator = 1000;
123 Fraction dfrac = (l2.frac() - l1.frac()).absValue();
124 int dpos = dfrac.numerator() * commonDenominator / dfrac.denominator();
125 dpos += 10000 * qAbs(l2.measure() - l1.measure());
126 return 1000 * dpos + 100 * qAbs(l2.track() - l1.track()) + 10 * qAbs(l2.note() - l1.note()) + qAbs(l2.graceIndex() - l1.graceIndex());
127 }
128
129 //---------------------------------------------------------
130 // ConnectorInfo::orderedConnectionDistance
131 //---------------------------------------------------------
132
orderedConnectionDistance(const ConnectorInfo & c1,const ConnectorInfo & c2)133 int ConnectorInfo::orderedConnectionDistance(const ConnectorInfo& c1, const ConnectorInfo& c2)
134 {
135 Location c1Next = c1._nextLoc;
136 c1Next.toRelative(c1._currentLoc);
137 Location c2Prev = c2._currentLoc; // inversed order to get equal signs
138 c2Prev.toRelative(c2._prevLoc);
139 if (c1Next == c2Prev)
140 return distance(c1._nextLoc, c2._currentLoc);
141 return INT_MAX;
142 }
143
144 //---------------------------------------------------------
145 // ConnectorInfo::connectionDistance
146 // Returns a "distance" representing a likelihood of
147 // that the checked connectors should be connected.
148 // Returns 0 if can be readily connected via connect(),
149 // < 0 if other is likely to be the first,
150 // INT_MAX if cannot be connected
151 //---------------------------------------------------------
152
connectionDistance(const ConnectorInfo & other) const153 int ConnectorInfo::connectionDistance(const ConnectorInfo& other) const
154 {
155 if (_type != other._type || _score != other._score)
156 return INT_MAX;
157 int distThisOther = INT_MAX;
158 int distOtherThis = INT_MAX;
159 if (hasNext() && _next == nullptr
160 && other.hasPrevious() && other._prev == nullptr)
161 distThisOther = orderedConnectionDistance(*this, other);
162 if (hasPrevious() && _prev == nullptr
163 && other.hasNext() && other._next == nullptr)
164 distOtherThis = orderedConnectionDistance(other, *this);
165 if (distOtherThis < distThisOther)
166 return -distOtherThis;
167 return distThisOther;
168 }
169
170 //---------------------------------------------------------
171 // ConnectorInfo::findFirst
172 //---------------------------------------------------------
173
findFirst()174 ConnectorInfo* ConnectorInfo::findFirst()
175 {
176 ConnectorInfo* i = this;
177 while (i->_prev) {
178 i = i->_prev;
179 if (i == this) {
180 qWarning("ConnectorInfo::findFirst: circular connector %p", this);
181 return nullptr;
182 }
183 }
184 return i;
185 }
186
187 //---------------------------------------------------------
188 // ConnectorInfo::findFirst
189 //---------------------------------------------------------
190
findFirst() const191 const ConnectorInfo* ConnectorInfo::findFirst() const
192 {
193 return const_cast<ConnectorInfo*>(this)->findFirst();
194 }
195
196 //---------------------------------------------------------
197 // ConnectorInfo::findLast
198 //---------------------------------------------------------
199
findLast()200 ConnectorInfo* ConnectorInfo::findLast()
201 {
202 ConnectorInfo* i = this;
203 while (i->_next) {
204 i = i->_next;
205 if (i == this) {
206 qWarning("ConnectorInfo::findLast: circular connector %p", this);
207 return nullptr;
208 }
209 }
210 return i;
211 }
212
213 //---------------------------------------------------------
214 // ConnectorInfo::findLast
215 //---------------------------------------------------------
216
findLast() const217 const ConnectorInfo* ConnectorInfo::findLast() const
218 {
219 return const_cast<ConnectorInfo*>(this)->findLast();
220 }
221
222 //---------------------------------------------------------
223 // ConnectorInfo::finished
224 //---------------------------------------------------------
225
finished() const226 bool ConnectorInfo::finished() const
227 {
228 return (finishedLeft() && finishedRight());
229 }
230
231 //---------------------------------------------------------
232 // ConnectorInfo::finishedLeft
233 //---------------------------------------------------------
234
finishedLeft() const235 bool ConnectorInfo::finishedLeft() const
236 {
237 const ConnectorInfo* i = findFirst();
238 return (i && !i->hasPrevious());
239 }
240
241 //---------------------------------------------------------
242 // ConnectorInfo::finishedRight
243 //---------------------------------------------------------
244
finishedRight() const245 bool ConnectorInfo::finishedRight() const
246 {
247 const ConnectorInfo* i = findLast();
248 return (i && !i->hasNext());
249 }
250
251 //---------------------------------------------------------
252 // ConnectorInfo::start
253 //---------------------------------------------------------
254
start()255 ConnectorInfo* ConnectorInfo::start()
256 {
257 ConnectorInfo* i = findFirst();
258 if (i && i->hasPrevious())
259 return nullptr;
260 return i;
261 }
262
263 //---------------------------------------------------------
264 // ConnectorInfo::end
265 //---------------------------------------------------------
266
end()267 ConnectorInfo* ConnectorInfo::end()
268 {
269 ConnectorInfo* i = findLast();
270 if (i && i->hasNext())
271 return nullptr;
272 return i;
273 }
274
275 //---------------------------------------------------------
276 // ConnectorInfoReader
277 //---------------------------------------------------------
278
ConnectorInfoReader(XmlReader & e,Element * current,int track)279 ConnectorInfoReader::ConnectorInfoReader(XmlReader& e, Element* current, int track)
280 : ConnectorInfo(current, track), _reader(&e), _connector(nullptr), _connectorReceiver(current)
281 {}
282
283 //---------------------------------------------------------
284 // readPositionInfo
285 //---------------------------------------------------------
286
readPositionInfo(const XmlReader & e,int track)287 static Location readPositionInfo(const XmlReader& e, int track) {
288 Location info = e.location();
289 info.setTrack(track);
290 return info;
291 }
292
293 //---------------------------------------------------------
294 // ConnectorInfoReader
295 //---------------------------------------------------------
296
ConnectorInfoReader(XmlReader & e,Score * current,int track)297 ConnectorInfoReader::ConnectorInfoReader(XmlReader& e, Score* current, int track)
298 : ConnectorInfo(current, readPositionInfo(e, track)), _reader(&e), _connector(nullptr), _connectorReceiver(current)
299 {
300 setCurrentUpdated(true);
301 }
302
303 //---------------------------------------------------------
304 // ConnectorInfoWriter
305 //---------------------------------------------------------
306
ConnectorInfoWriter(XmlWriter & xml,const Element * current,const Element * connector,int track,Fraction frac)307 ConnectorInfoWriter::ConnectorInfoWriter(XmlWriter& xml, const Element* current, const Element* connector, int track, Fraction frac)
308 : ConnectorInfo(current, track, frac), _xml(&xml), _connector(connector)
309 {
310 if (!connector) {
311 qFatal("ConnectorInfoWriter::ConnectorInfoWriter(): invalid arguments: %p, %p", connector, current);
312 return;
313 }
314 _type = connector->type();
315 updateCurrentInfo(xml.clipboardmode());
316 }
317
318 //---------------------------------------------------------
319 // ConnectorInfoWriter::write
320 //---------------------------------------------------------
321
write()322 void ConnectorInfoWriter::write()
323 {
324 XmlWriter& xml = *_xml;
325 if (!xml.canWrite(_connector))
326 return;
327 xml.stag(QString("%1 type=\"%2\"").arg(tagName(), _connector->name()));
328 if (isStart())
329 _connector->write(xml);
330 if (hasPrevious()) {
331 xml.stag("prev");
332 _prevLoc.toRelative(_currentLoc);
333 _prevLoc.write(xml);
334 xml.etag();
335 }
336 if (hasNext()) {
337 xml.stag("next");
338 _nextLoc.toRelative(_currentLoc);
339 _nextLoc.write(xml);
340 xml.etag();
341 }
342 xml.etag();
343 }
344
345 //---------------------------------------------------------
346 // ConnectorInfoReader::read
347 //---------------------------------------------------------
348
read()349 bool ConnectorInfoReader::read()
350 {
351 XmlReader& e = *_reader;
352 const QString name(e.attribute("type"));
353 _type = ScoreElement::name2type(&name);
354
355 e.fillLocation(_currentLoc);
356
357 while (e.readNextStartElement()) {
358 const QStringRef& tag(e.name());
359
360 if (tag == "prev")
361 readEndpointLocation(_prevLoc);
362 else if (tag == "next")
363 readEndpointLocation(_nextLoc);
364 else {
365 if (tag == name)
366 _connector = Element::name2Element(tag, _connectorReceiver->score());
367 else
368 qWarning("ConnectorInfoReader::read: element tag (%s) does not match connector type (%s). Is the file corrupted?", tag.toLatin1().constData(), name.toLatin1().constData());
369
370 if (!_connector) {
371 e.unknown();
372 return false;
373 }
374 _connector->setTrack(_currentLoc.track());
375 _connector->read(e);
376 }
377 }
378 return true;
379 }
380
381 //---------------------------------------------------------
382 // ConnectorInfoReader::readEndpointLocation
383 //---------------------------------------------------------
384
readEndpointLocation(Location & l)385 void ConnectorInfoReader::readEndpointLocation(Location& l)
386 {
387 XmlReader& e = *_reader;
388 while (e.readNextStartElement()) {
389 const QStringRef& tag(e.name());
390
391 if (tag == "location") {
392 l = Location::relative();
393 l.read(e);
394 }
395 else
396 e.unknown();
397 }
398 }
399
400 //---------------------------------------------------------
401 // ConnectorInfoReader::update
402 //---------------------------------------------------------
403
update()404 void ConnectorInfoReader::update()
405 {
406 if (!currentUpdated())
407 updateCurrentInfo(_reader->pasteMode());
408 if (hasPrevious())
409 _prevLoc.toAbsolute(_currentLoc);
410 if (hasNext())
411 _nextLoc.toAbsolute(_currentLoc);
412 }
413
414 //---------------------------------------------------------
415 // ConnectorInfoReader::addToScore
416 //---------------------------------------------------------
417
addToScore(bool pasteMode)418 void ConnectorInfoReader::addToScore(bool pasteMode)
419 {
420 ConnectorInfoReader* r = this;
421 while (r->prev())
422 r = r->prev();
423 while (r) {
424 r->_connectorReceiver->readAddConnector(r, pasteMode);
425 r = r->next();
426 }
427 }
428
429 //---------------------------------------------------------
430 // ConnectorInfoReader::readConnector
431 //---------------------------------------------------------
432
readConnector(std::unique_ptr<ConnectorInfoReader> info,XmlReader & e)433 void ConnectorInfoReader::readConnector(std::unique_ptr<ConnectorInfoReader> info, XmlReader& e)
434 {
435 if (!info->read()) {
436 e.skipCurrentElement();
437 return;
438 }
439 e.addConnectorInfoLater(std::move(info));
440 }
441
442 //---------------------------------------------------------
443 // ConnectorInfoReader::connector
444 //---------------------------------------------------------
445
connector()446 Element* ConnectorInfoReader::connector()
447 {
448 // connector should be contained in the first node normally.
449 ConnectorInfo* i = findFirst();
450 if (i)
451 return static_cast<ConnectorInfoReader*>(i)->_connector;
452 return nullptr;
453 }
454
455 //---------------------------------------------------------
456 // ConnectorInfoReader::connector
457 //---------------------------------------------------------
458
connector() const459 const Element* ConnectorInfoReader::connector() const
460 {
461 return const_cast<ConnectorInfoReader*>(this)->connector();
462 }
463
464 //---------------------------------------------------------
465 // ConnectorInfoReader::releaseConnector
466 //---------------------------------------------------------
467
releaseConnector()468 Element* ConnectorInfoReader::releaseConnector()
469 {
470 ConnectorInfoReader* i = static_cast<ConnectorInfoReader*>(findFirst());
471 if (!i) {
472 // circular connector?
473 ConnectorInfoReader* ii = this;
474 Element* c = nullptr;
475 while (ii->prev()) {
476 if (ii->_connector) {
477 c = ii->_connector;
478 ii->_connector = nullptr;
479 }
480 ii = ii->prev();
481 if (ii == this)
482 break;
483 }
484 return c;
485 }
486 Element* c = i->_connector;
487 i->_connector = nullptr;
488 return c;
489 }
490
491 }
492
493