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
19 #include "ModifyDeviceCommand.h"
20
21 #include "base/Device.h"
22 #include "base/MidiDevice.h"
23 #include "base/Studio.h"
24 #include "gui/application/RosegardenMainWindow.h"
25
26 #include <QString>
27 #include <iostream>
28
29
30 namespace Rosegarden
31 {
32
ModifyDeviceCommand(Studio * studio,DeviceId device,const std::string & name,const std::string & librarianName,const std::string & librarianEmail)33 ModifyDeviceCommand::ModifyDeviceCommand(
34 Studio *studio,
35 DeviceId device,
36 const std::string &name,
37 const std::string &librarianName,
38 const std::string &librarianEmail) :
39 NamedCommand(getGlobalName()),
40 m_studio(studio),
41 m_device(device),
42 m_name(name),
43 m_librarianName(librarianName),
44 m_librarianEmail(librarianEmail),
45 m_overwrite(true),
46 m_rename(true),
47 m_changeVariation(false),
48 m_changeBanks(false),
49 m_changePrograms(false),
50 m_changeControls(false),
51 m_changeKeyMappings(false),
52 m_clearBankAndProgramList(false)
53 {}
54
setVariation(MidiDevice::VariationType variationType)55 void ModifyDeviceCommand::setVariation(MidiDevice::VariationType variationType)
56 {
57 m_variationType = variationType;
58 m_changeVariation = true;
59 }
60
setBankList(const BankList & bankList)61 void ModifyDeviceCommand::setBankList(const BankList &bankList)
62 {
63 m_bankList = bankList;
64 m_changeBanks = true;
65 }
66
setProgramList(const ProgramList & programList)67 void ModifyDeviceCommand::setProgramList(const ProgramList &programList)
68 {
69 m_programList = programList;
70 m_changePrograms = true;
71 }
72
setControlList(const ControlList & controlList)73 void ModifyDeviceCommand::setControlList(const ControlList &controlList)
74 {
75 m_controlList = controlList;
76 m_changeControls = true;
77 }
78
setKeyMappingList(const KeyMappingList & keyMappingList)79 void ModifyDeviceCommand::setKeyMappingList(const KeyMappingList &keyMappingList)
80 {
81 m_keyMappingList = keyMappingList;
82 m_changeKeyMappings = true;
83 }
84
85 void
execute()86 ModifyDeviceCommand::execute()
87 {
88 Device *device = m_studio->getDevice(m_device);
89 if (!device) {
90 std::cerr << "ERROR: ModifyDeviceCommand::execute(): no such device as " << m_device << std::endl;
91 return;
92 }
93
94 MidiDevice *midiDevice = dynamic_cast<MidiDevice *>(device);
95 if (!midiDevice) {
96 std::cerr << "ERROR: ModifyDeviceCommand::execute(): device " << m_device << " is not a MIDI device" << std::endl;
97 return;
98 }
99
100 // Save Original Values for Undo
101
102 // ??? Really wish we could just m_oldDevice = *(midiDevice). See below.
103 m_oldName = midiDevice->getName();
104 m_oldBankList = midiDevice->getBanks();
105 m_oldProgramList = midiDevice->getPrograms();
106 m_oldControlList = midiDevice->getControlParameters();
107 m_oldKeyMappingList = midiDevice->getKeyMappings();
108 m_oldLibrarianName = midiDevice->getLibrarianName();
109 m_oldLibrarianEmail = midiDevice->getLibrarianEmail();
110 m_oldVariationType = midiDevice->getVariationType();
111 InstrumentList instruments = midiDevice->getAllInstruments();
112 for (size_t i = 0; i < instruments.size(); ++i) {
113 // ??? Preserving just the programs isn't enough. We need
114 // to preserve the rest of the Instrument as well. However,
115 // the auto/fixed channel feature has made it impossible
116 // to safely make copies of Instrument objects. Also, Instrument
117 // has an ID. How should that be handled for undo? ISTM
118 // that we either need to introduce some sort of copyForUndo()
119 // hack to each object, or develop a set of standards for coding
120 // objects that are undo-safe. Sounds like a pretty big project.
121 m_oldInstrumentPrograms.push_back(instruments[i]->getProgram());
122 }
123
124 // Make the Changes
125
126 if (m_changeVariation)
127 midiDevice->setVariationType(m_variationType);
128
129 if (m_overwrite) {
130 if (m_clearBankAndProgramList) {
131 midiDevice->clearBankList();
132 midiDevice->clearProgramList();
133 midiDevice->clearKeyMappingList();
134 } else {
135 if (m_changeBanks)
136 midiDevice->replaceBankList(m_bankList);
137 if (m_changePrograms)
138 midiDevice->replaceProgramList(m_programList);
139 if (m_changeBanks || m_changePrograms) {
140 // Make sure the instruments make sense.
141 for (size_t i = 0; i < instruments.size(); ++i) {
142 instruments[i]->pickFirstProgram(
143 midiDevice->isPercussionNumber(i));
144 instruments[i]->sendChannelSetup();
145 }
146 }
147 }
148
149 if (m_changeKeyMappings) {
150 midiDevice->replaceKeyMappingList(m_keyMappingList);
151 }
152
153 if (m_rename)
154 midiDevice->setName(m_name);
155 midiDevice->setLibrarian(m_librarianName, m_librarianEmail);
156 } else {
157 if (m_clearBankAndProgramList) {
158 midiDevice->clearBankList();
159 midiDevice->clearProgramList();
160 } else {
161 if (m_changeBanks)
162 midiDevice->mergeBankList(m_bankList);
163 if (m_changePrograms)
164 midiDevice->mergeProgramList(m_programList);
165 }
166
167 if (m_changeKeyMappings) {
168 midiDevice->mergeKeyMappingList(m_keyMappingList);
169 }
170
171 if (m_rename) {
172 std::string mergeName = midiDevice->getName() +
173 std::string("/") + m_name;
174 midiDevice->setName(mergeName);
175 }
176 }
177
178 //!!! merge option?
179 if (m_changeControls) {
180 midiDevice->replaceControlParameters(m_controlList);
181 }
182
183 // ??? Instead of this kludge, we should be calling a Studio::hasChanged()
184 // which would then notify all observers (e.g. MIPP) who, in turn,
185 // would update themselves.
186 RosegardenMainWindow::self()->uiUpdateKludge();
187 }
188
189 void
unexecute()190 ModifyDeviceCommand::unexecute()
191 {
192 Device *device = m_studio->getDevice(m_device);
193 if (!device) {
194 std::cerr << "ERROR: ModifyDeviceCommand::unexecute(): no such device as " << m_device << std::endl;
195 return;
196 }
197
198 MidiDevice *midiDevice = dynamic_cast<MidiDevice *>(device);
199 if (!midiDevice) {
200 std::cerr << "ERROR: ModifyDeviceCommand::unexecute(): device " << m_device << " is not a MIDI device" << std::endl;
201 return;
202 }
203
204 if (m_rename)
205 midiDevice->setName(m_oldName);
206 midiDevice->replaceBankList(m_oldBankList);
207 midiDevice->replaceProgramList(m_oldProgramList);
208 midiDevice->replaceControlParameters(m_oldControlList);
209 midiDevice->replaceKeyMappingList(m_oldKeyMappingList);
210 midiDevice->setLibrarian(m_oldLibrarianName, m_oldLibrarianEmail);
211 if (m_changeVariation)
212 midiDevice->setVariationType(m_oldVariationType);
213
214 InstrumentList instruments = midiDevice->getAllInstruments();
215 for (size_t i = 0; i < instruments.size(); ++i) {
216 instruments[i]->setProgram(m_oldInstrumentPrograms[i]);
217 instruments[i]->sendChannelSetup();
218 }
219
220 // ??? Instead of this kludge, we should be calling a Studio::hasChanged()
221 // which would then notify all observers (e.g. MIPP) who, in turn,
222 // would update themselves.
223 RosegardenMainWindow::self()->uiUpdateKludge();
224 }
225
226 }
227