1 //=============================================================================
2 //  MuseScore
3 //  Linux Music Score Editor
4 //
5 //  Copyright (C) 2008 Werner Schweer and others
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 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19 
20 #include "excerptsdialog.h"
21 #include "musescore.h"
22 #include "libmscore/score.h"
23 #include "libmscore/part.h"
24 #include "libmscore/undo.h"
25 #include "icons.h"
26 
27 namespace Ms {
28 
29 //---------------------------------------------------------
30 //   StaffItem
31 //---------------------------------------------------------
32 
StaffItem(int firstTrack,int idx1,int idx2,const QMultiMap<int,int> tracks,bool disabled,PartItem * parent)33 StaffItem::StaffItem(int firstTrack, int idx1, int idx2, const QMultiMap<int, int> tracks, bool disabled, PartItem* parent)
34    : QTreeWidgetItem(parent)
35       {
36       _firstTrack = -1; // Indicates initialising.
37 
38       setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable));
39 
40       for (int i = 0; i < VOICES; ++i) {
41             bool inRange { true };
42             if (idx1 >= 0) {
43                   int checkTrack = tracks.value(firstTrack + i, -1);
44                   inRange = ((idx1 * VOICES) <= checkTrack) && ((checkTrack < (idx2 + 1) * VOICES));
45                   }
46             setCheckState(i + 1, inRange ? Qt::Checked : Qt::Unchecked);
47             setDisabled(disabled);
48             }
49 
50        // By setting _firstTrack the check in setData is no longer bypassed.
51        _firstTrack = firstTrack;
52       }
53 
54 //---------------------------------------------------------
55 //   setLinkMaster
56 //---------------------------------------------------------
57 
setLink(StaffItem * si)58 void StaffItem::setLink(StaffItem* si)
59       {
60       _linked.append(si);
61       si->setDisabled(true);
62       }
63 
64 //---------------------------------------------------------
65 //   setData
66 //---------------------------------------------------------
67 
setData(int column,int role,const QVariant & value)68 void StaffItem::setData(int column, int role, const QVariant& value)
69       {
70       QTreeWidgetItem::setData(column, role, value);
71 
72       if (_firstTrack < 0)
73             // Is initially set to -1 in the constructor to bypass
74             // the checked for all unchecked items during initialisation.
75             return;
76 
77       // Make sure there is always one voice selected.
78       // Is bypassed during initialisation.
79 
80       int unchecked = 0;
81       for (int i = 1; i <= VOICES; i++) {
82             if (checkState(i) == Qt::Unchecked)
83                   unchecked += 1;
84             }
85       if (unchecked == VOICES)
86             setCheckState(column, Qt::Checked);
87 
88       for (auto si : _linked)
89             si->setData(column, role, value);
90       }
91 
92 //---------------------------------------------------------
93 //   tracks
94 //---------------------------------------------------------
95 
tracks(int & staff) const96 QMultiMap<int, int> StaffItem::tracks(int& staff) const
97       {
98       QMultiMap<int, int> tracks;
99       int voice = 0;
100       for (int i = 0; i < VOICES; ++i) {
101             if (checkState(i + 1) == Qt::Checked)
102                   tracks.insert(_firstTrack + i, staff * VOICES + voice++);
103             }
104       staff++;
105       return tracks;
106       }
107 
108 //---------------------------------------------------------
109 //   PartItem
110 //---------------------------------------------------------
111 
PartItem(Part * part,const QMultiMap<int,int> tracks,int & staffIdx,bool disabled,QTreeWidget * parent)112 PartItem::PartItem(Part* part, const QMultiMap<int, int> tracks, int& staffIdx, bool disabled, QTreeWidget* parent)
113    : QTreeWidgetItem(parent)
114       {
115       setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable));
116       _part   = part;
117       setText(0, _part->partName());
118 
119       Staff* master { nullptr };
120       StaffItem* msli { nullptr };
121       int firstTrack = _part->startTrack();
122       for (int i = 0; i < _part->nstaves(); ++i) {
123             StaffItem* sli = new StaffItem(firstTrack, staffIdx, staffIdx + _part->nstaves() - 1, tracks, disabled, this);
124             _staffItems.append(sli);
125 
126             // If the part contains linked staves they all must have the same
127             // selection. Make only the first editable.
128             Staff* s = _part->staves()->at(i);
129             if (!master) {
130                   if  (s->staffList().length() > 1) {
131                         master = s;
132                         msli = sli;
133                         }
134                   }
135             else if (master && !s->staffList().length()) {
136                   master = nullptr;
137                   msli = nullptr;
138                   }
139             else {
140                   if (master->staffList().contains(s))
141                         msli->setLink(sli);
142                   }
143             firstTrack += VOICES;
144             }
145 
146       if (staffIdx >= 0)
147             staffIdx += _part->nstaves();
148       }
149 
150 //---------------------------------------------------------
151 //   ~PartItem
152 //---------------------------------------------------------
153 
~PartItem()154 PartItem::~PartItem()
155       {
156       while (!_staffItems.isEmpty())
157             delete _staffItems.takeFirst();
158       _staffItems.clear();
159       }
160 
161 //---------------------------------------------------------
162 //   findStaffItem
163 //---------------------------------------------------------
164 
findStaffItem(QTreeWidgetItem * widget) const165 StaffItem* PartItem::findStaffItem(QTreeWidgetItem* widget) const
166       {
167       for (StaffItem* si : _staffItems) {
168             if (si == widget)
169                   return si;
170             }
171       return nullptr;
172       }
173 
174 //---------------------------------------------------------
175 //   tracks
176 //---------------------------------------------------------
177 
tracks(int & staff) const178 QMultiMap<int, int> PartItem::tracks(int& staff) const
179       {
180       QMultiMap<int, int> tracks;
181       for (StaffItem* si : _staffItems)
182             tracks += si->tracks(staff);
183       return tracks;
184       }
185 
186 //---------------------------------------------------------
187 //   ExcerptItem
188 //---------------------------------------------------------
189 
ExcerptItem(Excerpt * e,QListWidget * parent)190 ExcerptItem::ExcerptItem(Excerpt* e, QListWidget* parent)
191    : QListWidgetItem(parent)
192       {
193       _excerpt = e;
194       _tracks = _excerpt->tracks();
195       setText(_excerpt->title());
196 
197       int staffIdx { 0 };
198       for (Part* part : _excerpt->parts())
199             _partItems.append(new PartItem(part, _excerpt->tracks(), staffIdx, _excerpt->partScore(), 0));
200       }
201 
202 //---------------------------------------------------------
203 //   ExcerptItem
204 //---------------------------------------------------------
205 
~ExcerptItem()206 ExcerptItem::~ExcerptItem()
207       {
208       while (!_partItems.isEmpty())
209             delete _partItems.takeFirst();
210       _partItems.clear();
211       }
212 
213 //---------------------------------------------------------
214 //   addPartItem
215 //---------------------------------------------------------
216 
addPartItem(PartItem * pi)217 void ExcerptItem::addPartItem(PartItem* pi)
218       {
219       _excerpt->parts().append(pi->part());
220       _partItems.append(pi);
221       }
222 
223 //---------------------------------------------------------
224 //   removePartItem
225 //---------------------------------------------------------
226 
removePartItem(PartItem * pi)227 void ExcerptItem::removePartItem(PartItem* pi)
228       {
229       if (_partItems.contains(pi))
230             _partItems.removeAll(pi);
231       }
232 
233 //---------------------------------------------------------
234 //   findPartItem
235 //---------------------------------------------------------
236 
findPartItem(QTreeWidgetItem * widget) const237 PartItem* ExcerptItem::findPartItem(QTreeWidgetItem* widget) const
238       {
239       for (PartItem* pi : _partItems) {
240             if ((pi == widget) || pi->findStaffItem(widget))
241                   return pi;
242             }
243       return nullptr;
244       }
245 
246 //---------------------------------------------------------
247 //   tracks
248 //---------------------------------------------------------
249 
tracks() const250 QMultiMap<int, int> ExcerptItem::tracks() const
251       {
252       QMultiMap<int, int> tracks;
253       int staff = 0;
254 
255       for (PartItem* pi : _partItems)
256             tracks += pi->tracks(staff);
257       return tracks;
258       }
259 
260 //---------------------------------------------------------
261 //   setTitle
262 //---------------------------------------------------------
263 
setTitle(const QString name)264 void ExcerptItem::setTitle(const QString name)
265       {
266       setText(name);
267       if (!isPartScore())
268             _excerpt->setTitle(name);
269       }
270 
271 //---------------------------------------------------------
272 //   InstrumentItem
273 //---------------------------------------------------------
274 
InstrumentItem(Part * part,QListWidget * parent)275 InstrumentItem::InstrumentItem(Part* part, QListWidget* parent)
276    : QListWidgetItem(parent)
277       {
278       _part = part;
279       setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable));
280       setText(_part->partName().replace("/", "_"));
281       }
282 
283 //---------------------------------------------------------
284 //   newPartItem
285 //---------------------------------------------------------
286 
newPartItem(int count) const287 PartItem* InstrumentItem::newPartItem(int count) const
288       {
289       QMultiMap<int, int> tracks;
290       for (int track = _part->startTrack(); track < _part->endTrack(); ++track)
291             tracks.insert(track, count * VOICES + track - _part->startTrack());
292 
293       int staffIdx { -1 };
294       return new PartItem(_part, tracks, staffIdx, false, 0);
295       }
296 
297 //---------------------------------------------------------
298 //   ExcerptsDialog
299 //---------------------------------------------------------
300 
ExcerptsDialog(MasterScore * s,QWidget * parent)301 ExcerptsDialog::ExcerptsDialog(MasterScore* s, QWidget* parent)
302    : QDialog(parent)
303       {
304       setObjectName("PartEditor");
305       setupUi(this);
306       setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
307       setModal(true);
308 
309       score = s->masterScore();
310 
311       for (Part* p : score->parts())
312             instrumentList->addItem(new InstrumentItem(p));
313 
314       for (Excerpt* e : score->excerpts())
315             excerptList->addItem(new ExcerptItem(e));
316 
317 
318       connect(singlePartButton, SIGNAL(clicked()), SLOT(singlePartClicked()));
319       connect(allPartsButton, SIGNAL(clicked()), SLOT(allPartsClicked()));
320       connect(deleteButton, SIGNAL(clicked()), SLOT(deleteClicked()));
321       connect(moveUpButton, SIGNAL(clicked()), SLOT(moveUpClicked()));
322       connect(moveDownButton, SIGNAL(clicked()), SLOT(moveDownClicked()));
323       connect(addButton, SIGNAL(clicked()), SLOT(addButtonClicked()));
324       connect(removeButton, SIGNAL(clicked()), SLOT(removeButtonClicked()));
325 
326       connect(excerptList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
327          SLOT(excerptChanged(QListWidgetItem*, QListWidgetItem*)));
328       connect(partList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
329          SLOT(partDoubleClicked(QTreeWidgetItem*, int)));
330       connect(partList, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
331          SLOT(partChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
332       connect(instrumentList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
333               SLOT(addButtonClicked()));
334       connect(instrumentList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
335          SLOT(instrumentChanged(QListWidgetItem*, QListWidgetItem*)));
336       connect(title, SIGNAL(textChanged(const QString&)), SLOT(titleChanged(const QString&)));
337 
338       moveUpButton->setIcon(*icons[int(Icons::arrowUp_ICON)]);
339       moveDownButton->setIcon(*icons[int(Icons::arrowDown_ICON)]);
340 
341       for (int i = 1; i <= VOICES; i++)
342             partList->header()->resizeSection(i, 30);
343 
344       if (excerptList->count())
345             excerptList->setCurrentRow(0);
346       }
347 
348 //---------------------------------------------------------
349 //   ~ExcerptsDialog
350 //---------------------------------------------------------
351 
~ExcerptsDialog()352 ExcerptsDialog::~ExcerptsDialog()
353       {
354       // A delete of a QTreeWidget includes a delete of all item. But PartItem's in the
355       // partList are owned by ExcerptItem. So deleting excerptList will delete ExcerptItem's
356       // in the list **and** all its children (in ExcerptItem), leaving invalid pointer.
357       // So, just remove the item from partList and leave the delete to the owner.
358       clearPartList();
359       }
360 
361 //---------------------------------------------------------
362 //   startExcerptsDialog
363 //---------------------------------------------------------
364 
startExcerptsDialog()365 void MuseScore::startExcerptsDialog()
366       {
367       if (cs == 0)
368             return;
369       ExcerptsDialog ed(cs->masterScore(), 0);
370       MuseScore::restoreGeometry(&ed);
371       ed.exec();
372       MuseScore::saveGeometry(&ed);
373       cs->setLayoutAll();
374       cs->update();
375       }
376 
377 //---------------------------------------------------------
378 //   singlePartClicked
379 //---------------------------------------------------------
380 
singlePartClicked()381 void ExcerptsDialog::singlePartClicked()
382       {
383       QString name = createName("Part");
384       Excerpt* e   = new Excerpt(score);
385       e->setTitle(name);
386       ExcerptItem* ei = new ExcerptItem(e);
387       excerptList->addItem(ei);
388       excerptList->selectionModel()->clearSelection();
389       excerptList->setCurrentItem(ei, QItemSelectionModel::SelectCurrent);
390 
391       for (int i = 0; i < excerptList->count(); ++i) {
392             // if except score not created yet, change the UI title
393             // if already created, change back(see createName) the excerpt title
394             ExcerptItem* eii = getExcerptItemAt(i);
395             eii->setText(eii->excerpt()->title());
396             }
397       }
398 
399 //---------------------------------------------------------
400 //   allPartsClicked
401 //---------------------------------------------------------
402 
allPartsClicked()403 void ExcerptsDialog::allPartsClicked()
404       {
405       ExcerptItem* ei = 0;
406       for (Excerpt* e : Excerpt::createAllExcerpt(score)) {
407             ei = new ExcerptItem(e);
408             excerptList->addItem(ei);
409             }
410       if (ei) {
411             excerptList->selectionModel()->clearSelection();
412             excerptList->setCurrentItem(ei, QItemSelectionModel::SelectCurrent);
413             }
414       }
415 
416 //---------------------------------------------------------
417 //   deleteClicked
418 //---------------------------------------------------------
419 
deleteClicked()420 void ExcerptsDialog::deleteClicked()
421       {
422       ExcerptItem* ei = getCurrentExcerptItem();
423       if (ei == 0)
424             return;
425 
426       delete ei;
427       setWidgetState();
428       }
429 
430 //---------------------------------------------------------
431 //   moveUpClicked
432 //---------------------------------------------------------
433 
moveUpClicked()434 void ExcerptsDialog::moveUpClicked()
435       {
436       int currentRow = excerptList->currentRow();
437       if (currentRow <= 0)
438             return;
439       QListWidgetItem* currentItem = excerptList->takeItem(currentRow);
440       excerptList->insertItem(currentRow - 1, currentItem);
441       excerptList->setCurrentRow(currentRow - 1);
442       }
443 
444 //---------------------------------------------------------
445 //   moveDownClicked
446 //---------------------------------------------------------
447 
moveDownClicked()448 void ExcerptsDialog::moveDownClicked()
449       {
450       int currentRow = excerptList->currentRow();
451       if (currentRow >= excerptList->count() - 1)
452             return;
453       QListWidgetItem* currentItem = excerptList->takeItem(currentRow);
454       excerptList->insertItem(currentRow + 1, currentItem);
455       excerptList->setCurrentRow(currentRow + 1);
456       }
457 
458 //---------------------------------------------------------
459 //  addButtonClicked
460 //    add instrument to excerpt
461 //---------------------------------------------------------
462 
addButtonClicked()463 void ExcerptsDialog::addButtonClicked()
464       {
465       InstrumentItem* ii = getCurrentInstrumentItem();
466       ExcerptItem* ei = getCurrentExcerptItem();
467 
468       if (!ii || !ei || !partList->isEnabled())
469             return;
470 
471       PartItem* pi = ii->newPartItem(partList->topLevelItemCount());
472       ei->addPartItem(pi);
473       partList->addTopLevelItem(pi);
474       partList->resizeColumnToContents(0);
475       }
476 
477 //---------------------------------------------------------
478 //   removeButtonClicked
479 //    remove instrument from score
480 //---------------------------------------------------------
481 
removeButtonClicked()482 void ExcerptsDialog::removeButtonClicked()
483       {
484       ExcerptItem* ei = getCurrentExcerptItem();
485       PartItem* pi = getCurrentPartItem();
486       if (!ei || !pi)
487             return;
488 
489       ei->removePartItem(pi);
490       partList->takeTopLevelItem(partList->indexOfTopLevelItem(pi));
491 
492       partList->resizeColumnToContents(0);
493 //       removeButton->setEnabled(partList->topLevelItemCount() > 0);
494       }
495 
496 //---------------------------------------------------------
497 //   excerptChanged
498 //---------------------------------------------------------
499 
excerptChanged(QListWidgetItem *,QListWidgetItem *)500 void ExcerptsDialog::excerptChanged(QListWidgetItem*, QListWidgetItem*)
501       {
502       clearPartList();
503       ExcerptItem* ei = getCurrentExcerptItem();
504       if (ei) {
505             title->setText(ei->text());
506             for (PartItem* pi : ei->partItems())
507                   partList->addTopLevelItem(pi);
508             }
509       else {
510             title->setText("");
511             }
512 
513       setWidgetState();
514       }
515 
516 //---------------------------------------------------------
517 //   partChanged
518 //---------------------------------------------------------
519 
partChanged(QTreeWidgetItem *,QTreeWidgetItem *)520 void ExcerptsDialog::partChanged(QTreeWidgetItem*, QTreeWidgetItem*)
521       {
522       removeButton->setEnabled(getCurrentPartItem());
523       }
524 
525 //---------------------------------------------------------
526 //   instrumentChanged
527 //---------------------------------------------------------
528 
instrumentChanged(QListWidgetItem *,QListWidgetItem *)529 void ExcerptsDialog::instrumentChanged(QListWidgetItem*, QListWidgetItem*)
530       {
531       addButton->setEnabled(getCurrentInstrumentItem());
532       }
533 
534 //---------------------------------------------------------
535 //   partDoubleClicked
536 //---------------------------------------------------------
537 
partDoubleClicked(QTreeWidgetItem * item,int)538 void ExcerptsDialog::partDoubleClicked(QTreeWidgetItem* item, int)
539       {
540       if (!item->parent()) { // top level items are PartItem
541             PartItem* pi = static_cast<PartItem*>(item); // TODO
542             QString s = pi->part()->partName();
543             title->setText(s);
544             titleChanged(s);
545             }
546       }
547 
548 //---------------------------------------------------------
549 //   doubleClickedInstrument
550 //---------------------------------------------------------
551 
doubleClickedInstrument(QTreeWidgetItem *)552 void ExcerptsDialog::doubleClickedInstrument(QTreeWidgetItem*)
553       {
554       addButtonClicked();
555       }
556 
557 //---------------------------------------------------------
558 //   titleChanged
559 //---------------------------------------------------------
560 
titleChanged(const QString & s)561 void ExcerptsDialog::titleChanged(const QString& s)
562       {
563       ExcerptItem* ei = getCurrentExcerptItem();
564       if (ei)
565             ei->setTitle(s);
566 
567       score->masterScore()->setExcerptsChanged(true);
568       }
569 
570 //---------------------------------------------------------
571 //   createName
572 //---------------------------------------------------------
573 
createName(const QString & partName)574 QString ExcerptsDialog::createName(const QString& partName)
575       {
576       int count = excerptList->count();
577       QList<Excerpt*> excerpts;
578       for (int i = 0; i < count; ++i)
579             excerpts.append(getExcerptItemAt(i)->excerpt());
580 
581       return Excerpt::createName(partName, excerpts);
582       }
583 
584 //---------------------------------------------------------
585 //   getCurrentPartItem
586 //---------------------------------------------------------
587 
getCurrentPartItem() const588 PartItem* ExcerptsDialog::getCurrentPartItem() const
589       {
590       return static_cast<PartItem*>(partList->currentItem());
591       }
592 
593 //---------------------------------------------------------
594 //   getCurrentExcerptItem
595 //---------------------------------------------------------
596 
getCurrentExcerptItem() const597 ExcerptItem* ExcerptsDialog::getCurrentExcerptItem() const
598       {
599       return static_cast<ExcerptItem*>(excerptList->currentItem());
600       }
601 
602 //---------------------------------------------------------
603 //   getExcerptItemAt
604 //---------------------------------------------------------
605 
getExcerptItemAt(int index) const606 ExcerptItem* ExcerptsDialog::getExcerptItemAt(int index) const
607       {
608       return static_cast<ExcerptItem*>(excerptList->item(index));
609       }
610 
611 //---------------------------------------------------------
612 //   getInstrumentItemAt
613 //---------------------------------------------------------
614 
getCurrentInstrumentItem() const615 InstrumentItem* ExcerptsDialog::getCurrentInstrumentItem() const
616       {
617       return static_cast<InstrumentItem*>(instrumentList->currentItem());
618       }
619 
620 //---------------------------------------------------------
621 //   clearPartList
622 //---------------------------------------------------------
623 
clearPartList()624 void ExcerptsDialog::clearPartList()
625       {
626       // The clear method of QTreeWidget cannot be used because it will delete
627       // all items, leaving behind invalid pointer.
628       // takeTopLevelItem on the other hand will remove the item from the tree
629       // but will not delete it.
630       while (partList->takeTopLevelItem(0)) {}
631       }
632 
633 
634 //---------------------------------------------------------
635 //   setWidgetState
636 //---------------------------------------------------------
637 
setWidgetState()638 void ExcerptsDialog::setWidgetState()
639       {
640       ExcerptItem* ei = getCurrentExcerptItem();
641 
642       const bool enable = ei && !ei->isPartScore();
643 
644       instrumentList->setEnabled(enable);
645       title->setEnabled(ei);
646       deleteButton->setEnabled(ei);
647 
648       addButton->setEnabled(ei && getCurrentInstrumentItem());
649       removeButton->setEnabled(ei && getCurrentPartItem());
650 
651       int idx = excerptList->currentIndex().row();
652       moveUpButton->setEnabled(ei && (idx > 0));
653       moveDownButton->setEnabled(ei && (idx < (excerptList->count() - 1)));
654       }
655 
656 //---------------------------------------------------------
657 //   getExcerptItem
658 //---------------------------------------------------------
659 
getExcerptItem(Excerpt * e)660 ExcerptItem* ExcerptsDialog::getExcerptItem(Excerpt* e)
661       {
662       for (int i = 0; i < excerptList->count(); ++i) {
663             ExcerptItem* ei = getExcerptItemAt(i);
664             if (ei && (ei->excerpt() == e))
665                   return ei;
666             }
667       return 0;
668       }
669 
670 //---------------------------------------------------------
671 //   createNewExcerpt
672 //---------------------------------------------------------
673 
createNewExcerpt(ExcerptItem * ei)674 void ExcerptsDialog::createNewExcerpt(ExcerptItem* ei)
675       {
676       Excerpt* e = ei->excerpt();
677       e->setTitle(ei->text());
678       if (e->partScore())
679             return;
680       if (e->parts().isEmpty()) {
681             qDebug("no parts");
682             return;
683             }
684 
685       Score* nscore = new Score(e->oscore());
686       e->setPartScore(nscore);
687 
688       qDebug() << " + Add part : " << e->title();
689       score->undo(new AddExcerpt(e));
690       e->setTracks(ei->tracks());
691       Excerpt::createExcerpt(e);
692 
693       // a new excerpt is created in AddExcerpt, make sure the parts are filed
694       for (Excerpt* ee : e->oscore()->excerpts()) {
695             if (ee->partScore() == nscore && ee != e) {
696                   ee->parts().clear();
697                   ee->parts().append(e->parts());
698                   }
699             }
700 
701       partList->setEnabled(false);
702       title->setEnabled(false);
703       }
704 
705 //---------------------------------------------------------
706 //   accept
707 //---------------------------------------------------------
708 
accept()709 void ExcerptsDialog::accept()
710       {
711       for (int i = 0; i < excerptList->count(); ++i)
712             excerptList->setCurrentRow(i);
713 
714       score->startCmd();
715 
716       // first pass : see if actual parts needs to be deleted or renamed
717       foreach (Excerpt* e, score->excerpts()) {
718             Score* partScore  = e->partScore();
719             ExcerptItem* ei = getExcerptItem(e);
720             if (!getExcerptItem(e) && partScore)      // Delete it because not in the list anymore
721                   score->deleteExcerpt(e);
722             else if (ei->text() != e->title())
723                   score->undo(new ChangeExcerptTitle(e, ei->text()));
724             }
725 
726       // Second pass : Create new parts
727       for (int i = 0; i < excerptList->count(); ++i) {
728             ExcerptItem* ei = getExcerptItemAt(i);
729             if (ei && !ei->isEmpty())
730                   createNewExcerpt(ei);
731             }
732 
733       // Third pass : Remove empty parts.
734       int j = 0;
735       while (j < excerptList->count()) {
736             // This new part is empty, so we don't create an excerpt but remove it from the list.
737             // Necessary to order the parts later on.
738             ExcerptItem* ei = getExcerptItemAt(j);
739             if (ei->excerpt()->parts().isEmpty() && !ei->excerpt()->partScore()) {
740                   qDebug() << " - Deleting empty parts : " << ei->text();
741                   delete ei;
742                   }
743             else
744                   j++;
745             }
746 
747       // Update the score parts order following excerptList widget
748       // The reference is the excerpt list. So we iterate following it and swap parts in the score accordingly
749       for (int i = 0; i < excerptList->count(); ++i) {
750             ExcerptItem* ei = getExcerptItemAt(i);
751 
752             if (!ei || ei->isEmpty())
753                   continue;
754 
755             int position = 0;  // Actual order position in score
756             bool found = false;
757 
758             // Looks for the excerpt and its position.
759             foreach(Excerpt* e, score->excerpts()) {
760                   if (ei->excerpt() == e) {
761                         found = true;
762                         break;
763                         }
764                   position++;
765                   }
766             if (found && (position != i))
767                   score->undo(new SwapExcerpt(score, i, position));
768             }
769       score->endCmd();
770       QDialog::accept();
771       }
772 }
773