/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Rosegarden A MIDI and audio sequencer and musical notation editor. Copyright 2000-2011 the Rosegarden development team. This file is Copyright 2006 Pedro Lopez-Cabanillas D. Michael McIntyre Other copyrights also apply to some parts of this work. Please see the AUTHORS file and individual file headers for details. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "TrackParameterBox.h" #include "gui/widgets/SqueezedLabel.h" #include "misc/Debug.h" #include "misc/Strings.h" #include "gui/general/ClefIndex.h" #include "misc/ConfigGroups.h" #include "base/AudioPluginInstance.h" #include "base/Colour.h" #include "base/ColourMap.h" #include "base/Composition.h" #include "base/Device.h" #include "base/Exception.h" #include "base/Instrument.h" #include "base/MidiDevice.h" #include "base/MidiProgram.h" #include "base/NotationTypes.h" #include "base/Studio.h" #include "base/Track.h" #include "base/StaffExportTypes.h" #include "gui/widgets/TmpStatusMsg.h" #include "commands/segment/SegmentSyncCommand.h" #include "document/RosegardenDocument.h" #include "gui/dialogs/PitchPickerDialog.h" #include "gui/general/GUIPalette.h" #include "gui/general/PresetHandlerDialog.h" #include "gui/widgets/CollapsingFrame.h" #include "gui/widgets/ColourTable.h" #include "gui/general/GUIPalette.h" #include "RosegardenParameterArea.h" #include "RosegardenParameterBox.h" #include "sound/PluginIdentifier.h" #include "gui/widgets/LineEdit.h" #include "gui/widgets/InputDialog.h" #include "misc/Debug.h" #include "sequencer/RosegardenSequencer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Rosegarden { TrackParameterBox::TrackParameterBox(RosegardenDocument *doc, QWidget *parent) : RosegardenParameterBox(tr("Track"), tr("Track Parameters"), parent), m_doc(doc), m_highestPlayable(127), m_lowestPlayable(0), m_lastInstrumentType(-1), m_selectedTrackId((int)NO_TRACK) { setObjectName("Track Parameter Box"); QFont font(m_font); QFont title_font(m_font); QFontMetrics metrics(font); int width11 = metrics.width("12345678901"); int width20 = metrics.width("12345678901234567890"); int width22 = metrics.width("1234567890123456789012"); int width25 = metrics.width("1234567890123456789012345"); title_font.setBold(true); // Set up default expansions for the collapsing elements. These // CollapsingFrame objects keep track of their own settings per object name // internally. We have this delightful bit of nonsense looking code to // "initialize" all of this from the outside. It's impressively ghastly, // but having realized its purpose, I've decided to leave well enough alone. // QSettings settings; settings.beginGroup(CollapsingFrameConfigGroup); bool expanded = qStrToBool(settings.value("trackparametersplayback", "true")) ; settings.setValue("trackparametersplayback", expanded); expanded = qStrToBool(settings.value("trackparametersrecord", "false")) ; settings.setValue("trackparametersrecord", expanded); expanded = qStrToBool(settings.value("trackparametersdefaults", "false")) ; settings.setValue("trackparametersdefaults", expanded); expanded = qStrToBool(settings.value("trackstaffgroup", "false")) ; settings.setValue("trackstaffgroup", expanded); settings.endGroup(); setContentsMargins(2, 2, 2, 2); QGridLayout *mainLayout = new QGridLayout(this); mainLayout->setMargin(0); mainLayout->setSpacing(1); int row = 0; // track label // m_trackLabel = new SqueezedLabel (tr(""), this); m_trackLabel->setAlignment(Qt::AlignCenter); m_trackLabel->setFont(m_font); mainLayout->addWidget(m_trackLabel, 0, 0); // playback group // CollapsingFrame *cframe = new CollapsingFrame(tr("Playback parameters"), this, "trackparametersplayback"); m_playbackGroup = new QFrame(cframe); cframe->setWidget(m_playbackGroup); m_playbackGroup->setContentsMargins(3, 3, 3, 3); QGridLayout *groupLayout = new QGridLayout(m_playbackGroup); groupLayout->setMargin(0); groupLayout->setSpacing(2); // playback group title // row = 0; // playback device // // row++; QLabel *devLabel = new QLabel(tr("Device"), m_playbackGroup); devLabel->setFont(m_font); groupLayout->addWidget(devLabel, row, 0); m_playDevice = new QComboBox(m_playbackGroup); m_playDevice->setToolTip(tr("

Choose the device this track will use for playback.

Click to connect this device to a useful output if you do not hear sound

