1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 Other copyrights also apply to some parts of this work. Please
9 see the AUTHORS file and individual file headers for details.
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information.
16 */
17
18 #define RG_MODULE_STRING "[BankEditorDialog]"
19
20 #include "BankEditorDialog.h"
21 #include "MidiBankTreeWidgetItem.h"
22 #include "MidiDeviceTreeWidgetItem.h"
23 #include "MidiKeyMapTreeWidgetItem.h"
24 #include "MidiKeyMappingEditor.h"
25 #include "MidiProgramsEditor.h"
26
27 #include "misc/Debug.h"
28 #include "misc/Strings.h"
29 #include "misc/ConfigGroups.h"
30 #include "base/Device.h"
31 #include "base/MidiDevice.h"
32 #include "base/MidiProgram.h"
33 #include "base/NotationTypes.h"
34 #include "base/Studio.h"
35 #include "commands/studio/ModifyDeviceCommand.h"
36 #include "document/CommandHistory.h"
37 #include "document/RosegardenDocument.h"
38 #include "misc/ConfigGroups.h"
39 #include "gui/dialogs/ExportDeviceDialog.h"
40 #include "gui/dialogs/ImportDeviceDialog.h"
41 #include "gui/widgets/TmpStatusMsg.h"
42 #include "gui/widgets/FileDialog.h"
43 #include "gui/general/ResourceFinder.h"
44 #include "gui/general/ThornStyle.h"
45 #include "gui/dialogs/AboutDialog.h"
46 #include "document/Command.h"
47
48 #include <QLayout>
49 #include <QApplication>
50 #include <QAction>
51 #include <QComboBox>
52 #include <QTreeWidget>
53 #include <QMainWindow>
54 #include <QMessageBox>
55 #include <QCheckBox>
56 #include <QDialog>
57 #include <QDir>
58 #include <QFileInfo>
59 #include <QFrame>
60 #include <QGroupBox>
61 #include <QPushButton>
62 #include <QSizePolicy>
63 #include <QString>
64 #include <QToolTip>
65 #include <QWidget>
66 #include <QVBoxLayout>
67 #include <QHBoxLayout>
68 #include <QShortcut>
69 #include <QDesktopServices>
70 #if QT_VERSION >= 0x050000
71 #include <QStandardPaths>
72 #endif
73 #include <QDialogButtonBox>
74
75
76 namespace Rosegarden
77 {
78
BankEditorDialog(QWidget * parent,RosegardenDocument * doc,DeviceId defaultDevice)79 BankEditorDialog::BankEditorDialog(QWidget *parent,
80 RosegardenDocument *doc,
81 DeviceId defaultDevice):
82 QMainWindow(parent),
83 m_studio(&doc->getStudio()),
84 m_doc(doc),
85 m_copyBank(Device::NO_DEVICE, -1),
86 m_keepBankList(false),
87 m_deleteAllReally(false),
88 m_lastDevice(Device::NO_DEVICE),
89 m_updateDeviceList(false)
90 {
91 QWidget* mainFrame = new QWidget(this);
92 mainFrame->setContentsMargins(1, 1, 1, 1);
93 setCentralWidget(mainFrame);
94 QVBoxLayout *mainFrameLayout = new QVBoxLayout;
95 mainFrameLayout->setContentsMargins(0, 0, 0, 0);
96 mainFrameLayout->setSpacing(2);
97 mainFrame->setLayout(mainFrameLayout);
98
99 setWindowTitle(tr("Manage MIDI Banks and Programs"));
100
101 QWidget *splitter = new QWidget;
102 QHBoxLayout *splitterLayout = new QHBoxLayout;
103 splitterLayout->setContentsMargins(0, 0, 0, 0);
104 splitter->setLayout(splitterLayout);
105
106 mainFrameLayout->addWidget(splitter);
107
108 //
109 // Left-side list view
110 //
111 QWidget *leftPart = new QWidget;
112 QVBoxLayout *leftPartLayout = new QVBoxLayout;
113 leftPartLayout->setContentsMargins(2, 2, 2, 2);
114 leftPart->setLayout(leftPartLayout);
115 splitterLayout->addWidget(leftPart);
116
117 m_treeWidget = new QTreeWidget;
118 leftPartLayout->addWidget(m_treeWidget);
119
120 m_treeWidget->setColumnCount(4);
121 QStringList sl;
122 sl << tr("Device and Banks")
123 << tr("Type")
124 << tr("MSB")
125 << tr("LSB");
126 m_treeWidget->setHeaderLabels(sl);
127 m_treeWidget->setRootIsDecorated(true);
128 m_treeWidget->setSelectionBehavior(QAbstractItemView::SelectRows); //qt4
129 m_treeWidget->setSelectionMode(QAbstractItemView::SingleSelection); //qt4
130 // m_treeWidget->setAllColumnsShowFocus(true);
131 m_treeWidget->setSortingEnabled(true);
132
133 /*
134 m_treeWidget->setShowSortIndicator(true); //&&&
135 m_treeWidget->setItemsRenameable(true);
136 m_treeWidget->restoreLayout(BankEditorConfigGroup);
137 */
138
139 QFrame *bankBox = new QFrame(leftPart);
140 leftPartLayout->addWidget(bankBox);
141 bankBox->setContentsMargins(1, 1, 1, 1);
142 QGridLayout *gridLayout = new QGridLayout(bankBox);
143 gridLayout->setSpacing(4);
144
145 m_addBank = new QPushButton(tr("Add Bank"), bankBox);
146 m_addKeyMapping = new QPushButton(tr("Add Key Mapping"), bankBox);
147 m_delete = new QPushButton(tr("Delete"), bankBox);
148 m_deleteAll = new QPushButton(tr("Delete All"), bankBox);
149 gridLayout->addWidget(m_addBank, 0, 0);
150 gridLayout->addWidget(m_addKeyMapping, 0, 1);
151 gridLayout->addWidget(m_delete, 1, 0);
152 gridLayout->addWidget(m_deleteAll, 1, 1);
153
154 // Tips
155 //
156 m_addBank->setToolTip(tr("Add a Bank to the current device"));
157
158 m_addKeyMapping->setToolTip(tr("Add a Percussion Key Mapping to the current device"));
159
160 m_delete->setToolTip(tr("Delete the current Bank or Key Mapping"));
161
162 m_deleteAll->setToolTip(tr("Delete all Banks and Key Mappings from the current Device"));
163
164 m_importBanks = new QPushButton(tr("Import..."), bankBox);
165 m_exportBanks = new QPushButton(tr("Export..."), bankBox);
166 gridLayout->addWidget(m_importBanks, 2, 0);
167 gridLayout->addWidget(m_exportBanks, 2, 1);
168
169 // Tips
170 //
171 m_importBanks->setToolTip(tr("Import Bank and Program data from a Rosegarden file to the current Device"));
172 m_exportBanks->setToolTip(tr("Export all Device and Bank information to a Rosegarden format interchange file"));
173
174 m_copyPrograms = new QPushButton(tr("Copy"), bankBox);
175 m_pastePrograms = new QPushButton(tr("Paste"), bankBox);
176 gridLayout->addWidget(m_copyPrograms, 3, 0);
177 gridLayout->addWidget(m_pastePrograms, 3, 1);
178
179 bankBox->setLayout(gridLayout);
180
181 // Tips
182 //
183 m_copyPrograms->setToolTip(tr("Copy all Program names from current Bank to clipboard"));
184
185 m_pastePrograms->setToolTip(tr("Paste Program names from clipboard to current Bank"));
186
187 // connect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*)),
188 // this, SLOT(slotPopulateDeviceEditors(QTreeWidgetItem*)));
189 // note: above connection moved to setupActions
190
191 // Right side layout
192 m_rightSide = new QFrame;
193
194 m_rightSide->setContentsMargins(8, 8, 8, 8);
195 QVBoxLayout *rightSideLayout = new QVBoxLayout;
196 rightSideLayout->setContentsMargins(0, 0, 0, 0);
197 rightSideLayout->setSpacing(6);
198 m_rightSide->setLayout(rightSideLayout);
199
200 splitterLayout->addWidget(m_rightSide);
201
202 m_programEditor = new MidiProgramsEditor(this, m_rightSide);
203 rightSideLayout->addWidget(m_programEditor);
204
205 m_keyMappingEditor = new MidiKeyMappingEditor(this, m_rightSide);
206 rightSideLayout->addWidget(m_keyMappingEditor);
207 m_keyMappingEditor->hide();
208
209 m_programEditor->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
210 m_keyMappingEditor->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
211
212 m_optionBox = new QGroupBox(tr("Options"), m_rightSide);
213 QVBoxLayout *optionBoxLayout = new QVBoxLayout;
214 optionBoxLayout->setContentsMargins(0, 0, 0, 0);
215 rightSideLayout->addWidget(m_optionBox);
216
217 QWidget *variationBox = new QWidget(m_optionBox);
218 QHBoxLayout *variationBoxLayout = new QHBoxLayout;
219 variationBoxLayout->setContentsMargins(4, 4, 4, 4);
220 optionBoxLayout->addWidget(variationBox);
221
222 m_variationToggle = new QCheckBox(tr("Show Variation list based on "), variationBox);
223 variationBoxLayout->addWidget(m_variationToggle);
224 m_variationCombo = new QComboBox(variationBox);
225 variationBoxLayout->addWidget(m_variationCombo);
226 variationBox->setLayout(variationBoxLayout);
227 m_variationCombo->addItem(tr("LSB"));
228 m_variationCombo->addItem(tr("MSB"));
229
230 m_optionBox->setLayout(optionBoxLayout);
231
232 // device/bank modification
233
234 connect(m_treeWidget, &QTreeWidget::itemDoubleClicked,
235 this, &BankEditorDialog::slotEdit);
236
237 connect(m_addBank, &QAbstractButton::clicked,
238 this, &BankEditorDialog::slotAddBank);
239
240 connect(m_addKeyMapping, &QAbstractButton::clicked,
241 this, &BankEditorDialog::slotAddKeyMapping);
242
243 connect(m_delete, &QAbstractButton::clicked,
244 this, &BankEditorDialog::slotDelete);
245
246 connect(m_deleteAll, &QAbstractButton::clicked,
247 this, &BankEditorDialog::slotDeleteAll);
248
249 connect(m_importBanks, &QAbstractButton::clicked,
250 this, &BankEditorDialog::slotImport);
251
252 connect(m_exportBanks, &QAbstractButton::clicked,
253 this, &BankEditorDialog::slotExport);
254
255 connect(m_copyPrograms, &QAbstractButton::clicked,
256 this, &BankEditorDialog::slotEditCopy);
257
258 connect(m_pastePrograms, &QAbstractButton::clicked,
259 this, &BankEditorDialog::slotEditPaste);
260
261 connect(m_variationToggle, &QAbstractButton::clicked,
262 this, &BankEditorDialog::slotVariationToggled);
263
264 connect(m_variationCombo, SIGNAL(activated(int)),
265 this, SLOT(slotVariationChanged(int)));
266
267 // CommandHistory::getInstance()->attachView(actionCollection()); //&&&
268
269 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
270 this, SLOT(slotUpdate()));
271
272 // Button box
273 QDialogButtonBox *btnBox = new QDialogButtonBox(/*QDialogButtonBox::Apply | */
274 QDialogButtonBox::Reset |
275 QDialogButtonBox::Close);
276
277 mainFrameLayout->addWidget(btnBox);
278
279 m_closeButton = btnBox->button(QDialogButtonBox::Close);
280 m_resetButton = btnBox->button(QDialogButtonBox::Reset);
281
282 connect(m_resetButton, &QAbstractButton::clicked, this, &BankEditorDialog::slotReset);
283
284 // Initialize the dialog
285 //
286 initDialog();
287
288 setupActions();
289
290 // Check for no Midi devices and disable everything
291 //
292 DeviceList *devices = m_studio->getDevices();
293 DeviceListIterator it;
294 bool haveMidiPlayDevice = false;
295 for (it = devices->begin(); it != devices->end(); ++it) {
296 MidiDevice *md =
297 dynamic_cast<MidiDevice *>(*it);
298 if (md && md->getDirection() == MidiDevice::Play) {
299 haveMidiPlayDevice = true;
300 break;
301 }
302 }
303 if (!haveMidiPlayDevice) {
304 leftPart->setDisabled(true);
305 m_programEditor->setDisabled(true);
306 m_keyMappingEditor->setDisabled(true);
307 m_optionBox->setDisabled(true);
308 }
309
310 if (defaultDevice != Device::NO_DEVICE) {
311 setCurrentDevice(defaultDevice);
312 }
313
314 setAttribute(Qt::WA_DeleteOnClose);
315 // setAutoSaveSettings(BankEditorConfigGroup, true); //&&&
316 }
317
~BankEditorDialog()318 BankEditorDialog::~BankEditorDialog()
319 {
320 RG_DEBUG << "~BankEditorDialog()\n";
321
322 // m_treeWidget->saveLayout(BankEditorConfigGroup); //&&&
323
324 // if (m_doc) // see slotFileClose() for an explanation on why we need to test m_doc
325 // CommandHistory::getInstance()->detachView(actionCollection());
326 }
327
328 void
setupActions()329 BankEditorDialog::setupActions()
330 {
331 // KAction* close = KStandardAction::close (this, SLOT(slotFileClose()), actionCollection());
332
333 createAction("file_close", SLOT(slotFileClose()));
334
335 connect(m_closeButton, &QAbstractButton::clicked, this, &BankEditorDialog::slotFileClose);
336
337 createAction("edit_copy", SLOT(slotEditCopy()));
338 createAction("edit_paste", SLOT(slotEditPaste()));
339 createAction("bank_help", SLOT(slotHelpRequested()));
340 createAction("help_about_app", SLOT(slotHelpAbout()));
341
342
343 // connect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*)),
344 // this, SLOT(slotPopulateDeviceEditors(QTreeWidgetItem*)));
345
346 // connect( m_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*, int)), //twItem, column
347 // this, SLOT(slotPopulateDeviceEditors(QTreeWidgetItem*, int)) );
348
349
350 // currentItemChanged( QTreeWidgetItem* current, QTreeWidgetItem* previous )
351 connect( m_treeWidget, &QTreeWidget::currentItemChanged, //twItemCurr, teItemPrev
352 this, &BankEditorDialog::slotPopulateDeviceEditors );
353
354 connect(m_treeWidget, &QTreeWidget::itemChanged, this,
355 &BankEditorDialog::slotModifyDeviceOrBankName);
356
357 // some adjustments
358
359 /*
360 new KToolBarPopupAction(tr("Und&o"),
361 "undo",
362 KStandardShortcut::key(KStandardShortcut::Undo),
363 actionCollection(),
364 KStandardAction::stdName(KStandardAction::Undo));
365
366 new KToolBarPopupAction(tr("Re&do"),
367 "redo",
368 KStandardShortcut::key(KStandardShortcut::Redo),
369 actionCollection(),
370 KStandardAction::stdName(KStandardAction::Redo));
371 */
372
373 createMenusAndToolbars("bankeditor.rc"); //@@@ JAS orig. 0
374 }
375
376 void
initDialog()377 BankEditorDialog::initDialog()
378 {
379 // Clear down
380 //
381 m_deviceNameMap.clear();
382 m_treeWidget->clear();
383
384 // Fill list view
385 //
386 MidiDevice* midiDevice = nullptr;
387 QTreeWidgetItem* twItemDevice = nullptr;
388 DeviceList *devices = m_studio->getDevices();
389 DeviceListIterator it;
390 Device *devx;
391 // unsigned int i = 0;
392
393 // iterates over devices and create device-TreeWidgetItems (level: topLevelItem)
394 // then calls populateDeviceItem() to create bank-TreeWidgetItems (level: topLevelItem-child)
395 for (it = devices->begin(); it != devices->end(); ++it) {
396 devx = *it;
397 // for ( i=0; i < int(devices->size()); i++ ){
398 // devx = devices->at( i );
399
400 if (devx->getType() == Device::Midi) {
401
402 midiDevice = dynamic_cast<MidiDevice*>(devx);
403
404 if (!midiDevice) continue;
405 // skip read-only devices
406 if (midiDevice->getDirection() == MidiDevice::Record) continue;
407
408 m_deviceNameMap[midiDevice->getId()] = midiDevice->getName();
409 QString itemName = strtoqstr(midiDevice->getName());
410
411 RG_DEBUG << "BankEditorDialog::initDialog - adding " << itemName;
412
413 twItemDevice = new MidiDeviceTreeWidgetItem(midiDevice->getId(), m_treeWidget, itemName);
414 //twItemDevice->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable);
415
416 m_treeWidget->addTopLevelItem(twItemDevice); //
417
418 twItemDevice->setExpanded(true);
419
420 populateDeviceItem(twItemDevice, midiDevice);
421 }
422 }
423
424 populateDeviceEditors(m_treeWidget->topLevelItem(0));
425
426 // select the first device item
427 m_treeWidget->topLevelItem(0)->setSelected(true);
428 m_treeWidget->resizeColumnToContents(0);
429 m_treeWidget->setMinimumWidth(500);
430 }
431
432 void
updateDialog()433 BankEditorDialog::updateDialog()
434 {
435 // Update list view
436 //
437 m_treeWidget->blockSignals(true);
438 DeviceList *devices = m_studio->getDevices();
439 DeviceListIterator it;
440 bool deviceLabelUpdate = false;
441
442 MidiDevice* midiDevice = nullptr;
443 QTreeWidgetItem* currentIndex = nullptr;
444 MidiDeviceTreeWidgetItem* deviceItem = nullptr;
445 QTreeWidgetItem *child = nullptr;
446 unsigned int cnt, i;
447
448 for (it = devices->begin(); it != devices->end(); ++it) {
449
450 if ((*it)->getType() != Device::Midi){
451 continue;
452 }
453
454 midiDevice = dynamic_cast<MidiDevice*>(*it);
455 if (!midiDevice){
456 continue;
457 }
458
459 // skip read-only devices
460 if (midiDevice->getDirection() == MidiDevice::Record){
461 continue;
462 }
463
464 if (m_deviceNameMap.find(midiDevice->getId()) != m_deviceNameMap.end()) {
465 // Device already displayed but make sure the label is up to date
466 //
467
468 //QTreeWidgetItem*
469 currentIndex = m_treeWidget->currentItem();
470
471 if (currentIndex) {
472 //MidiDeviceTreeWidgetItem*
473 deviceItem = getParentDeviceItem(currentIndex);
474
475 if (deviceItem &&
476 deviceItem->getDeviceId() == midiDevice->getId()) {
477
478 if( deviceItem->text(0) != strtoqstr(midiDevice->getName()) ){
479
480 deviceItem->setText(0, strtoqstr(midiDevice->getName()));
481 m_deviceNameMap[midiDevice->getId()] =
482 midiDevice->getName();
483
484 /*
485 cout << "NEW TEXT FOR DEVICE " << midiDevice->getId()
486 << " IS " << midiDevice->getName() << endl;
487 cout << "LIST ITEM ID = "
488 << deviceItem->getDeviceId() << endl;
489 */
490
491 deviceLabelUpdate = true;
492 }
493
494 //QTreeWidgetItem *
495 //child = deviceItem->child(0); // bank/keymap item
496 cnt = deviceItem->childCount();
497
498 for( i=0; i<cnt; i++ ){
499
500 //QTreeWidgetItem*
501 child = deviceItem->child( i );
502
503 MidiBankTreeWidgetItem *bankItem =
504 dynamic_cast<MidiBankTreeWidgetItem *>(child);
505
506 if (bankItem) {
507 bool percussion = bankItem->isPercussion();
508 int msb = bankItem->text(2).toInt();
509 int lsb = bankItem->text(3).toInt();
510 std::string bankName =
511 midiDevice->getBankName( MidiBank(percussion, msb, lsb) );
512 if (bankName != "" &&
513 bankItem->text(0) != strtoqstr(bankName)) {
514 bankItem->setText(0, strtoqstr(bankName));
515 }
516 }
517
518 }// end for(i)
519
520 }
521 }// end if( currentIndex )
522
523 continue;
524 }
525 else
526 {
527
528 m_deviceNameMap[midiDevice->getId()] = midiDevice->getName();
529 QString itemName = strtoqstr(midiDevice->getName());
530
531 RG_DEBUG << "BankEditorDialog::updateDialog - adding "
532 << itemName;
533
534 QTreeWidgetItem* deviceItem = new MidiDeviceTreeWidgetItem
535 (midiDevice->getId(), m_treeWidget, itemName);
536
537 deviceItem->setExpanded(true);
538
539 populateDeviceItem(deviceItem, midiDevice);
540 }
541
542 }// end for( it = device ...)
543
544
545 // delete items whose corresponding devices are no longer present,
546 // and update the other ones
547 //
548 std::vector<MidiDeviceTreeWidgetItem*> itemsToDelete;
549
550 MidiDeviceTreeWidgetItem* sibling = dynamic_cast<MidiDeviceTreeWidgetItem*>
551 (m_treeWidget->topLevelItem(0));
552
553 // while (sibling) {
554 cnt = m_treeWidget->topLevelItemCount();
555 for( i=0; i<cnt; i++ ){
556 sibling = dynamic_cast<MidiDeviceTreeWidgetItem*>( m_treeWidget->topLevelItem(i) );
557
558 if (m_deviceNameMap.find(sibling->getDeviceId()) == m_deviceNameMap.end())
559 itemsToDelete.push_back(sibling);
560 else
561 updateDeviceItem(sibling);
562
563 }
564
565 for (size_t i = 0; i < itemsToDelete.size(); ++i)
566 delete itemsToDelete[i];
567
568 // m_treeWidget->sortItems(0, Qt::AscendingOrder); // column, order // sort by device (column 0)
569 // m_treeWidget->sortChildren();
570
571 m_treeWidget->blockSignals(false);
572 if (deviceLabelUpdate)
573 emit deviceNamesChanged();
574 }
575
576 void
setCurrentDevice(DeviceId device)577 BankEditorDialog::setCurrentDevice(DeviceId device)
578 {
579 unsigned int i, cnt;
580 QTreeWidgetItem* twItem;
581 MidiDeviceTreeWidgetItem* deviceItem;
582
583 cnt = m_treeWidget->topLevelItemCount();
584 for( i = 0; i < cnt; i++ ){
585 twItem = m_treeWidget->topLevelItem( i );
586 deviceItem = dynamic_cast<MidiDeviceTreeWidgetItem *>(twItem);
587 if (deviceItem && deviceItem->getDeviceId() == device) {
588 // m_treeWidget->setSelected(item, true);
589 m_treeWidget->setCurrentItem(twItem);
590 break;
591 }
592 }
593 }
594
595 void
populateDeviceItem(QTreeWidgetItem * deviceItem,MidiDevice * midiDevice)596 BankEditorDialog::populateDeviceItem(QTreeWidgetItem* deviceItem, MidiDevice* midiDevice)
597 {
598 clearItemChildren(deviceItem);
599
600 QString itemName = strtoqstr(midiDevice->getName());
601
602 BankList banks = midiDevice->getBanks();
603 // add banks for this device
604 for (size_t i = 0; i < banks.size(); ++i) {
605 RG_DEBUG << "BankEditorDialog::populateDeviceItem - adding "
606 << itemName << " - " << strtoqstr(banks[i].getName());
607 new MidiBankTreeWidgetItem(midiDevice->getId(), i, deviceItem,
608 strtoqstr(banks[i].getName()),
609 banks[i].isPercussion(),
610 banks[i].getMSB(), banks[i].getLSB());
611 }
612
613 const KeyMappingList &mappings = midiDevice->getKeyMappings();
614 for (size_t i = 0; i < mappings.size(); ++i) {
615 RG_DEBUG << "BankEditorDialog::populateDeviceItem - adding key mapping "
616 << itemName << " - " << strtoqstr(mappings[i].getName());
617 new MidiKeyMapTreeWidgetItem(midiDevice->getId(), deviceItem,
618 strtoqstr(mappings[i].getName()));
619 }
620 }
621
622 void
updateDeviceItem(MidiDeviceTreeWidgetItem * deviceItem)623 BankEditorDialog::updateDeviceItem(MidiDeviceTreeWidgetItem* deviceItem)
624 {
625 MidiDevice* midiDevice = getMidiDevice(deviceItem->getDeviceId());
626 if (!midiDevice) {
627 RG_DEBUG << "BankEditorDialog::updateDeviceItem : WARNING no midi device for this item\n";
628 return ;
629 }
630
631 QString itemName = strtoqstr(midiDevice->getName());
632
633 BankList banks = midiDevice->getBanks();
634 KeyMappingList keymaps = midiDevice->getKeyMappings();
635
636 // add missing banks for this device
637 //
638 for (size_t i = 0; i < banks.size(); ++i) {
639 if (deviceItemHasBank(deviceItem, i))
640 continue;
641
642 RG_DEBUG << "BankEditorDialog::updateDeviceItem - adding "
643 << itemName << " - " << strtoqstr(banks[i].getName());
644 new MidiBankTreeWidgetItem(midiDevice->getId(), i, deviceItem,
645 strtoqstr(banks[i].getName()),
646 banks[i].isPercussion(),
647 banks[i].getMSB(), banks[i].getLSB());
648 }
649
650 int cnt, i, n;
651
652 cnt = int(keymaps.size());
653 for ( i = 0; i < cnt; ++i) {
654
655 // QTreeWidgetItem *child = deviceItem->firstChild();
656 bool have = false;
657
658 n = 0;
659 while (n < deviceItem->childCount()) {
660 QTreeWidgetItem *child = deviceItem->child(n);
661
662 MidiKeyMapTreeWidgetItem *keyItem =
663 dynamic_cast<MidiKeyMapTreeWidgetItem*>(child);
664 if (keyItem) {
665 if (keyItem->getName() == strtoqstr(keymaps[i].getName())) {
666 have = true;
667 }
668 }
669 n += 1;
670 }
671
672 if (have)
673 continue;
674
675 RG_DEBUG << "BankEditorDialog::updateDeviceItem - adding "
676 << itemName << " - " << strtoqstr(keymaps[i].getName());
677 new MidiKeyMapTreeWidgetItem(midiDevice->getId(), deviceItem,
678 strtoqstr(keymaps[i].getName()));
679 }
680
681 // delete banks which are no longer present
682 //
683 std::vector<QTreeWidgetItem*> childrenToDelete;
684
685
686 n = 0;
687 while (n < deviceItem->childCount()) {
688
689 QTreeWidgetItem* child = deviceItem->child(n);
690
691 MidiBankTreeWidgetItem *bankItem =
692 dynamic_cast<MidiBankTreeWidgetItem *>(child);
693 if (bankItem) {
694 if (bankItem->getBank() >= int(banks.size()))
695 childrenToDelete.push_back(child);
696 else { // update the banks MSB/LSB which might have changed
697 bankItem->setPercussion(banks[bankItem->getBank()].isPercussion());
698 bankItem->setMSB(banks[bankItem->getBank()].getMSB());
699 bankItem->setLSB(banks[bankItem->getBank()].getLSB());
700 }
701 }
702
703 MidiKeyMapTreeWidgetItem *keyItem =
704 dynamic_cast<MidiKeyMapTreeWidgetItem *>(child);
705 if (keyItem) {
706 if (!midiDevice->getKeyMappingByName(qstrtostr(keyItem->getName()))) {
707 childrenToDelete.push_back(child);
708 }
709 }
710
711 // child = child->nextSibling();
712 n += 1;
713 }
714
715 for (size_t i = 0; i < childrenToDelete.size(); ++i)
716 delete childrenToDelete[i];
717 }
718
719 bool
deviceItemHasBank(MidiDeviceTreeWidgetItem * deviceItem,int bankNb)720 BankEditorDialog::deviceItemHasBank(MidiDeviceTreeWidgetItem* deviceItem, int bankNb)
721 {
722 // QTreeWidgetItem *child = deviceItem->firstChild();
723 MidiBankTreeWidgetItem *bankItem = nullptr;
724 QTreeWidgetItem *child = nullptr;
725
726 int n = 0;
727 while (n < deviceItem->childCount()) {
728 child = deviceItem->child(n);
729 bankItem = dynamic_cast<MidiBankTreeWidgetItem*>(child);
730 if (bankItem) {
731 if (bankItem->getBank() == bankNb)
732 return true;
733 }
734 // child = child->nextSibling();
735 n += 1;
736 }
737
738 return false;
739 }
740
741 void
clearItemChildren(QTreeWidgetItem * item)742 BankEditorDialog::clearItemChildren(QTreeWidgetItem* item)
743 {
744 // QTreeWidgetItem* child = 0;
745
746 // while ((child = item->child(0)))
747 // delete child;
748 while ((item->childCount() > 0))
749 delete item->child(0);
750 }
751
752 MidiDevice*
getCurrentMidiDevice()753 BankEditorDialog::getCurrentMidiDevice()
754 {
755 return getMidiDevice(m_treeWidget->currentItem());
756 }
757
758
slotPopulateDeviceEditors(QTreeWidgetItem * item,QTreeWidgetItem *)759 void BankEditorDialog::slotPopulateDeviceEditors(QTreeWidgetItem* item, QTreeWidgetItem*)
760 {
761 RG_DEBUG << "BankEditorDialog::slotPopulateDeviceEditors";
762
763 if (!item)
764 return ;
765
766 populateDeviceEditors(item);
767 }
768
769
populateDeviceEditors(QTreeWidgetItem * item)770 void BankEditorDialog::populateDeviceEditors(QTreeWidgetItem* item)
771 {
772 /**
773 * shows the program and bank editors
774 * and calls their populate( currentBank ) functions
775 **/
776 RG_DEBUG << "BankEditorDialog::populateDeviceEditors \n";
777
778 if (!item)
779 return ;
780
781 MidiKeyMapTreeWidgetItem *keyItem = dynamic_cast<MidiKeyMapTreeWidgetItem *>(item);
782
783 if (keyItem) {
784
785 enterActionState("on_key_item");
786 leaveActionState("on_bank_item");
787
788 m_delete->setEnabled(true);
789
790 MidiDevice *device = getMidiDevice(keyItem->getDeviceId());
791 if (!device)
792 return ;
793
794 setProgramList(device);
795
796 m_lastDevice = keyItem->getDeviceId();
797 m_keyMappingEditor->populate(item);
798
799 m_programEditor->hide();
800 m_keyMappingEditor->show();
801
802 m_rightSide->setEnabled(true);
803
804 return ;
805 }
806
807 MidiBankTreeWidgetItem* bankItem = dynamic_cast<MidiBankTreeWidgetItem*>(item);
808
809 if (bankItem) {
810
811 enterActionState("on_bank_item");
812 leaveActionState("on_key_item");
813
814 m_delete->setEnabled(true);
815 m_copyPrograms->setEnabled(true);
816
817 m_rightSide->setEnabled(true);
818
819 if (m_copyBank.first != Device::NO_DEVICE) m_pastePrograms->setEnabled(true);
820
821 MidiDevice *device = getMidiDevice(bankItem->getDeviceId());
822 if (!device)
823 return ;
824
825 if (!m_keepBankList || m_bankList.size() == 0) {
826 m_bankList = device->getBanks();
827 } else {
828 m_keepBankList = false;
829 }
830
831 setProgramList(device);
832
833 m_variationToggle->setChecked(device->getVariationType() !=
834 MidiDevice::NoVariations);
835 m_variationCombo->setEnabled(m_variationToggle->isChecked());
836 m_variationCombo->setCurrentIndex
837 (device->getVariationType() ==
838 MidiDevice::VariationFromLSB ? 0 : 1);
839
840 m_lastBank = m_bankList[bankItem->getBank()];
841
842 m_programEditor->populate(item);
843
844 m_keyMappingEditor->hide();
845 m_programEditor->show();
846
847 m_lastDevice = bankItem->getDeviceId();
848
849 return ;
850 }
851
852 // Device, not bank or key mapping
853 // Ensure we fill these lists for the new device
854 //
855 MidiDeviceTreeWidgetItem* deviceItem = getParentDeviceItem(item);
856 if (!deviceItem) {
857 RG_DEBUG << "BankEditorDialog::populateDeviceEditors - got no deviceItem (banks parent item) \n";
858 return ;
859 }
860
861
862 m_lastDevice = deviceItem->getDeviceId();
863
864 MidiDevice *device = getMidiDevice(deviceItem);
865 if (!device) {
866 RG_DEBUG << "BankEditorDialog::populateDeviceEditors - no device for this item\n";
867 return ;
868 }
869
870 // User must have selected a device (top level Widget)
871 // Fetch The chosen device and info.
872 // Deactivate the rightside panel and clear its contents.
873 m_bankList = device->getBanks();
874 setProgramList(device);
875
876 RG_DEBUG << "BankEditorDialog::populateDeviceEditors : not a bank item - disabling";
877 m_delete->setEnabled(false);
878 m_copyPrograms->setEnabled(false);
879 m_pastePrograms->setEnabled(false);
880 m_rightSide->setEnabled(false);
881
882 m_variationToggle->setChecked(device->getVariationType() !=
883 MidiDevice::NoVariations);
884 m_variationCombo->setEnabled(m_variationToggle->isChecked());
885 m_variationCombo->setCurrentIndex
886 (device->getVariationType() ==
887 MidiDevice::VariationFromLSB ? 0 : 1);
888
889 leaveActionState("on_bank_item");
890 leaveActionState("on_key_item");
891
892 m_programEditor->clearAll();
893 m_keyMappingEditor->clearAll();
894 }
895
896 void
slotApply()897 BankEditorDialog::slotApply()
898 {
899 RG_DEBUG << "BankEditorDialog::slotApply()\n";
900
901 ModifyDeviceCommand *command = nullptr;
902
903 MidiDevice *device = getMidiDevice(m_lastDevice);
904
905 // Make sure that we don't delete all the banks and programs
906 // if we've not populated them here yet.
907 //
908 if (m_bankList.size() == 0 && m_programList.size() == 0 &&
909 m_deleteAllReally == true) {
910 RG_DEBUG << "BankEditorDialog::slotApply() : m_bankList size = 0\n";
911
912 command = new ModifyDeviceCommand(m_studio,
913 m_lastDevice,
914 m_deviceNameMap[m_lastDevice],
915 device->getLibrarianName(),
916 device->getLibrarianEmail());
917
918 command->clearBankAndProgramList();
919 } else {
920 MidiDevice::VariationType variation =
921 MidiDevice::NoVariations;
922 if (m_variationToggle->isChecked()) {
923 if (m_variationCombo->currentIndex() == 0) {
924 variation = MidiDevice::VariationFromLSB;
925 } else {
926 variation = MidiDevice::VariationFromMSB;
927 }
928 }
929
930 RG_DEBUG << "BankEditorDialog::slotApply() : m_bankList size = "
931 << m_bankList.size();
932
933 command = new ModifyDeviceCommand(m_studio,
934 m_lastDevice,
935 m_deviceNameMap[m_lastDevice],
936 device->getLibrarianName(),
937 device->getLibrarianEmail());
938
939 MidiKeyMapTreeWidgetItem *keyItem = dynamic_cast<MidiKeyMapTreeWidgetItem*>
940 (m_treeWidget->currentItem());
941 if (keyItem) {
942 KeyMappingList kml(device->getKeyMappings());
943 for (size_t i = 0; i < kml.size(); ++i) {
944 if (kml[i].getName() == qstrtostr(keyItem->getName())) {
945 kml[i] = m_keyMappingEditor->getMapping();
946 break;
947 }
948 }
949 command->setKeyMappingList(kml);
950 }
951
952 command->setVariation(variation);
953 command->setBankList(m_bankList);
954 command->setProgramList(m_programList);
955 }
956
957 addCommandToHistory(command);
958
959 // Our freaky fudge to update instrument/device names externally
960 //
961 if (m_updateDeviceList) {
962 emit deviceNamesChanged();
963 m_updateDeviceList = false;
964 }
965
966 }
967
968 void
slotReset()969 BankEditorDialog::slotReset()
970 {
971 resetProgramList();
972
973 m_programEditor->reset();
974 m_programEditor->populate(m_treeWidget->currentItem());
975 m_keyMappingEditor->reset();
976 m_keyMappingEditor->populate(m_treeWidget->currentItem());
977
978 MidiDeviceTreeWidgetItem* deviceItem = getParentDeviceItem
979 (m_treeWidget->currentItem());
980
981 if (deviceItem) {
982 MidiDevice *device = getMidiDevice(deviceItem);
983 m_variationToggle->setChecked(device->getVariationType() !=
984 MidiDevice::NoVariations);
985 m_variationCombo->setEnabled(m_variationToggle->isChecked());
986 m_variationCombo->setCurrentIndex
987 (device->getVariationType() ==
988 MidiDevice::VariationFromLSB ? 0 : 1);
989 }
990
991 updateDialog();
992 }
993
994 void
resetProgramList()995 BankEditorDialog::resetProgramList()
996 {
997 m_programList = m_oldProgramList;
998 }
999
1000 void
setProgramList(MidiDevice * device)1001 BankEditorDialog::setProgramList(MidiDevice *device)
1002 {
1003 m_programList = device->getPrograms();
1004 m_oldProgramList = m_programList;
1005 }
1006
1007 void
slotUpdate()1008 BankEditorDialog::slotUpdate()
1009 {
1010 updateDialog();
1011 }
1012
1013 MidiDeviceTreeWidgetItem*
getParentDeviceItem(QTreeWidgetItem * item)1014 BankEditorDialog::getParentDeviceItem(QTreeWidgetItem* item)
1015 {
1016 /**
1017 * return the parent t.w.Item of a bank or keymap (which is a MidiDeviceTreeWidgetItem )
1018 **/
1019 if (!item)
1020 return nullptr;
1021
1022 if (dynamic_cast<MidiBankTreeWidgetItem*>(item)){
1023 // go up to the parent device item
1024 item = item->parent();
1025 }
1026 else if (dynamic_cast<MidiKeyMapTreeWidgetItem*>(item)){
1027 // go up to the parent device item
1028 item = item->parent();
1029 }
1030
1031 if (!item) {
1032 RG_DEBUG << "BankEditorDialog::getParentDeviceItem : missing parent device item for bank item - this SHOULD NOT HAPPEN";
1033 return nullptr;
1034 }
1035
1036 return dynamic_cast<MidiDeviceTreeWidgetItem*>(item);
1037 // QTreeWidgetItem *parent = item->parent();
1038 // if (!parent) {
1039 // // item must have been a top level widget
1040 // parent = item;
1041 // }
1042 //
1043 // return dynamic_cast<MidiDeviceTreeWidgetItem*>(parent);
1044 }
1045
1046 void
slotAddBank()1047 BankEditorDialog::slotAddBank()
1048 {
1049 if (!m_treeWidget->currentItem())
1050 return ;
1051
1052 QTreeWidgetItem* currentIndex = m_treeWidget->currentItem();
1053
1054 MidiDeviceTreeWidgetItem* deviceItem = getParentDeviceItem(currentIndex);
1055 MidiDevice *device = getMidiDevice(currentIndex);
1056
1057 if (device) {
1058 // If the bank and program lists are empty then try to
1059 // populate them.
1060 //
1061 if (m_bankList.size() == 0 && m_programList.size() == 0) {
1062 m_bankList = device->getBanks();
1063 setProgramList(device);
1064 }
1065
1066 QString name = "";
1067 int n = 0;
1068 while (name == "" || device->getBankByName(qstrtostr(name)) != nullptr) {
1069 ++n;
1070 if (n == 1)
1071 name = tr("<new bank>");
1072 else
1073 name = tr("<new bank %1>").arg(n);
1074 }
1075 std::pair<int, int> bank = getFirstFreeBank(m_treeWidget->currentItem());
1076
1077 MidiBank newBank(false,
1078 bank.first, bank.second,
1079 qstrtostr(name));
1080 m_bankList.push_back(newBank);
1081
1082 RG_DEBUG << "BankEditorDialog::slotAddBank() : deviceItem->getDeviceId() = " << deviceItem->getDeviceId();
1083 QTreeWidgetItem* newBankItem =
1084 new MidiBankTreeWidgetItem(deviceItem->getDeviceId(),
1085 m_bankList.size() - 1,
1086 deviceItem,
1087 strtoqstr(newBank.getName()),
1088 newBank.isPercussion(),
1089 newBank.getMSB(), newBank.getLSB());
1090 keepBankListForNextPopulate();
1091 m_treeWidget->setCurrentItem(newBankItem);
1092
1093 slotApply();
1094 }
1095 }
1096
1097 void
slotAddKeyMapping()1098 BankEditorDialog::slotAddKeyMapping()
1099 {
1100 if (!m_treeWidget->currentItem())
1101 return ;
1102
1103 QTreeWidgetItem* currentIndex = m_treeWidget->currentItem();
1104 // MidiDeviceTreeWidgetItem* deviceItem = getParentDeviceItem(currentIndex);
1105 MidiDevice *device = getMidiDevice(currentIndex);
1106
1107 if (device) {
1108
1109 QString name = "";
1110 int n = 0;
1111 while (name == "" || device->getKeyMappingByName(qstrtostr(name)) != nullptr) {
1112 ++n;
1113 if (n == 1)
1114 name = tr("<new mapping>");
1115 else
1116 name = tr("<new mapping %1>").arg(n);
1117 }
1118
1119 MidiKeyMapping newKeyMapping(qstrtostr(name));
1120
1121 ModifyDeviceCommand *command = new ModifyDeviceCommand
1122 (m_studio,
1123 device->getId(),
1124 device->getName(),
1125 device->getLibrarianName(),
1126 device->getLibrarianEmail());
1127
1128 KeyMappingList kml;
1129 kml.push_back(newKeyMapping);
1130 command->setKeyMappingList(kml);
1131 command->setOverwrite(false);
1132 command->setRename(false);
1133
1134 addCommandToHistory(command);
1135
1136 updateDialog();
1137 selectDeviceItem(device);
1138 }
1139 }
1140
1141 void
slotDelete()1142 BankEditorDialog::slotDelete()
1143 {
1144 if (!m_treeWidget->currentItem())
1145 return ;
1146
1147 QTreeWidgetItem* currentIndex = m_treeWidget->currentItem();
1148
1149 MidiBankTreeWidgetItem* bankItem = dynamic_cast<MidiBankTreeWidgetItem*>(currentIndex);
1150
1151 MidiDevice *device = getMidiDevice(currentIndex);
1152
1153 if (device && bankItem) {
1154 int currentBank = bankItem->getBank();
1155
1156 int reply =
1157 QMessageBox::warning(
1158 dynamic_cast<QWidget*>(this),
1159 "", /* no title */
1160 tr("Really delete this bank?"),
1161 QMessageBox::Yes | QMessageBox::No,
1162 QMessageBox::No);
1163
1164 if (reply == QMessageBox::Yes) {
1165 MidiBank bank = m_bankList[currentBank];
1166
1167 // Copy across all programs that aren't in the doomed bank
1168 //
1169 ProgramList newProgramList;
1170 for (ProgramList::iterator it = m_programList.begin();
1171 it != m_programList.end();
1172 ++it) {
1173 // If this program isn't in the bank that is being deleted,
1174 // add it to the new program list. We use partialCompare()
1175 // because the MidiBank objects in the program list do not
1176 // have their name fields filled in.
1177 if (!it->getBank().partialCompare(bank))
1178 newProgramList.push_back(*it);
1179 }
1180
1181 // Erase the bank and repopulate
1182 //
1183 BankList::iterator er =
1184 m_bankList.begin();
1185 er += currentBank;
1186 m_bankList.erase(er);
1187 m_programList = newProgramList;
1188 keepBankListForNextPopulate();
1189
1190 // the listview automatically selects a new current item
1191 m_treeWidget->blockSignals(true);
1192 delete currentIndex;
1193 m_treeWidget->blockSignals(false);
1194
1195 // Don't allow pasting from this defunct device
1196 //
1197 if (m_copyBank.first == bankItem->getDeviceId() &&
1198 m_copyBank.second == bankItem->getBank()) {
1199 m_pastePrograms->setEnabled(false);
1200 m_copyBank = std::pair<DeviceId, int>
1201 (Device::NO_DEVICE, -1);
1202 }
1203
1204 slotApply();
1205 selectDeviceItem(device);
1206 }
1207
1208 return ;
1209 }
1210
1211 MidiKeyMapTreeWidgetItem* keyItem = dynamic_cast<MidiKeyMapTreeWidgetItem*>(currentIndex);
1212
1213 if (keyItem && device) {
1214
1215 int reply =
1216 QMessageBox::warning(
1217 dynamic_cast<QWidget*>(this),
1218 "", /* no title */
1219 tr("Really delete this key mapping?"),
1220 QMessageBox::Yes | QMessageBox::No,
1221 QMessageBox::No);
1222
1223 if (reply == QMessageBox::Yes) {
1224
1225 std::string keyMappingName = qstrtostr(keyItem->getName());
1226
1227 ModifyDeviceCommand *command = new ModifyDeviceCommand
1228 (m_studio,
1229 device->getId(),
1230 device->getName(),
1231 device->getLibrarianName(),
1232 device->getLibrarianEmail());
1233
1234 KeyMappingList kml = device->getKeyMappings();
1235
1236 for (KeyMappingList::iterator i = kml.begin();
1237 i != kml.end(); ++i) {
1238 if (i->getName() == keyMappingName) {
1239 RG_DEBUG << "erasing " << keyMappingName;
1240 kml.erase(i);
1241 break;
1242 }
1243 }
1244
1245 RG_DEBUG << " setting " << kml.size() << " key mappings to device ";
1246
1247 command->setKeyMappingList(kml);
1248 command->setOverwrite(true);
1249
1250 addCommandToHistory(command);
1251
1252 RG_DEBUG << " device has " << device->getKeyMappings().size() << " key mappings now ";
1253
1254 updateDialog();
1255 }
1256
1257 return ;
1258 }
1259 }
1260
1261 void
slotDeleteAll()1262 BankEditorDialog::slotDeleteAll()
1263 {
1264 if (!m_treeWidget->currentItem())
1265 return ;
1266
1267 QTreeWidgetItem* currentIndex = m_treeWidget->currentItem();
1268 MidiDeviceTreeWidgetItem* deviceItem = getParentDeviceItem(currentIndex);
1269 MidiDevice *device = getMidiDevice(deviceItem);
1270
1271 QString question = tr("Really delete all banks for ") +
1272 strtoqstr(device->getName()) + QString(" ?");
1273
1274 int reply = QMessageBox::warning(
1275 dynamic_cast<QWidget*>(this),
1276 "", /* no title */
1277 question,
1278 QMessageBox::Yes | QMessageBox::No,
1279 QMessageBox::No);
1280
1281 if (reply == QMessageBox::Yes) {
1282
1283 // erase all bank items
1284 QTreeWidgetItem* child = nullptr;
1285 while ((child = deviceItem->child(0)))
1286 delete child;
1287
1288 m_bankList.clear();
1289 m_programList.clear();
1290
1291 // Don't allow pasting from this defunct device
1292 //
1293 if (m_copyBank.first == deviceItem->getDeviceId()) {
1294 m_pastePrograms->setEnabled(false);
1295 m_copyBank = std::pair<DeviceId, int>
1296 (Device::NO_DEVICE, -1);
1297 }
1298
1299 // Urgh, we have this horrible flag that we're using to frig this.
1300 // (we might not need this anymore but I'm too scared to remove it
1301 // now).
1302 //
1303 m_deleteAllReally = true;
1304 slotApply();
1305 m_deleteAllReally = false;
1306
1307 selectDeviceItem(device);
1308
1309 }
1310 }
1311
1312 MidiDevice*
getMidiDevice(DeviceId id)1313 BankEditorDialog::getMidiDevice(DeviceId id)
1314 {
1315 Device *device = m_studio->getDevice(id);
1316 MidiDevice *midiDevice =
1317 dynamic_cast<MidiDevice *>(device);
1318
1319 /*
1320 if (device) {
1321 if (!midiDevice) {
1322 std::cerr << "ERROR: BankEditorDialog::getMidiDevice: device "
1323 << id << " is not a MIDI device" << std::endl;
1324 }
1325 } else {
1326 std::cerr
1327 << "ERROR: BankEditorDialog::getMidiDevice: no such device as "
1328 << id << std::endl;
1329 }
1330 */
1331
1332 return midiDevice;
1333 }
1334
1335 MidiDevice*
getMidiDevice(QTreeWidgetItem * item)1336 BankEditorDialog::getMidiDevice(QTreeWidgetItem* item)
1337 {
1338 MidiDeviceTreeWidgetItem* deviceItem =
1339 dynamic_cast<MidiDeviceTreeWidgetItem*>(item);
1340 if (!deviceItem)
1341 return nullptr;
1342
1343 return getMidiDevice(deviceItem->getDeviceId());
1344 }
1345
1346 std::pair<int, int>
getFirstFreeBank(QTreeWidgetItem *)1347 BankEditorDialog::getFirstFreeBank(QTreeWidgetItem* /* item */)
1348 {
1349 //!!! percussion? this is actually only called in the expectation
1350 // that percussion==false at the moment
1351
1352 for (int msb = 0; msb < 128; ++msb) {
1353 for (int lsb = 0; lsb < 128; ++lsb) {
1354 BankList::iterator i = m_bankList.begin();
1355 for (; i != m_bankList.end(); ++i) {
1356 if (i->getLSB() == lsb && i->getMSB() == msb) {
1357 break;
1358 }
1359 }
1360 if (i == m_bankList.end())
1361 return std::pair<int, int>(msb, lsb);
1362 }
1363 }
1364
1365 return std::pair<int, int>(0, 0);
1366 }
1367
1368 void
slotModifyDeviceOrBankName(QTreeWidgetItem * item,int)1369 BankEditorDialog::slotModifyDeviceOrBankName(QTreeWidgetItem* item, int)
1370 {
1371 RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName";
1372
1373 MidiDeviceTreeWidgetItem* deviceItem =
1374 dynamic_cast<MidiDeviceTreeWidgetItem*>(item);
1375 MidiBankTreeWidgetItem* bankItem =
1376 dynamic_cast<MidiBankTreeWidgetItem*>(item);
1377 MidiKeyMapTreeWidgetItem *keyItem =
1378 dynamic_cast<MidiKeyMapTreeWidgetItem*>(item);
1379
1380 QString label = item->text(0);
1381 if (bankItem) {
1382
1383 // renaming a bank item
1384
1385 RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
1386 << "modify bank name to " << label;
1387
1388 if (m_bankList[bankItem->getBank()].getName() != qstrtostr(label)) {
1389 m_bankList[bankItem->getBank()].setName(qstrtostr(label));
1390 slotApply();
1391 }
1392
1393 } else if (keyItem) {
1394
1395 RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
1396 << "modify key mapping name to " << label;
1397
1398 QString oldName = keyItem->getName();
1399
1400 QTreeWidgetItem* currentIndex = m_treeWidget->currentItem();
1401 MidiDevice *device = getMidiDevice(currentIndex);
1402
1403 if (device) {
1404
1405 ModifyDeviceCommand *command = new ModifyDeviceCommand
1406 (m_studio,
1407 device->getId(),
1408 device->getName(),
1409 device->getLibrarianName(),
1410 device->getLibrarianEmail());
1411
1412 KeyMappingList kml = device->getKeyMappings();
1413
1414 for (KeyMappingList::iterator i = kml.begin();
1415 i != kml.end(); ++i) {
1416 if (i->getName() == qstrtostr(oldName)) {
1417 i->setName(qstrtostr(label));
1418 break;
1419 }
1420 }
1421
1422 command->setKeyMappingList(kml);
1423 command->setOverwrite(true);
1424
1425 addCommandToHistory(command);
1426
1427 updateDialog();
1428 }
1429
1430 } else if (deviceItem) { // must be last, as the others are subclasses
1431
1432 // renaming a device item
1433
1434 RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
1435 << "modify device name to " << label;
1436
1437 if (m_deviceNameMap[deviceItem->getDeviceId()] != qstrtostr(label)) {
1438 m_deviceNameMap[deviceItem->getDeviceId()] = qstrtostr(label);
1439 slotApply();
1440
1441 m_updateDeviceList = true;
1442 }
1443
1444 }
1445
1446 }
1447
1448 void
selectDeviceItem(MidiDevice * device)1449 BankEditorDialog::selectDeviceItem(MidiDevice *device)
1450 {
1451 /**
1452 * sets the device-TreeWidgetItem (visibly) selected
1453 **/
1454 QTreeWidgetItem *child;
1455 MidiDeviceTreeWidgetItem *midiDeviceItem;
1456 MidiDevice *midiDevice;
1457 unsigned int i, cnt;
1458
1459 cnt = m_treeWidget->topLevelItemCount();
1460 for( i=0; i<cnt; i++ ){
1461 child = m_treeWidget->topLevelItem( i );
1462 midiDeviceItem = dynamic_cast<MidiDeviceTreeWidgetItem*>(child);
1463
1464 if (midiDeviceItem) {
1465 midiDevice = getMidiDevice(midiDeviceItem);
1466
1467 if (midiDevice == device) {
1468 // m_treeWidget->setSelected(child, true);
1469 m_treeWidget->setCurrentItem(child);
1470 return ;
1471 }
1472 }
1473
1474 }
1475
1476 }
1477
1478 void
selectDeviceBankItem(DeviceId deviceId,int bank)1479 BankEditorDialog::selectDeviceBankItem(DeviceId deviceId,
1480 int bank)
1481 {
1482 /**
1483 * sets the device-TreeWidgetItem and bank-TreeWidgetItem with deviceId (visibly) selected
1484 **/
1485 // QTreeWidgetItem *deviceChild = m_treeWidget->topLevelItem(0);
1486 QTreeWidgetItem *bankChild;
1487 // int deviceCount = 0, bankCount = 0;
1488
1489
1490 QTreeWidgetItem *twItemDevice = nullptr;
1491 MidiDeviceTreeWidgetItem *midiDeviceItem = nullptr;
1492
1493 int bankI;
1494 int devI = 0;
1495 // while(n < m_treeWidget->childCount()){
1496 while( devI < m_treeWidget->topLevelItemCount() ){
1497 // bankChild = deviceChild->firstChild();
1498
1499 twItemDevice = m_treeWidget->topLevelItem(devI);
1500 midiDeviceItem = dynamic_cast<MidiDeviceTreeWidgetItem*>(twItemDevice);
1501
1502 if (midiDeviceItem){ // && bankChild) {
1503
1504 bankI = 0;
1505 while( bankI < twItemDevice->childCount() ){
1506 bankChild = twItemDevice->child(bankI);
1507
1508 if ((deviceId == midiDeviceItem->getDeviceId()) && (bank == bankI)) {
1509 // m_treeWidget->setSelected(bankChild, true);
1510 bankChild->setSelected(true);
1511 return ;
1512 }
1513 bankI += 1;
1514 // } while ((bankChild = bankChild->nextSibling()));
1515 }
1516 }
1517
1518 // deviceCount++;
1519 // bankCount = 0;
1520 devI += 1;
1521 // } while ((deviceChild = deviceChild->nextSibling()));
1522 }
1523 }
1524
1525 void
slotVariationToggled()1526 BankEditorDialog::slotVariationToggled()
1527 {
1528 slotApply();
1529 m_variationCombo->setEnabled(m_variationToggle->isChecked());
1530 }
1531
1532 void
slotVariationChanged(int)1533 BankEditorDialog::slotVariationChanged(int)
1534 {
1535 slotApply();
1536 }
1537
1538 void
addCommandToHistory(Command * command)1539 BankEditorDialog::addCommandToHistory(Command *command)
1540 {
1541 CommandHistory::getInstance()->addCommand(command);
1542 }
1543
1544 void
slotImport()1545 BankEditorDialog::slotImport()
1546 {
1547 #if QT_VERSION >= 0x050000
1548 QString home = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).path();
1549 #else
1550 QString home = QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)).path();
1551 #endif
1552 QString deviceDir = home + "/.local/share/rosegarden/library";
1553
1554 QString url_str = FileDialog::getOpenFileName(this, tr("Import Banks from Device in File"), deviceDir,
1555 tr("Rosegarden Device files") + " (*.rgd *.RGD)" + ";;" +
1556 tr("Rosegarden files") + " (*.rg *.RG)" + ";;" +
1557 tr("Sound fonts") + " (*.sf2 *.SF2)" + ";;" +
1558 tr("LinuxSampler configuration files") + " (*.lscp *.LSCP)" + ";;" +
1559 tr("All files") + " (*)", nullptr); ///!!! Should we allow 'All files' here since LinuxSampler files don't need to have an extension?!
1560
1561 QUrl url(url_str);
1562
1563 if (url.isEmpty())
1564 return ;
1565
1566 ImportDeviceDialog *dialog = new ImportDeviceDialog(this, url);
1567 if (dialog->doImport() && dialog->exec() == QDialog::Accepted) {
1568
1569 MidiDeviceTreeWidgetItem* deviceItem =
1570 dynamic_cast<MidiDeviceTreeWidgetItem*>
1571 (m_treeWidget->currentItem()); //### was ->selectedItem()
1572
1573 if (!deviceItem) {
1574 QMessageBox::critical(
1575 dynamic_cast<QWidget*>(this),
1576 "", /* no title */
1577 tr("Some internal error: cannot locate selected device"),
1578 QMessageBox::Ok,
1579 QMessageBox::Ok);
1580 return ;
1581 }
1582
1583 if (!dialog->haveDevice()) {
1584 QMessageBox::critical(
1585 dynamic_cast<QWidget*>(this),
1586 "", /* no title */
1587 tr("Some internal error: no device selected"),
1588 QMessageBox::Ok,
1589 QMessageBox::Ok);
1590 return ;
1591 }
1592
1593 ModifyDeviceCommand *command = nullptr;
1594
1595 BankList banks(dialog->getBanks());
1596 ProgramList programs(dialog->getPrograms());
1597 ControlList controls(dialog->getControllers());
1598 KeyMappingList keyMappings(dialog->getKeyMappings());
1599 MidiDevice::VariationType variation(dialog->getVariationType());
1600 std::string librarianName(dialog->getLibrarianName());
1601 std::string librarianEmail(dialog->getLibrarianEmail());
1602
1603 // don't record the librarian when
1604 // merging banks -- it's misleading.
1605 // (also don't use variation type)
1606 if (!dialog->shouldOverwriteBanks()) {
1607 librarianName = "";
1608 librarianEmail = "";
1609 }
1610
1611 command = new ModifyDeviceCommand(m_studio,
1612 deviceItem->getDeviceId(),
1613 dialog->getDeviceName(),
1614 librarianName,
1615 librarianEmail);
1616
1617 if (dialog->shouldOverwriteBanks()) {
1618 command->setVariation(variation);
1619 }
1620 if (dialog->shouldImportBanks()) {
1621 command->setBankList(banks);
1622 command->setProgramList(programs);
1623 }
1624 if (dialog->shouldImportControllers()) {
1625 command->setControlList(controls);
1626 }
1627 if (dialog->shouldImportKeyMappings()) {
1628 command->setKeyMappingList(keyMappings);
1629 }
1630 command->setOverwrite(dialog->shouldOverwriteBanks());
1631 command->setRename(dialog->shouldRename());
1632
1633 addCommandToHistory(command);
1634
1635 // No need to redraw the dialog, this is done by
1636 // slotUpdate, signalled by the CommandHistory
1637 MidiDevice *device = getMidiDevice(deviceItem);
1638 if (device) {
1639 selectDeviceItem(device);
1640 }
1641 }
1642
1643 delete dialog;
1644 updateDialog();
1645 }
1646
1647 void
slotEdit(QTreeWidgetItem * item,int)1648 BankEditorDialog::slotEdit(QTreeWidgetItem * item, int)
1649 {
1650 RG_DEBUG << "BankEditorDialog::slotEdit()";
1651
1652 if (item->flags() & Qt::ItemIsEditable)
1653 m_treeWidget->editItem(item);
1654 }
1655
1656 void
slotEditCopy()1657 BankEditorDialog::slotEditCopy()
1658 {
1659 MidiBankTreeWidgetItem* bankItem
1660 = dynamic_cast<MidiBankTreeWidgetItem*>(m_treeWidget->currentItem());
1661
1662 if (bankItem) {
1663 m_copyBank = std::pair<DeviceId, int>(bankItem->getDeviceId(),
1664 bankItem->getBank());
1665 m_pastePrograms->setEnabled(true);
1666 }
1667 }
1668
1669 void
slotEditPaste()1670 BankEditorDialog::slotEditPaste()
1671 {
1672 MidiBankTreeWidgetItem* bankItem
1673 = dynamic_cast<MidiBankTreeWidgetItem*>(m_treeWidget->currentItem());
1674
1675 if (bankItem) {
1676 // Get the full program and bank list for the source device
1677 //
1678 MidiDevice *device = getMidiDevice(m_copyBank.first);
1679 std::vector<MidiBank> tempBank = device->getBanks();
1680
1681 ProgramList::iterator it;
1682 std::vector<MidiProgram> tempProg;
1683
1684 // Remove programs that will be overwritten
1685 //
1686 for (it = m_programList.begin(); it != m_programList.end(); ++it) {
1687 if (!(it->getBank().partialCompare(m_lastBank)))
1688 tempProg.push_back(*it);
1689 }
1690 m_programList = tempProg;
1691
1692 // Now get source list and msb/lsb
1693 //
1694 tempProg = device->getPrograms();
1695 MidiBank sourceBank = tempBank[m_copyBank.second];
1696
1697 // Add the new programs
1698 //
1699 for (it = tempProg.begin(); it != tempProg.end(); ++it) {
1700 if (it->getBank().partialCompare(sourceBank)) {
1701 // Insert with new MSB and LSB
1702 //
1703 MidiProgram copyProgram(m_lastBank,
1704 it->getProgram(),
1705 it->getName());
1706
1707 m_programList.push_back(copyProgram);
1708 }
1709 }
1710
1711 // Save these for post-apply
1712 //
1713 DeviceId devPos = bankItem->getDeviceId();
1714 int bankPos = bankItem->getBank();
1715
1716 slotApply();
1717
1718 // Select same bank
1719 //
1720 selectDeviceBankItem(devPos, bankPos);
1721 }
1722 }
1723
1724 void
slotExport()1725 BankEditorDialog::slotExport()
1726 {
1727 QString extension = "rgd";
1728
1729 /*
1730 * Qt4:
1731 * QString getSaveFileName (QWidget * parent = 0, const QString & caption =
1732 * QString(), const QString & dir = QString(), const QString & filter =
1733 * QString(), QString * selectedFilter = 0, Options options = 0)
1734 *
1735 * KDE3:
1736 * QString KFileDialog::getSaveFileName ( const QString & startDir =
1737 * QString::null,
1738 * const QString & filter = QString::null,
1739 * QWidget * parent = 0,
1740 * const QString & caption = QString::null
1741 * ) [static]
1742 *
1743 */
1744
1745 QString dir = ResourceFinder().getResourceSaveDir("library");
1746
1747 QString name =
1748 FileDialog::getSaveFileName(this,
1749 tr("Export Device as..."),
1750 dir,
1751 (extension.isEmpty() ? QString("*") : ("*." + extension)));
1752
1753 // Check for the existence of the name
1754 if (name.isEmpty())
1755 return ;
1756
1757 // Append extension if we don't have one
1758 //
1759 if (!extension.isEmpty()) {
1760 if (!name.endsWith("." + extension)) {
1761 name += "." + extension;
1762 }
1763 }
1764
1765 QFileInfo info(name);
1766
1767 if (info.isDir()) {
1768 QMessageBox::warning(
1769 dynamic_cast<QWidget*>(this),
1770 "", /* no title */
1771 tr("You have specified a directory"),
1772 QMessageBox::Ok,
1773 QMessageBox::Ok);
1774 return ;
1775 }
1776
1777 if (info.exists()) {
1778 int overwrite = QMessageBox::question(
1779 dynamic_cast<QWidget*>(this),
1780 "", /* no title */
1781 tr("The specified file exists. Overwrite?"),
1782 QMessageBox::Yes | QMessageBox::No,
1783 QMessageBox::No);
1784
1785 if (overwrite != QMessageBox::Yes)
1786 return ;
1787 }
1788
1789 MidiDeviceTreeWidgetItem* deviceItem =
1790 dynamic_cast<MidiDeviceTreeWidgetItem*>
1791 (m_treeWidget->currentItem());
1792
1793 std::vector<DeviceId> devices;
1794 MidiDevice *md = getMidiDevice(deviceItem);
1795
1796 if (md) {
1797 ExportDeviceDialog *ed = new ExportDeviceDialog
1798 (this, strtoqstr(md->getName()));
1799 if (ed->exec() != QDialog::Accepted)
1800 return ;
1801 if (ed->getExportType() == ExportDeviceDialog::ExportOne) {
1802 devices.push_back(md->getId());
1803 }
1804 }
1805
1806 QString errMsg;
1807 if (!m_doc->exportStudio(name, errMsg, devices)) {
1808 if (errMsg != "") {
1809 QMessageBox::critical
1810 (nullptr, tr("Rosegarden"), tr(QString("Could not export studio to file at %1\n(%2)")
1811 .arg(name).arg(errMsg).toStdString().c_str()));
1812 } else {
1813 QMessageBox::critical
1814 (nullptr, tr("Rosegarden"), tr(QString("Could not export studio to file at %1")
1815 .arg(name).toStdString().c_str()));
1816 }
1817 }
1818 }
1819
1820 void
slotFileClose()1821 BankEditorDialog::slotFileClose()
1822 {
1823 RG_DEBUG << "BankEditorDialog::slotFileClose()\n";
1824
1825 // We need to do this because we might be here due to a
1826 // documentAboutToChange signal, in which case the document won't
1827 // be valid by the time we reach the dtor, since it will be
1828 // triggered when the closeEvent is actually processed.
1829 //
1830 // CommandHistory::getInstance()->detachView(actionCollection()); //&&&
1831 m_doc = nullptr;
1832 close();
1833 }
1834
1835 void
closeEvent(QCloseEvent * e)1836 BankEditorDialog::closeEvent(QCloseEvent *e)
1837 {
1838 emit closing();
1839 QMainWindow::closeEvent(e);
1840 }
1841
1842
1843 void
slotHelpRequested()1844 BankEditorDialog::slotHelpRequested()
1845 {
1846 // TRANSLATORS: if the manual is translated into your language, you can
1847 // change the two-letter language code in this URL to point to your language
1848 // version, eg. "http://rosegardenmusic.com/wiki/doc:bankEditorDialog-es" for the
1849 // Spanish version. If your language doesn't yet have a translation, feel
1850 // free to create one.
1851 QString helpURL = tr("http://rosegardenmusic.com/wiki/doc:bankEditorDialog-en");
1852 QDesktopServices::openUrl(QUrl(helpURL));
1853 }
1854
1855 void
slotHelpAbout()1856 BankEditorDialog::slotHelpAbout()
1857 {
1858 new AboutDialog(this);
1859 }
1860 }
1861