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