")); m_playDevice->setMinimumWidth(width25); m_playDevice->setFont(m_font); groupLayout->addWidget(m_playDevice, row, 1, row- row+1, 2); // playback instrument // row++; QLabel *insLabel = new QLabel(tr("Instrument"), m_playbackGroup); insLabel->setFont(m_font); groupLayout->addWidget(insLabel, row, 0, row- row+1, 1- 0+1); m_instrument = new QComboBox(m_playbackGroup); m_instrument->setFont(m_font); m_instrument->setToolTip(tr("

Choose the instrument this track will use for playback. (Configure the instrument in Instrument Parameters).

")); m_instrument->setMaxVisibleItems(16); m_instrument->setMinimumWidth(width22); groupLayout->addWidget(m_instrument, row, 2); groupLayout->setColumnStretch(groupLayout->columnCount() - 1, 1); mainLayout->addWidget(cframe, 1, 0); // record group // cframe = new CollapsingFrame(tr("Recording filters"), this, "trackparametersrecord"); m_recordGroup = new QFrame(cframe); cframe->setWidget(m_recordGroup); m_recordGroup->setContentsMargins(3, 3, 3, 3); groupLayout = new QGridLayout(m_recordGroup); groupLayout->setMargin(0); groupLayout->setSpacing(2); // recording group title // row = 0; // recording device QLabel *label = new QLabel(tr("Device"), m_recordGroup); label->setFont(m_font); groupLayout->addWidget(label, row, 0); m_recDevice = new QComboBox(m_recordGroup); m_recDevice->setFont(m_font); m_recDevice->setToolTip(tr("

This track will only record Audio/MIDI from the selected device, filtering anything else out

")); m_recDevice->setMinimumWidth(width25); groupLayout->addWidget(m_recDevice, row, 1, row- row+1, 2); // recording channel // row++; label = new QLabel(tr("Channel"), m_recordGroup); label->setFont(font); groupLayout->addWidget(label, row, 0, 1, 2); m_recChannel = new QComboBox(m_recordGroup); m_recChannel->setFont(m_font); m_recChannel->setToolTip(tr("

This track will only record Audio/MIDI from the selected channel, filtering anything else out

")); m_recChannel->setMaxVisibleItems(17); m_recChannel->setMinimumWidth(width11); groupLayout->addWidget(m_recChannel, row, 2); groupLayout->setColumnStretch(groupLayout->columnCount() - 1, 1); mainLayout->addWidget(cframe, 2, 0); // staff group // cframe = new CollapsingFrame(tr("Staff export options"), this, "staffoptions"); m_staffGroup = new QFrame(cframe); cframe->setWidget(m_staffGroup); m_staffGroup->setContentsMargins(2, 2, 2, 2); groupLayout = new QGridLayout(m_staffGroup); groupLayout->setSpacing(2); groupLayout->setMargin(0); groupLayout->setColumnStretch(1, 1); row = 0; // Notation size (export only) // // NOTE: This is the only way to get a \small or \tiny inserted before the // first note in LilyPond export. Setting the actual staff size on a // per-staff (rather than per-score) basis is something the author of the // LilyPond documentation has no idea how to do, so we settle for this, // which is not as nice, but actually a lot easier to implement. m_staffGrpLbl = new QLabel(tr("Notation size:"), m_staffGroup); m_staffGrpLbl->setFont(m_font); groupLayout->addWidget(m_staffGrpLbl, row, 0, Qt::AlignLeft); m_staffSizeCombo = new QComboBox(m_staffGroup); m_staffSizeCombo->setFont(m_font); m_staffSizeCombo->setToolTip(tr("

Choose normal, \\small or \\tiny font size for notation elements on this (normal-sized) staff when exporting to LilyPond.

This is as close as we get to enabling you to print parts in cue size

")); m_staffSizeCombo->setMinimumWidth(width11); m_staffSizeCombo->addItem(tr("Normal"), StaffTypes::Normal); m_staffSizeCombo->addItem(tr("Small"), StaffTypes::Small); m_staffSizeCombo->addItem(tr("Tiny"), StaffTypes::Tiny); groupLayout->addWidget(m_staffSizeCombo, row, 1, row- row+1, 2); // Staff bracketing (export only at the moment, but using this for GUI // rendering would be nice in the future!) //!!! row++; m_grandStaffLbl = new QLabel(tr("Bracket type:"), m_staffGroup); m_grandStaffLbl->setFont(m_font); groupLayout->addWidget(m_grandStaffLbl, row, 0, Qt::AlignLeft); m_staffBracketCombo = new QComboBox(m_staffGroup); m_staffBracketCombo->setFont(m_font); m_staffBracketCombo->setToolTip(tr("

Bracket staffs in LilyPond
(fragile, use with caution)

