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