1 //=============================================================================
2 //  MuseScore
3 //  Linux Music Score Editor
4 //
5 //  Copyright (C) 2002-2009 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 "instrdialog.h"
21 #include "musescore.h"
22 #include "preferences.h"
23 #include "scoreview.h"
24 #include "seq.h"
25 #include "libmscore/barline.h"
26 #include "libmscore/clef.h"
27 #include "libmscore/excerpt.h"
28 #include "libmscore/instrtemplate.h"
29 #include "libmscore/measure.h"
30 #include "libmscore/part.h"
31 #include "libmscore/score.h"
32 #include "libmscore/segment.h"
33 #include "libmscore/slur.h"
34 #include "libmscore/staff.h"
35 #include "libmscore/stafftype.h"
36 #include "libmscore/undo.h"
37 #include "libmscore/bracketItem.h"
38 #include "libmscore/score.h"
39 #include "libmscore/scoreOrder.h"
40 
41 namespace Ms {
42 
43 //---------------------------------------------------------
44 //   InstrumentsDialog
45 //---------------------------------------------------------
46 
InstrumentsDialog(QWidget * parent)47 InstrumentsDialog::InstrumentsDialog(QWidget* parent)
48    : AbstractDialog(parent)
49       {
50       setObjectName("Instruments");
51       setupUi(this);
52       setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
53       QAction* a = getAction("instruments");
54       connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(buttonBoxClicked(QAbstractButton*)));
55       addAction(a);
56       saveButton->setVisible(false);
57       loadButton->setVisible(false);
58       readSettings();
59       }
60 
61 //---------------------------------------------------------
62 //   init
63 //---------------------------------------------------------
64 
init()65 void InstrumentsDialog::init()
66       {
67       instrumentsWidget->init();
68       }
69 
70 //---------------------------------------------------------
71 //   retranslate
72 //---------------------------------------------------------
73 
retranslate()74 void InstrumentsDialog::retranslate()
75       {
76       retranslateUi(this);
77       }
78 
79 //---------------------------------------------------------
80 //   buttonBoxClicked
81 //---------------------------------------------------------
82 
buttonBoxClicked(QAbstractButton * button)83 void InstrumentsDialog::buttonBoxClicked(QAbstractButton* button)
84       {
85       switch (buttonBox->buttonRole(button)) {
86             case QDialogButtonBox::AcceptRole:
87                   accept();
88                   // fall through
89             case QDialogButtonBox::RejectRole:
90                   close();
91             default:
92                   break;
93             }
94       }
95 
96 //---------------------------------------------------------
97 //   accept
98 //---------------------------------------------------------
99 
accept()100 void InstrumentsDialog::accept()
101       {
102       done(1);
103       }
104 
105 //---------------------------------------------------------
106 //   on_saveButton_clicked
107 //---------------------------------------------------------
108 
on_saveButton_clicked()109 void InstrumentsDialog::on_saveButton_clicked()
110       {
111       QString name = QFileDialog::getSaveFileName(
112          this,
113          tr("Save Instrument List"),
114          ".",
115          tr("MuseScore Instruments") + " (*.xml)",
116          0,
117          preferences.getBool(PREF_UI_APP_USENATIVEDIALOGS) ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog
118          );
119       if (name.isEmpty())
120             return;
121       QString ext(".xml");
122       QFileInfo info(name);
123 
124       if (info.suffix().isEmpty())
125             info.setFile(info.filePath() + ext);
126       QFile f(info.filePath());
127       if (!f.open(QIODevice::WriteOnly)) {
128             QString s = tr("Open Instruments File\n%1\nfailed: %2").arg(f.fileName(), strerror(errno));
129             QMessageBox::critical(mscore, tr("Open Instruments File"), s);
130             return;
131             }
132 
133       XmlWriter xml(0, &f);
134       xml.header();
135       xml.stag("museScore version=\"" MSC_VERSION "\"");
136       for (InstrumentGroup* g : instrumentGroups) {
137             xml.stag(QString("InstrumentGroup name=\"%1\" extended=\"%2\"").arg(g->name).arg(g->extended));
138             for (InstrumentTemplate* t : g->instrumentTemplates)
139                   t->write(xml);
140             xml.etag();
141             }
142       xml.etag();
143       if (f.error() != QFile::NoError) {
144             QString s = tr("Write Instruments File failed: %1").arg(f.errorString());
145             QMessageBox::critical(this, tr("Write Instruments File"), s);
146             }
147       }
148 
149 //---------------------------------------------------------
150 //   on_loadButton_clicked
151 //---------------------------------------------------------
152 
on_loadButton_clicked()153 void InstrumentsDialog::on_loadButton_clicked()
154       {
155       QString fn = QFileDialog::getOpenFileName(
156          this, tr("Load Instrument List"),
157           mscoreGlobalShare + "/templates",
158          tr("MuseScore Instruments") + " (*.xml)",
159          0,
160          preferences.getBool(PREF_UI_APP_USENATIVEDIALOGS) ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog
161          );
162       if (fn.isEmpty())
163             return;
164       QFile f(fn);
165       if (!loadInstrumentTemplates(fn)) {
166             QMessageBox::warning(0,
167                QWidget::tr("Load Style Failed"),
168                QString(strerror(errno)),
169                QString(), QWidget::tr("Quit"), QString(), 0, 1);
170             return;
171             }
172       instrumentsWidget->buildTemplateList();
173       }
174 
175 //---------------------------------------------------------
176 //   writeSettings
177 //---------------------------------------------------------
178 
writeSettings()179 void InstrumentsDialog::writeSettings()
180       {
181       MuseScore::saveGeometry(this);
182       }
183 
184 //---------------------------------------------------------
185 //   readSettings
186 //---------------------------------------------------------
187 
readSettings()188 void InstrumentsDialog::readSettings()
189       {
190       MuseScore::restoreGeometry(this);
191       }
192 
193 //---------------------------------------------------------
194 //   genPartList
195 //---------------------------------------------------------
196 
genPartList(Score * s)197 void InstrumentsDialog::genPartList(Score* s)
198       {
199       instrumentsWidget->genPartList(s);
200       }
201 
202 //---------------------------------------------------------
203 //   setScoreOrder
204 //---------------------------------------------------------
205 
setScoreOrder(ScoreOrder * order)206 void InstrumentsDialog::setScoreOrder(ScoreOrder* order)
207       {
208       instrumentsWidget->setScoreOrder(order);
209       }
210 
211 //---------------------------------------------------------
212 //   getScoreOrder
213 //---------------------------------------------------------
214 
getScoreOrder()215 ScoreOrder* InstrumentsDialog::getScoreOrder()
216       {
217       return instrumentsWidget->getScoreOrder();
218       }
219 
220 //---------------------------------------------------------
221 //   setBracketsAndBarlines
222 //---------------------------------------------------------
223 
setBracketsAndBarlines(Score * s)224 void InstrumentsDialog::setBracketsAndBarlines(Score* s)
225       {
226       instrumentsWidget->setBracketsAndBarlines(s);
227       }
228 
229 //---------------------------------------------------------
230 //   partiturList
231 //---------------------------------------------------------
232 
partiturList()233 QTreeWidget* InstrumentsDialog::partiturList()
234       {
235       return instrumentsWidget->getPartiturList();
236       }
237 
238 //---------------------------------------------------------
239 //   buildInstrumentsList
240 //---------------------------------------------------------
241 
buildInstrumentsList()242 void InstrumentsDialog::buildInstrumentsList()
243       {
244       instrumentsWidget->buildTemplateList();
245       }
246 
247 //---------------------------------------------------------
248 //   updateInstrumentDialog
249 //---------------------------------------------------------
250 
updateInstrumentDialog()251 void MuseScore::updateInstrumentDialog()
252       {
253       if (instrList)
254             instrList->buildInstrumentsList();
255       }
256 
257 //---------------------------------------------------------
258 //   editInstrList
259 //---------------------------------------------------------
260 
editInstrList()261 void MuseScore::editInstrList()
262       {
263       if (cs == 0)
264             return;
265       if (!instrList)
266             instrList = new InstrumentsDialog(this);
267       else if (instrList->isVisible()) {
268             instrList->done(0);
269             return;
270             }
271       instrList->init();
272       MasterScore* masterScore = cs->masterScore();
273       QList<Staff*> originalStaves = masterScore->staves();
274       instrList->genPartList(masterScore);
275       ScoreOrder* order = masterScore->scoreOrder();
276       instrList->setScoreOrder(order ? order : scoreOrders.customScoreOrder());
277       masterScore->startCmd();
278       masterScore->deselectAll();
279       int rv = instrList->exec();
280 
281       if (rv == 0) {
282             masterScore->endCmd();
283             return;
284             }
285       ScoreView* csv = currentScoreView();
286       if (csv && csv->noteEntryMode()) {
287 		csv->cmd(getAction("escape"));
288             qApp->processEvents();
289             updateInputState(csv->score());
290             }
291       masterScore->inputState().setTrack(-1);
292       masterScore->undo(new ChangeScoreOrder(masterScore, instrList->getScoreOrder()));
293 
294       // keep the keylist of the first pitched staff to apply it to new ones
295       KeyList tmpKeymap;
296       Staff* firstStaff = 0;
297       for (Staff* s : masterScore->staves()) {
298             KeyList* km = s->keyList();
299             if (!s->isDrumStaff(Fraction(0,1))) {     // TODO
300                   tmpKeymap.insert(km->begin(), km->end());
301                   firstStaff = s;
302                   break;
303                   }
304             }
305       Key normalizedC = Key::C;
306       // normalize the keyevents to concert pitch if necessary
307       if (firstStaff && !masterScore->styleB(Sid::concertPitch) && firstStaff->part()->instrument()->transpose().chromatic ) {
308             int interval = firstStaff->part()->instrument()->transpose().chromatic;
309             normalizedC = transposeKey(normalizedC, interval);
310             for (auto i = tmpKeymap.begin(); i != tmpKeymap.end(); ++i) {
311                   int tick = i->first;
312                   Key oKey = i->second.key();
313                   tmpKeymap[tick].setKey(transposeKey(oKey, interval));
314                   }
315             }
316       // create initial keyevent for transposing instrument if necessary
317       auto i = tmpKeymap.begin();
318       if (i == tmpKeymap.end() || i->first != 0)
319             tmpKeymap[0].setKey(normalizedC);
320 
321       //
322       // process modified partitur list
323       //
324       QTreeWidget* pl = instrList->partiturList();
325       Part* part   = 0;
326       int staffIdx = 0;
327 
328       QTreeWidgetItem* item = 0;
329       for (int idx = 0; (item = pl->topLevelItem(idx)); ++idx) {
330             PartListItem* pli = static_cast<PartListItem*>(item);
331             // check if the part contains any remaining staves
332             // mark to remove part if not
333             QTreeWidgetItem* ci = 0;
334             int staves = 0;
335             for (int cidx = 0; (ci = pli->child(cidx)); ++cidx) {
336                   StaffListItem* sli = static_cast<StaffListItem*>(ci);
337                   if (sli->op() != ListItemOp::I_DELETE)
338                         ++staves;
339                   }
340             if (staves == 0)
341                   pli->op = ListItemOp::I_DELETE;
342             }
343 
344       item = 0;
345       for (int idx = 0; (item = pl->topLevelItem(idx)); ++idx) {
346             int rstaff = 0;
347             PartListItem* pli = static_cast<PartListItem*>(item);
348             if (pli->op == ListItemOp::I_DELETE)
349                   masterScore->cmdRemovePart(pli->part);
350             else if (pli->op == ListItemOp::ADD) {
351                   const InstrumentTemplate* t = pli->it;
352                   part = new Part(masterScore);
353                   part->initFromInstrTemplate(t);
354                   part->setSoloist(pli->isSoloist());
355                   masterScore->undo(new InsertPart(part, staffIdx));
356 
357                   pli->part = part;
358                   for (int cidx = 0; pli->child(cidx); ++cidx) {
359                         StaffListItem* sli = static_cast<StaffListItem*>(pli->child(cidx));
360                         Staff* staff       = new Staff(masterScore);
361                         staff->setPart(part);
362                         sli->setStaff(staff);
363 
364                         staff->init(t, sli->staffType(), cidx);
365                         staff->setDefaultClefType(sli->defaultClefType());
366                         if (staffIdx > 0)
367                               staff->setBarLineSpan(masterScore->staff(staffIdx - 1)->barLineSpan());
368 
369                         masterScore->undoInsertStaff(staff, cidx);
370                         ++staffIdx;
371 
372                         Staff* linkedStaff = part->staves()->front();
373                         if (sli->linked() && linkedStaff != staff)
374                               Excerpt::cloneStaff(linkedStaff, staff);
375                         }
376 
377                   //insert keysigs
378                   int sidx = masterScore->staffIdx(part);
379                   int eidx = sidx + part->nstaves();
380                   if (firstStaff)
381                         masterScore->adjustKeySigs(sidx, eidx, tmpKeymap);
382                   }
383             else {
384                   part = pli->part;
385                   part->setSoloist(pli->isSoloist());
386                   if (part->show() != pli->visible())
387                         part->undoChangeProperty(Pid::VISIBLE, pli->visible());
388                   for (int cidx = 0; pli->child(cidx); ++cidx) {
389                         StaffListItem* sli = static_cast<StaffListItem*>(pli->child(cidx));
390                         if (sli->op() == ListItemOp::I_DELETE) {
391                               Staff* staff = sli->staff();
392                               int sidx = staff->idx();
393                               masterScore->cmdRemoveStaff(sidx);
394                               }
395                         else if (sli->op() == ListItemOp::ADD) {
396                               Staff* staff = new Staff(masterScore);
397                               staff->setPart(part);
398                               staff->initFromStaffType(sli->staffType());
399                               sli->setStaff(staff);
400                               staff->setDefaultClefType(sli->defaultClefType());
401                               if (staffIdx > 0)
402                                     staff->setBarLineSpan(masterScore->staff(staffIdx - 1)->barLineSpan());
403 
404                               KeySigEvent ke;
405                               if (part->staves()->empty())
406                                     ke.setKey(Key::C);
407                               else
408                                     ke = part->staff(0)->keySigEvent(Fraction(0,1));
409 
410                               staff->setKey(Fraction(0,1), ke);
411 
412                               Staff* linkedStaff = 0;
413                               if (sli->linked()) {
414                                     if (rstaff > 0)
415                                           linkedStaff = part->staves()->front();
416                                     else {
417                                           for (Staff* st : *part->staves()) {
418                                                 if (st != staff) {
419                                                       linkedStaff = st;
420                                                       break;
421                                                       }
422                                                 }
423                                           }
424                                     }
425                               if (linkedStaff) {
426                                     // do not create a link if linkedStaff will be removed,
427                                     for (int k = 0; pli->child(k); ++k) {
428                                           StaffListItem* li = static_cast<StaffListItem*>(pli->child(k));
429                                           if (li->op() == ListItemOp::I_DELETE && li->staff() == linkedStaff) {
430                                                 linkedStaff = 0;
431                                                 break;
432                                                 }
433                                           }
434                                     }
435                               masterScore->undoInsertStaff(staff, rstaff, linkedStaff == 0);
436                               if (linkedStaff)
437                                     Excerpt::cloneStaff(linkedStaff, staff);
438                               else {
439                                     if (firstStaff)
440                                           masterScore->adjustKeySigs(staffIdx, staffIdx+1, tmpKeymap);
441                                     }
442                               ++staffIdx;
443                               ++rstaff;
444                               }
445                         else if (sli->op() == ListItemOp::UPDATE) {
446                               // check changes in staff type
447                               Staff* staff = sli->staff();
448                               const StaffType* stfType = sli->staffType();
449 
450                               // use selected staff type
451                               if (stfType->name() != staff->staffType(Fraction(0,1))->name())
452                                     masterScore->undo(new ChangeStaffType(staff, *stfType));
453                               }
454                         else {
455                               ++staffIdx;
456                               ++rstaff;
457                               }
458                         }
459                   }
460             }
461 
462       //
463       //    sort staves
464       //
465       QList<Staff*> dst;
466       for (int idx = 0; idx < pl->topLevelItemCount(); ++idx) {
467             PartListItem* pli = (PartListItem*)pl->topLevelItem(idx);
468             if (pli->op == ListItemOp::I_DELETE)
469                   continue;
470             QTreeWidgetItem* ci = 0;
471             for (int cidx = 0; (ci = pli->child(cidx)); ++cidx) {
472                   StaffListItem* sli = (StaffListItem*) ci;
473                   if (sli->op() == ListItemOp::I_DELETE)
474                         continue;
475                   dst.push_back(sli->staff());
476                   }
477             }
478 
479       QList<int> dl;
480       QList<int> trackMap;
481       int idx2 = 0;
482       bool sort = false;
483       for (Staff* staff : dst) {
484             trackMap.append(originalStaves.indexOf(staff));
485             int idx = masterScore->staves().indexOf(staff);
486             if (idx == -1)
487                   qDebug("staff in dialog(%p) not found in score", staff);
488             else
489                   dl.push_back(idx);
490             if (idx != idx2)
491                   sort = true;
492             ++idx2;
493             }
494 
495       if (sort)
496             masterScore->undo(new SortStaves(masterScore, dl));
497       masterScore->undo(new MapExcerptTracks(masterScore, trackMap));
498 
499       //
500       // check for valid barLineSpan and bracketSpan
501       // in all staves
502       //
503       for (Score* s : masterScore->scoreList()) {
504             int n = s->nstaves();
505             int curSpan = 0;
506             for (int j = 0; j < n; ++j) {
507                   Staff* staff = s->staff(j);
508                   int span = staff->barLineSpan();
509                   int setSpan = -1;
510 
511                   // determine if we need to update barline span
512                   if (curSpan == 0) {
513                         // no current span; this staff must start a new one
514                         if (span == 0) {
515                               // no span; this staff must have been within a span
516                               // update it to a span of 1
517                               setSpan = j;
518                               }
519                         else if (span > (n - j)) {
520                               // span too big; staves must have been removed
521                               // reduce span to last staff
522                               setSpan = n - j;
523                               }
524                         else if (span > 1 && staff->barLineTo() > 0) {
525                               // TODO: check if span is still valid
526                               // (true if the last staff is the same as it was before this edit)
527                               // the code here fixes https://musescore.org/en/node/41786
528                               // but by forcing an update,
529                               // we lose custom modifications to staff barLineTo
530                               // at least this happens only for span > 1, and not for Mensurstrich (barLineTo<=0)
531                               setSpan = span;   // force update to pick up new barLineTo value
532                               }
533                         else {
534                               // this staff starts a span
535                               curSpan = span;
536                               }
537                         }
538                   else if (span && staff->barLineTo() > 0) {
539                         // within a current span; staff must have span of 0
540                         // except for Mensurstrich (barLineTo<=0)
541                         // for consistency with Barline::endEdit,
542                         // don't special case 1-line staves
543 //TODO                        s->undoChangeBarLineSpan(staff, 0, 0, (staff->lines() - 1) * 2);
544                         }
545 
546                   // update barline span if necessary
547                   if (setSpan > 0) {
548                         // this staff starts a span
549                         curSpan = setSpan;
550                         // calculate spanFrom and spanTo values
551 //                        int spanFrom = staff->lines() == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : 0;
552 //                        int linesTo = masterScore->staff(i + setSpan - 1)->lines();
553 //                        int spanTo = linesTo == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (linesTo - 1) * 2;
554 //TODO                         s->undoChangeBarLineSpan(staff, setSpan, spanFrom, spanTo);
555                         }
556 
557                   // count off one from barline span
558                   --curSpan;
559 
560                   // update brackets
561                   for (BracketItem* bi : staff->brackets()) {
562                         if ((bi->bracketSpan() > (n - j)))
563                               bi->undoChangeProperty(Pid::BRACKET_SPAN, n - j);
564                         }
565                   }
566             }
567 
568       //
569       // there should be at least one measure
570       //
571       if (masterScore->measures()->size() == 0)
572             masterScore->insertMeasure(ElementType::MEASURE, 0, false);
573 
574       const QList<Excerpt*> excerpts(masterScore->excerpts()); // excerpts list may change in the loop below
575       for (Excerpt* excerpt : excerpts) {
576             QList<Staff*> sl       = excerpt->partScore()->staves();
577             if (sl.size() == 0)
578                   masterScore->undo(new RemoveExcerpt(excerpt));
579             }
580 
581       // Recreate brackets and barlines.
582       instrList->setBracketsAndBarlines(masterScore);
583       for (Excerpt* excerpt : excerpts)
584             instrList->setBracketsAndBarlines(excerpt->partScore());
585 
586       masterScore->setLayoutAll();
587       masterScore->endCmd();
588       masterScore->rebuildAndUpdateExpressive(MuseScore::synthesizer("Fluid"));
589       seq->initInstruments();
590       }
591 
592 }
593