")); m_staffBracketCombo->setMinimumWidth(width11); m_staffBracketCombo->addItem(tr("-----"), Brackets::None); m_staffBracketCombo->addItem(tr("[----"), Brackets::SquareOn); m_staffBracketCombo->addItem(tr("----]"), Brackets::SquareOff); m_staffBracketCombo->addItem(tr("[---]"), Brackets::SquareOnOff); m_staffBracketCombo->addItem(tr("{----"), Brackets::CurlyOn); m_staffBracketCombo->addItem(tr("----}"), Brackets::CurlyOff); m_staffBracketCombo->addItem(tr("{[---"), Brackets::CurlySquareOn); m_staffBracketCombo->addItem(tr("---]}"), Brackets::CurlySquareOff); groupLayout->addWidget(m_staffBracketCombo, row, 1, row- row+1, 2); mainLayout->addWidget(cframe, 3, 0); // default segment group // cframe = new CollapsingFrame(tr("Create segments with"), this, "trackparametersdefaults"); m_defaultsGroup = new QFrame(cframe); cframe->setWidget(m_defaultsGroup); m_defaultsGroup->setContentsMargins(3, 3, 3, 3); groupLayout = new QGridLayout(m_defaultsGroup); groupLayout->setSpacing(2); groupLayout->setMargin(0); groupLayout->setColumnStretch(1, 1); row = 0; // preset picker m_psetLbl = new QLabel(tr("Preset"), m_defaultsGroup); m_psetLbl->setFont(m_font); groupLayout->addWidget(m_psetLbl, row, 0, Qt::AlignLeft); m_presetLbl = new QLabel(tr(""), m_defaultsGroup); m_presetLbl->setFont(m_font); m_presetLbl->setObjectName("SPECIAL_LABEL"); m_presetLbl->setFrameStyle(QFrame::Panel | QFrame::Sunken); m_presetLbl->setMinimumWidth(width20); groupLayout->addWidget(m_presetLbl, row, 1, 1, 3); m_presetButton = new QPushButton(tr("Load"), m_defaultsGroup); m_presetButton->setFont(m_font); m_presetButton->setToolTip(tr("

Load a segment parameters preset from our comprehensive database of real-world instruments.

When you create new segments, they will have these parameters at the moment of creation. To use these parameters on existing segments (eg. to convert an existing part in concert pitch for playback on a Bb trumpet) use Segments -> Convert notation for in the notation editor.

")); groupLayout->addWidget(m_presetButton, row, 4, 1, 2); // default clef // row++; m_clefLbl = new QLabel(tr("Clef"), m_defaultsGroup); m_clefLbl->setFont(m_font); groupLayout->addWidget(m_clefLbl, row, 0, Qt::AlignLeft); m_defClef = new QComboBox(m_defaultsGroup); m_defClef->setFont(m_font); m_defClef->setToolTip(tr("

New segments will be created with this clef inserted at the beginning

")); m_defClef->setMinimumWidth(width11); m_defClef->addItem(tr("treble"), TrebleClef); m_defClef->addItem(tr("bass"), BassClef); m_defClef->addItem(tr("crotales"), CrotalesClef); m_defClef->addItem(tr("xylophone"), XylophoneClef); m_defClef->addItem(tr("guitar"), GuitarClef); m_defClef->addItem(tr("contrabass"), ContrabassClef); m_defClef->addItem(tr("celesta"), CelestaClef); m_defClef->addItem(tr("old celesta"), OldCelestaClef); m_defClef->addItem(tr("french"), FrenchClef); m_defClef->addItem(tr("soprano"), SopranoClef); m_defClef->addItem(tr("mezzosoprano"), MezzosopranoClef); m_defClef->addItem(tr("alto"), AltoClef); m_defClef->addItem(tr("tenor"), TenorClef); m_defClef->addItem(tr("baritone"), BaritoneClef); m_defClef->addItem(tr("varbaritone"), VarbaritoneClef); m_defClef->addItem(tr("subbass"), SubbassClef); /* clef types in the datbase that are not yet supported must be ignored for * now: m_defClef->addItem(tr("two bar"), TwoBarClef); */ groupLayout->addWidget(m_defClef, row, 1, 1, 2); // default transpose // m_transpLbl = new QLabel(tr("Transpose"), m_defaultsGroup); m_transpLbl->setFont(m_font); groupLayout->addWidget(m_transpLbl, row, 3, 1, 2, Qt::AlignRight); m_defTranspose = new QComboBox(m_defaultsGroup); m_defTranspose->setFont(m_font); m_defTranspose->setToolTip(tr("

New segments will be created with this transpose property set

")); connect(m_defTranspose, SIGNAL(activated(int)), SLOT(slotTransposeIndexChanged(int))); int transposeRange = 48; for (int i = -transposeRange; i < transposeRange + 1; i++) { m_defTranspose->addItem(QString("%1").arg(i)); if (i == 0) m_defTranspose->setCurrentIndex(m_defTranspose->count() - 1); } groupLayout->addWidget(m_defTranspose, row, 5, 1, 1); // highest/lowest playable note // row++; m_rangeLbl = new QLabel(tr("Pitch"), m_defaultsGroup); m_rangeLbl->setFont(m_font); groupLayout->addWidget(m_rangeLbl, row, 0, Qt::AlignLeft); label = new QLabel(tr("Lowest"), m_defaultsGroup); label->setFont(m_font); groupLayout->addWidget(label, row, 1, Qt::AlignRight); m_lowButton = new QPushButton(tr("---"), m_defaultsGroup); m_lowButton->setFont(m_font); m_lowButton->setToolTip(tr("

