1 //=============================================================================
2 //  MuseScore
3 //  Linux Music Score Editor
4 //
5 //  Copyright (C) 2002-2007 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 "editdrumset.h"
21 #include "menus.h"
22 #include "musescore.h"
23 #include "libmscore/xml.h"
24 #include "libmscore/utils.h"
25 #include "libmscore/chord.h"
26 #include "libmscore/score.h"
27 #include "libmscore/note.h"
28 #include "libmscore/stem.h"
29 
30 namespace Ms {
31 
32 enum Column : char { PITCH, NOTE, SHORTCUT, NAME };
33 
34 //---------------------------------------------------------
35 //   noteHeadNames
36 //   "Sol" and "Alt. Brevis" omitted,
37 //   as not being useful for drums
38 //---------------------------------------------------------
39 
40 NoteHead::Group noteHeadNames[] = {
41       NoteHead::Group::HEAD_NORMAL,
42       NoteHead::Group::HEAD_CROSS,
43       NoteHead::Group::HEAD_PLUS,
44       NoteHead::Group::HEAD_XCIRCLE,
45       NoteHead::Group::HEAD_WITHX,
46       NoteHead::Group::HEAD_TRIANGLE_UP,
47       NoteHead::Group::HEAD_TRIANGLE_DOWN,
48       NoteHead::Group::HEAD_SLASH,
49       NoteHead::Group::HEAD_SLASHED1,
50       NoteHead::Group::HEAD_SLASHED2,
51       NoteHead::Group::HEAD_DIAMOND,
52       NoteHead::Group::HEAD_DIAMOND_OLD,
53       NoteHead::Group::HEAD_CIRCLED,
54       NoteHead::Group::HEAD_CIRCLED_LARGE,
55       NoteHead::Group::HEAD_LARGE_ARROW,
56       NoteHead::Group::HEAD_DO,
57       NoteHead::Group::HEAD_RE,
58       NoteHead::Group::HEAD_MI,
59       NoteHead::Group::HEAD_FA,
60       NoteHead::Group::HEAD_LA,
61       NoteHead::Group::HEAD_TI,
62       NoteHead::Group::HEAD_CUSTOM
63       };
64 
65 //---------------------------------------------------------
66 //   operator<
67 //---------------------------------------------------------
68 
operator <(const QTreeWidgetItem & other) const69 bool EditDrumsetTreeWidgetItem::operator<(const QTreeWidgetItem & other) const
70       {
71       if (treeWidget()->sortColumn() == Column::PITCH)
72             return data(Column::PITCH, Qt::UserRole).toInt() < other.data(Column::PITCH, Qt::UserRole).toInt();
73       else
74             return QTreeWidgetItem::operator<(other);
75       }
76 
77 //---------------------------------------------------------
78 //   EditDrumset
79 //---------------------------------------------------------
80 
81 struct SymbolIcon {
82       SymId id;
83       QIcon icon;
SymbolIconMs::SymbolIcon84       SymbolIcon(SymId i, QIcon j)
85             : id(i), icon(j)
86             {}
87 
generateIconMs::SymbolIcon88       static SymbolIcon generateIcon(const SymId& id, double w, double h, double defaultScale)
89             {
90             QIcon icon;
91             QPixmap image(w, h);
92             image.fill(Qt::transparent);
93             QPainter painter(&image);
94             const QRectF& bbox = ScoreFont::fallbackFont()->bbox(id, 1);
95             const qreal actualSymbolScale = std::min(w / bbox.width(), h / bbox.height());
96             qreal mag = std::min(defaultScale, actualSymbolScale);
97             const qreal& xStShift = (w - mag * bbox.width()) / 2 - mag*bbox.left();
98             const qreal& yStShift = (h - mag * bbox.height()) / 2 - mag*bbox.top();
99             const QPointF& stPtPos = QPointF(xStShift, yStShift);
100             ScoreFont::fallbackFont()->draw(id, &painter, mag, stPtPos);
101             painter.end();
102             icon.addPixmap(image);
103             return SymbolIcon(id, icon);
104             }
105 };
106 
EditDrumset(const Drumset * ds,QWidget * parent)107 EditDrumset::EditDrumset(const Drumset* ds, QWidget* parent)
108    : QDialog(parent)
109       {
110       setObjectName("EditDrumset");
111 
112       nDrumset = *ds;
113       setupUi(this);
114       setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
115 
116       drumNote->setGrid(70, 80);
117       drumNote->setDrawGrid(false);
118       drumNote->setReadOnly(true);
119 
120       updatePitchesList();
121 
122       for (auto g : noteHeadNames)
123             noteHead->addItem(NoteHead::group2userName(g), int(g));
124 
125       connect(pitchList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
126          SLOT(itemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
127       connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(bboxClicked(QAbstractButton*)));
128       connect(name, SIGNAL(textChanged(const QString&)), SLOT(nameChanged(const QString&)));
129       connect(noteHead, SIGNAL(currentIndexChanged(int)), SLOT(valueChanged()));
130       connect(staffLine, SIGNAL(valueChanged(int)), SLOT(valueChanged()));
131       connect(voice, SIGNAL(currentIndexChanged(int)), SLOT(valueChanged()));
132       connect(stemDirection, SIGNAL(currentIndexChanged(int)), SLOT(valueChanged()));
133       connect(shortcut, SIGNAL(currentIndexChanged(int)), SLOT(shortcutChanged()));
134       connect(loadButton, SIGNAL(clicked()), SLOT(load()));
135       connect(saveButton, SIGNAL(clicked()), SLOT(save()));
136       pitchList->setColumnWidth(0, 40);
137       pitchList->setColumnWidth(1, 60);
138       pitchList->setColumnWidth(2, 30);
139 
140       QStringList validNoteheadRanges = { "Noteheads", "Round and square noteheads", "Slash noteheads", "Shape note noteheads", "Shape note noteheads supplement" };
141       QSet<QString> excludeSym = {"noteheadParenthesisLeft", "noteheadParenthesisRight", "noteheadParenthesis", "noteheadNull"};
142       QStringList primaryNoteheads = {
143             "noteheadXOrnate",
144             "noteheadXBlack",
145             "noteheadXHalf",
146             "noteheadXWhole",
147             "noteheadXDoubleWhole",
148             "noteheadSlashedBlack1",
149             "noteheadSlashedHalf1",
150             "noteheadSlashedWhole1",
151             "noteheadSlashedDoubleWhole1",
152             "noteheadSlashedBlack2",
153             "noteheadSlashedHalf2",
154             "noteheadSlashedWhole2",
155             "noteheadSlashedDoubleWhole2",
156             "noteheadSquareBlack",
157             "noteheadMoonBlack",
158             "noteheadTriangleUpRightBlack",
159             "noteheadTriangleDownBlack",
160             "noteheadTriangleUpBlack",
161             "noteheadTriangleLeftBlack",
162             "noteheadTriangleRoundDownBlack",
163             "noteheadDiamondBlack",
164             "noteheadDiamondHalf",
165             "noteheadDiamondWhole",
166             "noteheadDiamondDoubleWhole",
167             "noteheadRoundWhiteWithDot",
168             "noteheadVoidWithX",
169             "noteheadHalfWithX",
170             "noteheadWholeWithX",
171             "noteheadDoubleWholeWithX",
172             "noteheadLargeArrowUpBlack",
173             "noteheadLargeArrowUpHalf",
174             "noteheadLargeArrowUpWhole",
175             "noteheadLargeArrowUpDoubleWhole"
176       };
177 
178       int w = quarterCmb->iconSize().width()  * qApp->devicePixelRatio();
179       int h = quarterCmb->iconSize().height() * qApp->devicePixelRatio();
180       //default scale is 0.3, will use smaller scale for large noteheads symbols
181       const qreal defaultScale = 0.3 * qApp->devicePixelRatio();
182 
183       QList<SymbolIcon> resNoteheads;
184       for (auto symName : primaryNoteheads) {
185              SymId id = Sym::name2id(symName);
186              resNoteheads.append(SymbolIcon::generateIcon(id, w, h, defaultScale));
187              }
188 
189       for (QString range : validNoteheadRanges) {
190             for (auto symName : (*smuflRanges())[range]) {
191                    SymId id = Sym::name2id(symName);
192                    if (!excludeSym.contains(symName) && !primaryNoteheads.contains(symName))
193                          resNoteheads.append(SymbolIcon::generateIcon(id, w, h, defaultScale));
194                    }
195             }
196 
197       QComboBox* combos[] = { wholeCmb, halfCmb, quarterCmb, doubleWholeCmb };
198       for (QComboBox* combo : combos) {
199             for (auto si : resNoteheads) {
200                   SymId id = si.id;
201                   QIcon icon = si.icon;
202                   combo->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
203                   combo->addItem(icon, Sym::id2userName(id), Sym::id2name(id));
204                   }
205             }
206       wholeCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(SymId::noteheadWhole)));
207       halfCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(SymId::noteheadHalf)));
208       quarterCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(SymId::noteheadBlack)));
209       doubleWholeCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(SymId::noteheadDoubleWhole)));
210 
211       connect(customGbox, SIGNAL(toggled(bool)), this, SLOT(customGboxToggled(bool)));
212       connect(quarterCmb, SIGNAL(currentIndexChanged(int)), SLOT(customQuarterChanged(int)));
213 
214       MuseScore::restoreGeometry(this);
215 
216       Q_ASSERT(pitchList->topLevelItemCount() > 0);
217       pitchList->setCurrentItem(pitchList->topLevelItem(0));
218       pitchList->setFocus();
219       }
220 
221 //---------------------------------------------------------
222 //   customGboxToggled
223 //---------------------------------------------------------
224 
customGboxToggled(bool checked)225 void EditDrumset::customGboxToggled(bool checked) {
226       noteHead->setEnabled(!checked);
227       if (checked)
228             noteHead->setCurrentIndex(noteHead->findData(int(NoteHead::Group::HEAD_CUSTOM)));
229       else
230             noteHead->setCurrentIndex(noteHead->findData(int(NoteHead::Group::HEAD_NORMAL)));
231 }
232 
233 //---------------------------------------------------------
234 //   updatePitchesList
235 //---------------------------------------------------------
236 
updatePitchesList()237 void EditDrumset::updatePitchesList()
238       {
239       pitchList->clear();
240       for (int i = 0; i < 128; ++i) {
241             QTreeWidgetItem* item = new EditDrumsetTreeWidgetItem(pitchList);
242             item->setText(Column::PITCH, QString("%1").arg(i));
243             item->setText(Column::NOTE, pitch2string(i));
244             if (nDrumset.shortcut(i) == 0)
245                   item->setText(Column::SHORTCUT, "");
246             else {
247                   QString s(QChar(nDrumset.shortcut(i)));
248                   item->setText(Column::SHORTCUT, s);
249                   }
250             item->setText(Column::NAME, qApp->translate("drumset", nDrumset.name(i).toUtf8().constData()));
251             item->setData(Column::PITCH, Qt::UserRole, i);
252             }
253       pitchList->sortItems(3, Qt::SortOrder::DescendingOrder);
254       }
255 
256 //---------------------------------------------------------
257 //   refreshPitchesList
258 //---------------------------------------------------------
refreshPitchesList()259 void EditDrumset::refreshPitchesList()
260       {
261       for (int i = 0; i < pitchList->topLevelItemCount(); ++i) {
262             QTreeWidgetItem* item = pitchList->topLevelItem(i);
263             int pitch = item->data(0, Qt::UserRole).toInt();
264             if (nDrumset.shortcut(pitch) == 0)
265                   item->setText(Column::SHORTCUT, "");
266             else {
267                   QString s(QChar(nDrumset.shortcut(pitch)));
268                   item->setText(Column::SHORTCUT, s);
269                   }
270             item->setText(Column::NAME, qApp->translate("drumset", nDrumset.name(pitch).toUtf8().constData()));
271             item->setData(0, Qt::UserRole, pitch);
272             }
273       }
274 
setEnabledPitchControls(bool enable)275 void EditDrumset::setEnabledPitchControls(bool enable)
276       {
277       customGbox->setEnabled(enable);
278       noteHead->setEnabled(enable);
279       voice->setEnabled(enable);
280       shortcut->setEnabled(enable);
281       staffLine->setEnabled(enable);
282       stemDirection->setEnabled(enable);
283       drumNote->setEnabled(enable);
284       label_2->setEnabled(enable);
285       label_3->setEnabled(enable);
286       label_4->setEnabled(enable);
287       label_5->setEnabled(enable);
288       label_6->setEnabled(enable);
289       }
290 
291 //---------------------------------------------------------
292 //   nameChanged
293 //---------------------------------------------------------
294 
nameChanged(const QString & n)295 void EditDrumset::nameChanged(const QString& n)
296       {
297       QTreeWidgetItem* item = pitchList->currentItem();
298       if (item) {
299             item->setText(Column::NAME, n);
300             int pitch = item->data(Column::PITCH, Qt::UserRole).toInt();
301             if (!n.isEmpty()) {
302                   if (!nDrumset.isValid(pitch))
303                         noteHead->setCurrentIndex(0);
304                   }
305             else
306                   nDrumset.drum(pitch).name.clear();
307             }
308       setEnabledPitchControls(!n.isEmpty());
309       }
310 
311 //---------------------------------------------------------
312 //   shortcutChanged
313 //---------------------------------------------------------
314 
shortcutChanged()315 void EditDrumset::shortcutChanged()
316       {
317       QTreeWidgetItem* item = pitchList->currentItem();
318       if (!item)
319             return;
320 
321       int pitch = item->data(Column::PITCH, Qt::UserRole).toInt();
322       int sc;
323       if (shortcut->currentIndex() == 7)
324             sc = 0;
325       else
326             sc = "ABCDEFG"[shortcut->currentIndex()];
327 
328       if (QString(QChar(nDrumset.drum(pitch).shortcut)) != shortcut->currentText()) {
329             //
330             // remove conflicting shortcuts
331             //
332             for (int i = 0; i < DRUM_INSTRUMENTS; ++i) {
333                   if (i == pitch)
334                         continue;
335                   if (nDrumset.drum(i).shortcut == sc)
336                         nDrumset.drum(i).shortcut = 0;
337                   }
338             nDrumset.drum(pitch).shortcut = sc;
339             if (shortcut->currentIndex() == 7)
340                   item->setText(Column::SHORTCUT, "");
341             else
342                   item->setText(Column::SHORTCUT, shortcut->currentText());
343             }
344       refreshPitchesList();
345       }
346 
347 //---------------------------------------------------------
348 //   bboxClicked
349 //---------------------------------------------------------
bboxClicked(QAbstractButton * button)350 void EditDrumset::bboxClicked(QAbstractButton* button)
351       {
352       QDialogButtonBox::ButtonRole br = buttonBox->buttonRole(button);
353       switch(br) {
354             case QDialogButtonBox::ApplyRole:
355                   apply();
356                   break;
357 
358             case QDialogButtonBox::AcceptRole:
359                   apply();
360                   // fall through
361 
362             case QDialogButtonBox::RejectRole:
363                   close();
364                   break;
365 
366             default:
367                   qDebug("EditDrumSet: unknown button");
368                   break;
369             }
370       }
371 
372 //---------------------------------------------------------
373 //   apply
374 //---------------------------------------------------------
apply()375 void EditDrumset::apply()
376       {
377       valueChanged();  //save last changes in name
378       }
379 
380 //---------------------------------------------------------
381 //   fillCustomNoteheadsDataFromComboboxes
382 //---------------------------------------------------------
fillCustomNoteheadsDataFromComboboxes(int pitch)383 void EditDrumset::fillCustomNoteheadsDataFromComboboxes(int pitch)
384       {
385       nDrumset.drum(pitch).notehead = NoteHead::Group::HEAD_CUSTOM;
386       nDrumset.drum(pitch).noteheads[int(NoteHead::Type::HEAD_WHOLE)] = Sym::name2id(wholeCmb->currentData().toString());
387       nDrumset.drum(pitch).noteheads[int(NoteHead::Type::HEAD_QUARTER)] = Sym::name2id(quarterCmb->currentData().toString());
388       nDrumset.drum(pitch).noteheads[int(NoteHead::Type::HEAD_HALF)] = Sym::name2id(halfCmb->currentData().toString());
389       nDrumset.drum(pitch).noteheads[int(NoteHead::Type::HEAD_BREVIS)] = Sym::name2id(doubleWholeCmb->currentData().toString());
390       }
391 
fillNoteheadsComboboxes(bool customGroup,int pitch)392 void EditDrumset::fillNoteheadsComboboxes(bool customGroup, int pitch)
393       {
394       if (customGroup) {
395             wholeCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(nDrumset.noteHeads(pitch, NoteHead::Type::HEAD_WHOLE))));
396             halfCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(nDrumset.noteHeads(pitch, NoteHead::Type::HEAD_HALF))));
397             quarterCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(nDrumset.noteHeads(pitch, NoteHead::Type::HEAD_QUARTER))));
398             doubleWholeCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(nDrumset.noteHeads(pitch, NoteHead::Type::HEAD_BREVIS))));
399             }
400       else {
401             const auto group = nDrumset.drum(pitch).notehead;
402             if (group == NoteHead::Group::HEAD_INVALID)
403                   return;
404 
405             wholeCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(Note::noteHead(0, group, NoteHead::Type::HEAD_WHOLE))));
406             halfCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(Note::noteHead(0, group, NoteHead::Type::HEAD_HALF))));
407             quarterCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(Note::noteHead(0, group, NoteHead::Type::HEAD_QUARTER))));
408             doubleWholeCmb->setCurrentIndex(quarterCmb->findData(Sym::id2name(Note::noteHead(0, group, NoteHead::Type::HEAD_BREVIS))));
409             }
410       }
411 
412 //---------------------------------------------------------
413 //   itemChanged
414 //---------------------------------------------------------
itemChanged(QTreeWidgetItem * current,QTreeWidgetItem * previous)415 void EditDrumset::itemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
416       {
417       if (previous) {
418             int pitch = previous->data(0, Qt::UserRole).toInt();
419             nDrumset.drum(pitch).name          = name->text();
420             if (customGbox->isChecked())
421                   fillCustomNoteheadsDataFromComboboxes(pitch);
422             else {
423                   const QVariant currData = noteHead->currentData();
424                   if (currData.isValid())
425                         nDrumset.drum(pitch).notehead = NoteHead::Group(currData.toInt());
426                   }
427 
428             nDrumset.drum(pitch).line          = staffLine->value();
429             nDrumset.drum(pitch).voice         = voice->currentIndex();
430             if (shortcut->currentIndex() == 7)
431                   nDrumset.drum(pitch).shortcut = 0;
432             else
433                   nDrumset.drum(pitch).shortcut = "ABCDEFG"[shortcut->currentIndex()];
434             nDrumset.drum(pitch).stemDirection = Direction(stemDirection->currentIndex());
435             previous->setText(Column::NAME, qApp->translate("drumset", nDrumset.name(pitch).toUtf8().constData()));
436             }
437       if (current == 0)
438             return;
439 
440       staffLine->blockSignals(true);
441       voice->blockSignals(true);
442       stemDirection->blockSignals(true);
443       noteHead->blockSignals(true);
444 
445       int pitch = current->data(0, Qt::UserRole).toInt();
446       name->setText(qApp->translate("drumset", nDrumset.name(pitch).toUtf8().constData()));
447       staffLine->setValue(nDrumset.line(pitch));
448       qDebug("BEFORE %d", nDrumset.voice(pitch));
449       voice->setCurrentIndex(nDrumset.voice(pitch));
450       qDebug("AFTER %d", nDrumset.voice(pitch));
451       stemDirection->setCurrentIndex(int(nDrumset.stemDirection(pitch)));
452 
453       NoteHead::Group nh = nDrumset.noteHead(pitch);
454       bool isCustomGroup = (nh == NoteHead::Group::HEAD_CUSTOM);
455       if (nDrumset.isValid(pitch))
456             setCustomNoteheadsGUIEnabled(isCustomGroup);
457       noteHead->setCurrentIndex(noteHead->findData(int(nh)));
458       fillNoteheadsComboboxes(isCustomGroup, pitch);
459 
460       if (nDrumset.shortcut(pitch) == 0)
461             shortcut->setCurrentIndex(7);
462       else
463             shortcut->setCurrentIndex(nDrumset.shortcut(pitch) - 'A');
464 
465       staffLine->blockSignals(false);
466       voice->blockSignals(false);
467       stemDirection->blockSignals(false);
468       noteHead->blockSignals(false);
469 
470       updateExample();
471       }
472 
473 //---------------------------------------------------------
474 //   setCustomNoteheadsGUIEnabled
475 //---------------------------------------------------------
setCustomNoteheadsGUIEnabled(bool enabled)476 void EditDrumset::setCustomNoteheadsGUIEnabled(bool enabled)
477       {
478       customGbox->setChecked(enabled);
479       noteHead->setEnabled(!enabled);
480       if (enabled)
481             noteHead->setCurrentIndex(noteHead->findData(int(NoteHead::Group::HEAD_CUSTOM)));
482       }
483 
484 //---------------------------------------------------------
485 //   valueChanged
486 //---------------------------------------------------------
valueChanged()487 void EditDrumset::valueChanged()
488       {
489       if(!pitchList->currentItem())
490             return;
491       int pitch = pitchList->currentItem()->data(Column::PITCH, Qt::UserRole).toInt();
492       nDrumset.drum(pitch).name          = name->text();
493       if (customGbox->isChecked() || noteHead->currentIndex() == noteHead->findData(int(NoteHead::Group::HEAD_CUSTOM))) {
494             fillCustomNoteheadsDataFromComboboxes(pitch);
495             setCustomNoteheadsGUIEnabled(true);
496             }
497       else {
498             nDrumset.drum(pitch).notehead = NoteHead::Group(noteHead->currentData().toInt());
499             fillNoteheadsComboboxes(false, pitch);
500             setCustomNoteheadsGUIEnabled(false);
501       }
502 
503       nDrumset.drum(pitch).line          = staffLine->value();
504       nDrumset.drum(pitch).voice         = voice->currentIndex();
505       nDrumset.drum(pitch).stemDirection = Direction(stemDirection->currentIndex());
506       if (QString(QChar(nDrumset.drum(pitch).shortcut)) != shortcut->currentText()) {
507             if (shortcut->currentText().isEmpty())
508                   nDrumset.drum(pitch).shortcut = 0;
509             else
510                   nDrumset.drum(pitch).shortcut = shortcut->currentText().at(0).toLatin1();
511             }
512       updateExample();
513       }
514 
515 //---------------------------------------------------------
516 //   updateExample
517 //---------------------------------------------------------
updateExample()518 void EditDrumset::updateExample()
519       {
520       int pitch = pitchList->currentItem()->data(0, Qt::UserRole).toInt();
521       if (!nDrumset.isValid(pitch)) {
522             drumNote->add(0,  0, "");
523             return;
524             }
525       int line      = nDrumset.line(pitch);
526       NoteHead::Group nh = nDrumset.noteHead(pitch);
527       int v         = nDrumset.voice(pitch);
528       Direction dir = nDrumset.stemDirection(pitch);
529       bool up = (Direction::UP == dir) || (Direction::AUTO == dir && line > 4);
530       Chord* chord = new Chord(gscore);
531       chord->setDurationType(TDuration::DurationType::V_QUARTER);
532       chord->setStemDirection(dir);
533       chord->setTrack(v);
534       chord->setUp(up);
535       Note* note = new Note(gscore);
536       note->setParent(chord);
537       note->setTrack(v);
538       note->setPitch(pitch);
539       note->setTpcFromPitch();
540       note->setLine(line);
541       note->setPos(0.0, gscore->spatium() * .5 * line);
542       note->setHeadType(NoteHead::Type::HEAD_QUARTER);
543       note->setHeadGroup(nh);
544       note->setCachedNoteheadSym(Sym::name2id(quarterCmb->currentData().toString()));
545       chord->add(note);
546       Stem* stem = new Stem(gscore);
547       stem->setLen((up ? -3.0 : 3.0) * gscore->spatium());
548       chord->add(stem);
549       drumNote->add(0,  chord, qApp->translate("drumset", nDrumset.name(pitch).toUtf8().constData()));
550       }
551 
552 //---------------------------------------------------------
553 //   load
554 //---------------------------------------------------------
555 
load()556 void EditDrumset::load()
557       {
558       QString fname = mscore->getDrumsetFilename(true);
559       if (fname.isEmpty())
560             return;
561 
562       QFile fp(fname);
563       if (!fp.open(QIODevice::ReadOnly))
564             return;
565 
566       XmlReader e(&fp);
567       nDrumset.clear();
568       while (e.readNextStartElement()) {
569             if (e.name() == "museScore") {
570                   if (e.attribute("version") != MSC_VERSION) {
571                         QMessageBox::StandardButton b = QMessageBox::warning(this, tr("Drumset file too old"),
572                                                                              tr("MuseScore may not be able to load this drumset file."),
573                                                                              QMessageBox::Cancel|QMessageBox::Ignore, QMessageBox::Cancel);
574                         if (b != QMessageBox::Ignore) // covers Cancel and Esc
575                               return;
576                         }
577                   while (e.readNextStartElement()) {
578                         if (e.name() == "Drum")
579                               nDrumset.load(e);
580                         else
581                               e.unknown();
582                         }
583                   }
584             }
585       fp.close();
586       updatePitchesList();
587       }
588 
589 //---------------------------------------------------------
590 //   save
591 //---------------------------------------------------------
592 
save()593 void EditDrumset::save()
594       {
595       QString fname = mscore->getDrumsetFilename(false);
596       if (fname.isEmpty())
597             return;
598 
599       QFile f(fname);
600       if (!f.open(QIODevice::WriteOnly)) {
601             QString s = tr("Open File\n%1\nfailed: %2").arg(f.fileName()).arg(strerror(errno));
602             QMessageBox::critical(mscore, tr("Open File"), s);
603             return;
604             }
605       valueChanged();  //save last changes in name
606       XmlWriter xml(0, &f);
607       xml.header();
608       xml.stag("museScore version=\"" MSC_VERSION "\"");
609       nDrumset.save(xml);
610       xml.etag();
611       if (f.error() != QFile::NoError) {
612             QString s = tr("Write File failed: %1").arg(f.errorString());
613             QMessageBox::critical(this, tr("Write Drumset"), s);
614             }
615       }
616 
617 //---------------------------------------------------------
618 //   hideEvent
619 //---------------------------------------------------------
hideEvent(QHideEvent * event)620 void EditDrumset::hideEvent(QHideEvent* event)
621       {
622       MuseScore::saveGeometry(this);
623       QDialog::hideEvent(event);
624       }
625 
626 //---------------------------------------------------------
627 //   customQuarterChanged
628 //---------------------------------------------------------
customQuarterChanged(int)629 void EditDrumset::customQuarterChanged(int)
630       {
631       updateExample();
632       }
633 }
634