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