Choose the lowest suggested playable note, using a staff

")); groupLayout->addWidget(m_lowButton, row, 2, 1, 1); groupLayout->setColumnStretch(2, 2); label = new QLabel(tr("Highest"), m_defaultsGroup); label->setFont(m_font); groupLayout->addWidget(label, row, 3, Qt::AlignRight); m_highButton = new QPushButton(tr("---"), m_defaultsGroup); m_highButton->setFont(m_font); m_highButton->setToolTip(tr("

Choose the highest suggested playable note, using a staff

")); groupLayout->addWidget(m_highButton, row, 4, 1, 2); // m_lowButton->setMaximumWidth(width11); // m_highButton->setMaximumWidth(width11); updateHighLow(); // default color // row++; m_colorLbl = new QLabel(tr("Color"), m_defaultsGroup); m_colorLbl->setFont(m_font); groupLayout->addWidget(m_colorLbl, row, 0, Qt::AlignLeft); m_defColor = new QComboBox(m_defaultsGroup); m_defColor->setFont(m_font); m_defColor->setToolTip(tr("

New segments will be created using this color

")); m_defColor->setEditable(false); m_defColor->setMaxVisibleItems(20); groupLayout->addWidget(m_defColor, row, 1, 1, 5); // populate combo from doc colors slotDocColoursChanged(); // Force a popluation of Record / Playback Devices (Playback was not populating). slotPopulateDeviceLists(); mainLayout->addWidget(cframe, 4, 0); // Configure the empty final row to accomodate any extra vertical space. // // mainLayout->setRowStretch(mainLayout->rowCount() - 1, 1); // Connections connect(m_playDevice, SIGNAL(activated(int)), this, SLOT(slotPlaybackDeviceChanged(int))); connect(m_instrument, SIGNAL(activated(int)), this, SLOT(slotInstrumentChanged(int))); connect(m_recDevice, SIGNAL(activated(int)), this, SLOT(slotRecordingDeviceChanged(int))); connect(m_recChannel, SIGNAL(activated(int)), this, SLOT(slotRecordingChannelChanged(int))); connect(m_defClef, SIGNAL(activated(int)), this, SLOT(slotClefChanged(int))); // Detect when the document colours are updated connect(m_doc, SIGNAL(docColoursChanged()), this, SLOT(slotDocColoursChanged())); // handle colour combo changes connect(m_defColor, SIGNAL(activated(int)), SLOT(slotColorChanged(int))); connect(m_highButton, SIGNAL(released()), SLOT(slotHighestPressed())); connect(m_lowButton, SIGNAL(released()), SLOT(slotLowestPressed())); connect(m_presetButton, SIGNAL(released()), SLOT(slotPresetPressed())); connect(m_staffSizeCombo, SIGNAL(activated(int)), this, SLOT(slotStaffSizeChanged(int))); connect(m_staffBracketCombo, SIGNAL(activated(int)), this, SLOT(slotStaffBracketChanged(int))); m_doc->getComposition().addObserver(this); slotUpdateControls(-1); } TrackParameterBox::~TrackParameterBox() { } void TrackParameterBox::setDocument(RosegardenDocument *doc) { if (m_doc != doc) { RG_DEBUG << "TrackParameterBox::setDocument\n"; m_doc = doc; slotPopulateDeviceLists(); } } void TrackParameterBox::slotPopulateDeviceLists() { RG_DEBUG << "TrackParameterBox::slotPopulateDeviceLists()\n"; populatePlaybackDeviceList(); m_lastInstrumentType = -1; // Attempt to force initial record device populate populateRecordingDeviceList(); slotUpdateControls(-1); } void TrackParameterBox::populatePlaybackDeviceList() { RG_DEBUG << "TrackParameterBox::populatePlaybackDeviceList()\n"; m_playDevice->clear(); m_playDeviceIds.clear(); m_instrument->clear(); m_instrumentIds.clear(); m_instrumentNames.clear(); Studio &studio = m_doc->getStudio(); // Get the list InstrumentList list = studio.getPresentationInstruments(); InstrumentList::iterator it; int currentDevId = -1; for (it = list.begin(); it != list.end(); it++) { if (! (*it)) continue; // sanity check QString iname(QObject::tr(QString::fromStdString((*it)->getName()))); QString pname(QObject::tr(strtoqstr((*it)->getProgramName()))); Device *device = (*it)->getDevice(); DeviceId devId = device->getId(); if ((*it)->getType() == Instrument::SoftSynth) { iname.replace(QObject::tr("Synth plugin"), ""); pname = ""; AudioPluginInstance *plugin = (*it)->getPlugin (Instrument::SYNTH_PLUGIN_POSITION); if (plugin) { pname = strtoqstr(plugin->getProgram()); QString identifier = strtoqstr(plugin->getIdentifier()); if (identifier != "") { QString type, soName, label; PluginIdentifier::parseIdentifier (identifier, type, soName, label); if (pname == "") { pname = strtoqstr(plugin->getDistinctiveConfigurationText()); } if (pname != "") { pname = QString("%1: %2").arg(label).arg(pname); } else { pname = label; } } } } if (devId != (DeviceId)(currentDevId)) { currentDevId = int(devId); QString deviceName = QObject::tr(strtoqstr(device->getName())); m_playDevice->addItem(deviceName); m_playDeviceIds.push_back(currentDevId); } if (pname != "") iname += " (" + pname + ")"; // cut off the redundant eg. "General MIDI Device" that appears in the // combo right above here anyway iname = iname.mid(iname.indexOf("#"), iname.length()); m_instrumentIds[currentDevId].push_back((*it)->getId()); m_instrumentNames[currentDevId].append(iname); } m_playDevice->setCurrentIndex(-1); m_instrument->setCurrentIndex(-1); } void TrackParameterBox::populateRecordingDeviceList() { RG_DEBUG << "TrackParameterBox::populateRecordingDeviceList()\n"; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); Instrument *inst = m_doc->getStudio().getInstrumentFor(trk); if (!inst) return ; if (m_lastInstrumentType != (char)inst->getInstrumentType()) { m_lastInstrumentType = (char)inst->getInstrumentType(); m_recDevice->clear(); m_recDeviceIds.clear(); m_recChannel->clear(); if (inst->getInstrumentType() == Instrument::Audio) { m_recDeviceIds.push_back(Device::NO_DEVICE); m_recDevice->addItem(tr("Audio")); m_recChannel->addItem(tr("Audio")); m_recDevice->setEnabled(false); m_recChannel->setEnabled(false); // hide these for audio instruments m_defaultsGroup->parentWidget()->setShown(false); } else { // InstrumentType::Midi and InstrumentType::SoftSynth // show these if not audio instrument m_defaultsGroup->parentWidget()->setShown(true); m_recDeviceIds.push_back(Device::ALL_DEVICES); m_recDevice->addItem(tr("All")); DeviceList *devices = m_doc->getStudio().getDevices(); DeviceListConstIterator it; for (it = devices->begin(); it != devices->end(); it++) { MidiDevice *dev = dynamic_cast(*it); if (dev) { if (dev->getDirection() == MidiDevice::Record && dev->isRecording()) { QString deviceName = QObject::tr(strtoqstr(dev->getName())); m_recDevice->addItem(deviceName); m_recDeviceIds.push_back(dev->getId()); } } } m_recChannel->addItem(tr("All")); for (int i = 1; i < 17; ++i) { m_recChannel->addItem(QString::number(i)); } m_recDevice->setEnabled(true); m_recChannel->setEnabled(true); } } if (inst->getInstrumentType() == Instrument::Audio) { m_recDevice->setCurrentIndex(0); m_recChannel->setCurrentIndex(0); } else { m_recDevice->setCurrentIndex(0); m_recChannel->setCurrentIndex((int)trk->getMidiInputChannel() + 1); for (unsigned int i = 0; i < m_recDeviceIds.size(); ++i) { if (m_recDeviceIds[i] == trk->getMidiInputDevice()) { m_recDevice->setCurrentIndex(i); break; } } } } void TrackParameterBox::updateHighLow() { Composition &comp = m_doc->getComposition(); Track *trk = comp.getTrackById(comp.getSelectedTrack()); if (!trk) return ; trk->setHighestPlayable(m_highestPlayable); trk->setLowestPlayable(m_lowestPlayable); Accidental accidental = Accidentals::NoAccidental; Pitch highest(m_highestPlayable, accidental); Pitch lowest(m_lowestPlayable, accidental); QSettings settings; settings.beginGroup(GeneralOptionsConfigGroup); int base = settings.value("midipitchoctave", -2).toInt() ; settings.endGroup(); bool includeOctave = false; // NOTE: this now uses a new, overloaded version of Pitch::getAsString() // that explicitly works with the key of C major, and does not allow the // calling code to specify how the accidentals should be written out. // // Separate the note letter from the octave to avoid undue burden on // translators having to retranslate the same thing but for a number // difference QString tmp = QObject::tr(strtoqstr(highest.getAsString(includeOctave, base)), "note name"); tmp += tr(" %1").arg(highest.getOctave(base)); m_highButton->setText(tmp); tmp = QObject::tr(strtoqstr(lowest.getAsString(includeOctave, base)), "note name"); tmp += tr(" %1").arg(lowest.getOctave(base)); m_lowButton->setText(tmp); m_presetLbl->setEnabled(false); } void TrackParameterBox::slotUpdateControls(int /*dummy*/) { RG_DEBUG << "TrackParameterBox::slotUpdateControls()\n"; slotPlaybackDeviceChanged(-1); slotInstrumentChanged(-1); if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); m_defClef->setCurrentIndex(trk->getClef()); m_defTranspose->setCurrentText(QString("%1").arg(trk->getTranspose())); m_defColor->setCurrentIndex(trk->getColor()); m_highestPlayable = trk->getHighestPlayable(); m_lowestPlayable = trk->getLowestPlayable(); updateHighLow(); // set this down here because updateHighLow just disabled the label m_presetLbl->setText(strtoqstr(trk->getPresetLabel())); m_presetLbl->setEnabled(true); m_staffSizeCombo->setCurrentIndex(trk->getStaffSize()); m_staffBracketCombo->setCurrentIndex(trk->getStaffBracket()); } void TrackParameterBox::trackDeleted(const Composition *comp, TrackId id) { std::cerr << "TrackParameterBox::trackDeleted(" << id << "), selected is " << m_selectedTrackId << std::endl; if ((int)id == m_selectedTrackId) slotSelectedTrackChanged(); } void TrackParameterBox::slotSelectedTrackChanged() { RG_DEBUG << "TrackParameterBox::slotSelectedTrackChanged()\n"; Composition &comp = m_doc->getComposition(); TrackId newTrack = comp.getSelectedTrack(); if ((int)newTrack != m_selectedTrackId) { m_presetLbl->setEnabled(true); m_selectedTrackId = newTrack; slotSelectedTrackNameChanged(); slotUpdateControls(-1); } } void TrackParameterBox::slotSelectedTrackNameChanged() { RG_DEBUG << "TrackParameterBox::sotSelectedTrackNameChanged()\n"; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); QString m_trackName = strtoqstr(trk->getLabel()); if (m_trackName.isEmpty()) m_trackName = tr(""); else m_trackName.truncate(20); int trackNum = trk->getPosition() + 1; m_trackLabel->setText(tr("[ Track %1 - %2 ]").arg(trackNum).arg(m_trackName)); } void TrackParameterBox::slotPlaybackDeviceChanged(int index) { RG_DEBUG << "TrackParameterBox::slotPlaybackDeviceChanged(" << index << ")\n"; DeviceId devId; if (index == -1) { if (m_selectedTrackId == (int)NO_TRACK) return ; Composition &comp = m_doc->getComposition(); Track *trk = comp.getTrackById(m_selectedTrackId); if (!trk) return ; Instrument *inst = m_doc->getStudio().getInstrumentFor(trk); if (!inst) return ; devId = inst->getDevice()->getId(); int pos = -1; IdsVector::const_iterator it; // this works because we don't have a usable index, and we're having to // figure out what to set to. the external change was to 10001, so we // hunt through our mangled data structure to find that. this jibes // with the notion that our own representation of what's what does not // remotely match the TB menu representation. Our data is the same, but // in a completely different and utterly nonsensical order, so we can // find it accurately if we know what we're hunting for, otherwise, // we're fucked for (it = m_playDeviceIds.begin(); it != m_playDeviceIds.end(); ++it) { pos++; if ((*it) == devId) break; } m_playDevice->setCurrentIndex(pos); } else { devId = m_playDeviceIds[index]; } // used to be "General MIDI Device #7" now we change to "QSynth Device" and // we want to remember the #7 bit int previousIndex = m_instrument->currentIndex(); // clear the instrument combo and re-populate it from the new device m_instrument->clear(); m_instrument->addItems(m_instrumentNames[devId]); // try to keep the same index (the #7 bit) as was in use previously, unless // the new instrument has fewer indices available than the previous one did, // in which case we just go with the highest valid index available if (previousIndex > m_instrument->count()) previousIndex = m_instrument->count(); populateRecordingDeviceList(); if (index != -1) { m_instrument->setCurrentIndex(previousIndex); slotInstrumentChanged(previousIndex); } } void TrackParameterBox::slotInstrumentChanged(int index) { RG_DEBUG << "TrackParameterBox::slotInstrumentChanged(" << index << ")\n"; DeviceId devId; Instrument *inst; if (index == -1) { Composition &comp = m_doc->getComposition(); Track *trk = comp.getTrackById(comp.getSelectedTrack()); if (!trk) return ; inst = m_doc->getStudio().getInstrumentFor(trk); if (!inst) return ; devId = inst->getDevice()->getId(); int pos = -1; IdsVector::const_iterator it; for (it = m_instrumentIds[devId].begin(); it != m_instrumentIds[devId].end(); ++it) { pos++; if ((*it) == trk->getInstrument()) break; } m_instrument->setCurrentIndex(pos); } else { devId = m_playDeviceIds[m_playDevice->currentIndex()]; // Calculate an index to use in Studio::getInstrumentFromList() which // gets emitted to TrackButtons, and TrackButtons actually does the work // of assigning the track to the instrument, for some bizarre reason. // // This new method for calculating the index works by: // // 1. for every play device combo index between 0 and its current index, // // 2. get the device that corresponds with that combo box index, and // // 3. figure out how many instruments that device contains, then // // 4. Add it all up. That's how many slots we have to jump over to get // to the point where the instrument combo box index we're working // with here will target the correct instrument in the studio list. // // I'm sure this whole architecture seemed clever once, but it's an // unmaintainable pain in the ass is what it is. We changed one // assumption somewhere, and the whole thing fell on its head, // swallowing two entire days of my life to put back with the following // magic lines of code: int prepend = 0; for (unsigned int n = 0; n < m_playDevice->currentIndex(); n++) { DeviceId id = m_playDeviceIds[n]; Device *dev = m_doc->getStudio().getDevice(id); // get the number of instruments belonging to the device (not the // studio) InstrumentList il = dev->getPresentationInstruments(); prepend += il.size(); } index += prepend; // emit the index we've calculated, relative to the studio list RG_DEBUG << "TrackParameterBox::slotInstrumentChanged() index = " << index << "\n"; if (m_doc->getComposition().haveTrack(m_selectedTrackId)) { emit instrumentSelected(m_selectedTrackId, index); } } } void TrackParameterBox::slotRecordingDeviceChanged(int index) { RG_DEBUG << "TrackParameterBox::slotRecordingDeviceChanged(" << index << ")" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); Instrument *inst = m_doc->getStudio().getInstrumentFor(trk); if (!inst) return ; if (inst->getInstrumentType() == Instrument::Audio) { //Not implemented yet } else { trk->setMidiInputDevice(m_recDeviceIds[index]); } } void TrackParameterBox::slotRecordingChannelChanged(int index) { RG_DEBUG << "TrackParameterBox::slotRecordingChannelChanged(" << index << ")" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); Instrument *inst = m_doc->getStudio().getInstrumentFor(trk); if (!inst) return ; if (inst->getInstrumentType() == Instrument::Audio) { //Not implemented yet } else { trk->setMidiInputChannel(index - 1); } } void TrackParameterBox::slotInstrumentLabelChanged(InstrumentId id, QString label) { RG_DEBUG << "TrackParameterBox::slotInstrumentLabelChanged(" << id << ") = " << label << "\n"; populatePlaybackDeviceList(); slotUpdateControls(-1); } void TrackParameterBox::showAdditionalControls(bool) { // No longer needed. Remove when we strip the last of the classic vs. // tabbed garbage out of the other parameter boxes } void TrackParameterBox::slotClefChanged(int clef) { RG_DEBUG << "TrackParameterBox::slotClefChanged(" << clef << ")" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); trk->setClef(clef); m_presetLbl->setEnabled(false); } void TrackParameterBox::slotTransposeChanged(int transpose) { RG_DEBUG << "TrackParameterBox::slotTransposeChanged(" << transpose << ")" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); trk->setTranspose(transpose); m_presetLbl->setEnabled(false); } void TrackParameterBox::slotTransposeIndexChanged(int index) { slotTransposeTextChanged(m_defTranspose->text(index)); } void TrackParameterBox::slotTransposeTextChanged(QString text) { if (text.isEmpty()) return; int value = text.toInt(); slotTransposeChanged(value); } void TrackParameterBox::slotDocColoursChanged() { RG_DEBUG << "TrackParameterBox::slotDocColoursChanged()" << endl; m_defColor->clear(); m_colourList.clear(); // Populate it from composition.m_segmentColourMap ColourMap temp = m_doc->getComposition().getSegmentColourMap(); unsigned int i = 0; for (RCMap::const_iterator it = temp.begin(); it != temp.end(); ++it) { QString qtrunc(QObject::tr(strtoqstr(it->second.second))); QPixmap colour(15, 15); colour.fill(GUIPalette::convertColour(it->second.first)); if (qtrunc == "") { m_defColor->addItem(colour, tr("Default"), i); } else { // truncate name to 25 characters to avoid the combo forcing the // whole kit and kaboodle too wide (This expands from 15 because the // translators wrote books instead of copying the style of // TheShortEnglishNames, and because we have that much room to // spare.) if (qtrunc.length() > 25) qtrunc = qtrunc.left(22) + "..."; m_defColor->addItem(colour, qtrunc, i); } m_colourList[it->first] = i; // maps colour number to menu index ++i; } m_addColourPos = i; m_defColor->addItem(tr("Add New Color"), m_addColourPos); // remove the item we just inserted; this leaves the translation alone, but // eliminates the useless option // //!!! fix after release m_defColor->removeItem(m_addColourPos); m_defColor->setCurrentIndex(0); } void TrackParameterBox::slotColorChanged(int index) { RG_DEBUG << "TrackParameterBox::slotColorChanged(" << index << ")" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); trk->setColor(index); if (index == m_addColourPos) { ColourMap newMap = m_doc->getComposition().getSegmentColourMap(); QColor newColour; bool ok = false; QString newName = InputDialog::getText(this, tr("New Color Name"), tr("Enter new name:"), LineEdit::Normal, tr("New"), &ok); if ((ok == true) && (!newName.isEmpty())) { // QColorDialog box(this, "", true); // int result = box.getColor(newColour); //QRgb QColorDialog::getRgba(0xffffffff, &ok, this); QColor newColor = QColorDialog::getColor(Qt::white, this); if (newColor.isValid()) { Colour newRColour = GUIPalette::convertColour(newColour); newMap.addItem(newRColour, qstrtostr(newName)); slotDocColoursChanged(); } } // Else we don't do anything as they either didn't give a name // or didn't give a colour } } void TrackParameterBox::slotHighestPressed() { RG_DEBUG << "TrackParameterBox::slotHighestPressed()" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } PitchPickerDialog dialog(0, m_highestPlayable, tr("Highest playable note")); if (dialog.exec() == QDialog::Accepted) { m_highestPlayable = dialog.getPitch(); updateHighLow(); } m_presetLbl->setEnabled(false); } void TrackParameterBox::slotLowestPressed() { RG_DEBUG << "TrackParameterBox::slotLowestPressed()" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } PitchPickerDialog dialog(0, m_lowestPlayable, tr("Lowest playable note")); if (dialog.exec() == QDialog::Accepted) { m_lowestPlayable = dialog.getPitch(); updateHighLow(); } m_presetLbl->setEnabled(false); } void TrackParameterBox::slotPresetPressed() { RG_DEBUG << "TrackParameterBox::slotPresetPressed()" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } //PresetHandlerDialog dialog(this); PresetHandlerDialog dialog(0); // no parent means no style from group box parent, but what about popup location? Track *trk = comp.getTrackById(m_selectedTrackId); try { if (dialog.exec() == QDialog::Accepted) { m_presetLbl->setText(dialog.getName()); trk->setPresetLabel(qstrtostr(dialog.getName())); if (dialog.getConvertAllSegments()) { SegmentSyncCommand* command = new SegmentSyncCommand( comp.getSegments(), m_selectedTrackId, dialog.getTranspose(), dialog.getLowRange(), dialog.getHighRange(), clefIndexToClef(dialog.getClef())); CommandHistory::getInstance()->addCommand(command); } m_defClef->setCurrentIndex(dialog.getClef()); // m_defTranspose->setCurrentIndex(QString("%1").arg // (dialog.getTranspose()), true); m_defTranspose->setCurrentText(QString("%1").arg (dialog.getTranspose())); m_highestPlayable = dialog.getHighRange(); m_lowestPlayable = dialog.getLowRange(); updateHighLow(); slotClefChanged(dialog.getClef()); slotTransposeChanged(dialog.getTranspose()); // the preceding slots will have set this disabled, so we // re-enable it until it is subsequently re-disabled by the // user overriding the preset, calling one of the above slots // in the normal course m_presetLbl->setEnabled(true); } } catch (Exception e) { //!!! This should be a more verbose error to pass along the // row/column of the corruption, but I can't be bothered to work // that out just at the moment. Hopefully this code will never // execute anyway. QMessageBox::warning(0, tr("Rosegarden"), tr("The instrument preset database is corrupt. Check your installation.")); } } void TrackParameterBox::slotStaffSizeChanged(int index) { RG_DEBUG << "TrackParameterBox::sotStaffSizeChanged()" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); trk->setStaffSize(index); } void TrackParameterBox::slotStaffBracketChanged(int index) { RG_DEBUG << "TrackParameterBox::sotStaffBracketChanged()" << endl; if (m_selectedTrackId == (int)NO_TRACK) return; Composition &comp = m_doc->getComposition(); if (!comp.haveTrack(m_selectedTrackId)) { m_selectedTrackId = (int)NO_TRACK; return; } Track *trk = comp.getTrackById(m_selectedTrackId); trk->setStaffBracket(index); } QString TrackParameterBox::getPreviousBox(RosegardenParameterArea::Arrangement arrangement) const { if (arrangement == RosegardenParameterArea::CLASSIC_STYLE) { return tr("Segment"); } else { return ""; } } } #include "TrackParameterBox.moc"