//========================================================= // MusE // Linux Music Editor // // RoutePopupMenu.cpp // (C) Copyright 2011 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; version 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "app.h" #include "routepopup.h" #include "midiport.h" #include "mididev.h" #include "audio.h" #include "driver/audiodev.h" #include "song.h" #include "track.h" #include "synth.h" #include "route.h" #include "icons.h" #include "menutitleitem.h" #include "popupmenu.h" #include "custom_widget_actions.h" #include "globaldefs.h" #define _USE_CUSTOM_WIDGET_ACTIONS_ namespace MusEGui { //--------------------------------------------------------- // addMenuItem //--------------------------------------------------------- int RoutePopupMenu::addMenuItem(MusECore::AudioTrack* track, MusECore::Track* route_track, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { // totalInChannels is only used by syntis. //int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. //if(track->channels() == 1) // toch = 1; // Don't add the last stray mono route if the track is stereo. //if(route_track->channels() > 1 && (channel+1 == chans)) // return id; MusECore::RouteList* rl = isOutput ? track->outRoutes() : track->inRoutes(); QAction* act; QString s(route_track->name()); act = lb->addAction(s); act->setCheckable(true); int ach = channel; int bch = -1; MusECore::Route r(route_track, isOutput ? ach : bch, channels); r.remoteChannel = isOutput ? bch : ach; act->setData(qVariantFromValue(r)); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && ir->remoteChannel == r.remoteChannel) { int tcompch = r.channel; if(tcompch == -1) tcompch = 0; int tcompchs = r.channels; if(tcompchs == -1) tcompchs = isOutput ? track->channels() : route_track->channels(); int compch = ir->channel; if(compch == -1) compch = 0; int compchs = ir->channels; if(compchs == -1) compchs = isOutput ? track->channels() : ir->track->channels(); if(compch == tcompch && compchs == tcompchs) { act->setChecked(true); break; } } } if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. { if( (isOutput ? track : route_track)->isCircularRoute(isOutput ? route_track : track) ) act->setEnabled(false); } return ++id; } //--------------------------------------------------------- // addAuxPorts //--------------------------------------------------------- int RoutePopupMenu::addAuxPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::AuxList* al = MusEGlobal::song->auxs(); for (MusECore::iAudioAux i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addInPorts //--------------------------------------------------------- int RoutePopupMenu::addInPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::InputList* al = MusEGlobal::song->inputs(); for (MusECore::iAudioInput i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addOutPorts //--------------------------------------------------------- int RoutePopupMenu::addOutPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::OutputList* al = MusEGlobal::song->outputs(); for (MusECore::iAudioOutput i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addGroupPorts //--------------------------------------------------------- int RoutePopupMenu::addGroupPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::GroupList* al = MusEGlobal::song->groups(); for (MusECore::iAudioGroup i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addWavePorts //--------------------------------------------------------- int RoutePopupMenu::addWavePorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::WaveTrackList* al = MusEGlobal::song->waves(); for (MusECore::iWaveTrack i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addSyntiPorts //--------------------------------------------------------- int RoutePopupMenu::addSyntiPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); QAction* act; MusECore::SynthIList* al = MusEGlobal::song->syntis(); for (MusECore::iSynthI i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. if(track->channels() == 1) toch = 1; // totalInChannels is only used by syntis. int chans = (!isOutput || track->type() != MusECore::Track::AUDIO_SOFTSYNTH) ? toch : ((MusECore::AudioTrack*)track)->totalInChannels(); int tchans = (channels != -1) ? channels: t->channels(); if(tchans == 2) { // Ignore odd numbered left-over mono channel. //chans = chans & ~1; //if(chans != 0) chans -= 1; } if(chans > 0) { PopupMenu* chpup = new PopupMenu(lb, true); chpup->setTitle(track->name()); for(int ch = 0; ch < chans; ++ch) { char buffer[128]; if(tchans == 2) snprintf(buffer, 128, "%s %d,%d", tr("Channel").toLatin1().constData(), ch+1, ch+2); else snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), ch+1); act = chpup->addAction(QString(buffer)); act->setCheckable(true); int ach = (channel == -1) ? ch : channel; int bch = (channel == -1) ? -1 : ch; MusECore::Route rt(track, (t->type() != MusECore::Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans); rt.remoteChannel = (t->type() != MusECore::Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach; act->setData(qVariantFromValue(rt)); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) { int tcompch = rt.channel; if(tcompch == -1) tcompch = 0; int tcompchs = rt.channels; if(tcompchs == -1) tcompchs = isOutput ? t->channels() : track->channels(); int compch = ir->channel; if(compch == -1) compch = 0; int compchs = ir->channels; if(compchs == -1) compchs = isOutput ? t->channels() : ir->track->channels(); if(compch == tcompch && compchs == tcompchs) { act->setChecked(true); break; } } } if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. { if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) act->setEnabled(false); } ++id; } lb->addMenu(chpup); } } return id; } //--------------------------------------------------------- // addMultiChannelOutPorts //--------------------------------------------------------- int RoutePopupMenu::addMultiChannelPorts(MusECore::AudioTrack* t, PopupMenu* pup, int id, bool isOutput) { int toch = t->totalOutChannels(); // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. if(t->channels() == 1) toch = 1; // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. // totalInChannels is only used by syntis. int chans = (isOutput || t->type() != MusECore::Track::AUDIO_SOFTSYNTH) ? toch : t->totalInChannels(); if(chans > 1) pup->addAction(new MenuTitleItem("", pup)); // // If it's more than one channel, create a sub-menu. If it's just one channel, don't bother with a sub-menu... // PopupMenu* chpup = pup; for(int ch = 0; ch < chans; ++ch) { // If more than one channel, create the sub-menu. if(chans > 1) chpup = new PopupMenu(pup, true); if(isOutput) { switch(t->type()) { case MusECore::Track::AUDIO_INPUT: case MusECore::Track::WAVE: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_SOFTSYNTH: case MusECore::Track::AUDIO_AUX: id = addWavePorts(t, chpup, id, ch, 1, isOutput); id = addOutPorts(t, chpup, id, ch, 1, isOutput); id = addGroupPorts(t, chpup, id, ch, 1, isOutput); id = addSyntiPorts(t, chpup, id, ch, 1, isOutput); break; default: break; } } else { switch(t->type()) { case MusECore::Track::AUDIO_OUTPUT: id = addWavePorts(t, chpup, id, ch, 1, isOutput); id = addInPorts(t, chpup, id, ch, 1, isOutput); id = addGroupPorts(t, chpup, id, ch, 1, isOutput); id = addAuxPorts(t, chpup, id, ch, 1, isOutput); id = addSyntiPorts(t, chpup, id, ch, 1, isOutput); break; case MusECore::Track::WAVE: case MusECore::Track::AUDIO_SOFTSYNTH: case MusECore::Track::AUDIO_GROUP: id = addWavePorts(t, chpup, id, ch, 1, isOutput); id = addInPorts(t, chpup, id, ch, 1, isOutput); id = addGroupPorts(t, chpup, id, ch, 1, isOutput); id = addAuxPorts(t, chpup, id, ch, 1, isOutput); id = addSyntiPorts(t, chpup, id, ch, 1, isOutput); break; default: break; } } // If more than one channel, add the created sub-menu. if(chans > 1) { char buffer[128]; snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), ch+1); chpup->setTitle(QString(buffer)); pup->addMenu(chpup); } } // For stereo listing, ignore odd numbered left-over channels. chans -= 1; if(chans > 0) { // Ignore odd numbered left-over channels. //int schans = (chans & ~1) - 1; pup->addSeparator(); pup->addAction(new MenuTitleItem("", pup)); // // If it's more than two channels, create a sub-menu. If it's just two channels, don't bother with a sub-menu... // chpup = pup; if(chans <= 2) // Just do one iteration. chans = 1; for(int ch = 0; ch < chans; ++ch) { // If more than two channels, create the sub-menu. if(chans > 2) chpup = new PopupMenu(pup, true); if(isOutput) { switch(t->type()) { case MusECore::Track::AUDIO_INPUT: case MusECore::Track::WAVE: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_SOFTSYNTH: case MusECore::Track::AUDIO_AUX: id = addWavePorts(t, chpup, id, ch, 2, isOutput); id = addOutPorts(t, chpup, id, ch, 2, isOutput); id = addGroupPorts(t, chpup, id, ch, 2, isOutput); id = addSyntiPorts(t, chpup, id, ch, 2, isOutput); break; default: break; } } else { switch(t->type()) { case MusECore::Track::AUDIO_OUTPUT: id = addWavePorts(t, chpup, id, ch, 2, isOutput); id = addInPorts(t, chpup, id, ch, 2, isOutput); id = addGroupPorts(t, chpup, id, ch, 2, isOutput); id = addAuxPorts(t, chpup, id, ch, 2, isOutput); id = addSyntiPorts(t, chpup, id, ch, 2, isOutput); break; case MusECore::Track::WAVE: case MusECore::Track::AUDIO_SOFTSYNTH: case MusECore::Track::AUDIO_GROUP: id = addWavePorts(t, chpup, id, ch, 2, isOutput); id = addInPorts(t, chpup, id, ch, 2, isOutput); id = addGroupPorts(t, chpup, id, ch, 2, isOutput); id = addAuxPorts(t, chpup, id, ch, 2, isOutput); id = addSyntiPorts(t, chpup, id, ch, 2, isOutput); break; default: break; } } // If more than two channels, add the created sub-menu. if(chans > 2) { char buffer[128]; snprintf(buffer, 128, "%s %d,%d", tr("Channel").toLatin1().constData(), ch+1, ch+2); chpup->setTitle(QString(buffer)); pup->addMenu(chpup); } } } return id; } //--------------------------------------------------------- // nonSyntiTrackAddSyntis //--------------------------------------------------------- int RoutePopupMenu::nonSyntiTrackAddSyntis(MusECore::AudioTrack* t, PopupMenu* lb, int id, bool isOutput) { MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); QAction* act; MusECore::SynthIList* al = MusEGlobal::song->syntis(); for (MusECore::iSynthI i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. if(track->channels() == 1) toch = 1; // totalInChannels is only used by syntis. int chans = (!isOutput || track->type() != MusECore::Track::AUDIO_SOFTSYNTH) ? toch : ((MusECore::AudioTrack*)track)->totalInChannels(); //int schans = synti->channels(); //if(schans < chans) // chans = schans; // int tchans = (channels != -1) ? channels: t->channels(); // if(tchans == 2) // { // Ignore odd numbered left-over mono channel. //chans = chans & ~1; //if(chans != 0) // chans -= 1; // } //int tchans = (channels != -1) ? channels: t->channels(); if(chans > 0) { PopupMenu* chpup = new PopupMenu(lb, true); chpup->setTitle(track->name()); if(chans > 1) chpup->addAction(new MenuTitleItem("", chpup)); for(int ch = 0; ch < chans; ++ch) { char buffer[128]; snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), ch+1); act = chpup->addAction(QString(buffer)); act->setCheckable(true); int ach = ch; int bch = -1; MusECore::Route rt(track, isOutput ? bch : ach, 1); rt.remoteChannel = isOutput ? ach : bch; act->setData(qVariantFromValue(rt)); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) { int tcompch = rt.channel; if(tcompch == -1) tcompch = 0; int tcompchs = rt.channels; if(tcompchs == -1) tcompchs = isOutput ? t->channels() : track->channels(); int compch = ir->channel; if(compch == -1) compch = 0; int compchs = ir->channels; if(compchs == -1) compchs = isOutput ? t->channels() : ir->track->channels(); if(compch == tcompch && compchs == tcompchs) { act->setChecked(true); break; } } } if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. { if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) act->setEnabled(false); } ++id; } chans -= 1; if(chans > 0) { // Ignore odd numbered left-over channels. //int schans = (chans & ~1) - 1; chpup->addSeparator(); chpup->addAction(new MenuTitleItem("", chpup)); for(int ch = 0; ch < chans; ++ch) { char buffer[128]; snprintf(buffer, 128, "%s %d,%d", tr("Channel").toLatin1().constData(), ch+1, ch+2); act = chpup->addAction(QString(buffer)); act->setCheckable(true); int ach = ch; int bch = -1; MusECore::Route rt(track, isOutput ? bch : ach, 2); rt.remoteChannel = isOutput ? ach : bch; act->setData(qVariantFromValue(rt)); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) { int tcompch = rt.channel; if(tcompch == -1) tcompch = 0; int tcompchs = rt.channels; if(tcompchs == -1) tcompchs = isOutput ? t->channels() : track->channels(); int compch = ir->channel; if(compch == -1) compch = 0; int compchs = ir->channels; if(compchs == -1) compchs = isOutput ? t->channels() : ir->track->channels(); if(compch == tcompch && compchs == tcompchs) { act->setChecked(true); break; } } } if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. { if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) act->setEnabled(false); } ++id; } } lb->addMenu(chpup); } } return id; } //--------------------------------------------------------- // addMidiPorts //--------------------------------------------------------- int RoutePopupMenu::addMidiPorts(MusECore::AudioTrack* t, PopupMenu* pup, int id, bool isOutput) { #ifndef _USE_CUSTOM_WIDGET_ACTIONS_ QAction* act; #endif #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ PixmapButtonsHeaderWidgetAction* wa_hdr = new PixmapButtonsHeaderWidgetAction("Output port/device", darkRedLedIcon, MIDI_CHANNELS, pup); pup->addAction(wa_hdr); ++id; #else pup->addAction(new MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Output port/device")), pup)); #endif for(int i = 0; i < MIDI_PORTS; ++i) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* md = mp->device(); // This is desirable, but could lead to 'hidden' routes unless we add more support // such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. //if(!md) // continue; if(!md || !(md->rwFlags() & (isOutput ? 2 : 1))) // If this is an input click we are looking for midi outputs here. continue; // Do not list synth devices! if(md->isSynti()) continue; MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); int chanmask = 0; // To reduce number of routes required, from one per channel to just one containing a channel mask. // Look for the first route to this midi port. There should always be only a single route for each midi port, now. for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) { // We have a route to the midi port. Grab the channel mask. chanmask = ir->channel; break; } } #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ PixmapButtonsWidgetAction* wa = new PixmapButtonsWidgetAction(QString::number(i + 1) + ":" + (md ? md->name() : tr("")), redLedIcon, darkRedLedIcon,MIDI_CHANNELS, chanmask, pup); MusECore::Route srcRoute(i, 0); // Ignore the routing channels - our action holds the channels. //wa->setData(id++); wa->setData(qVariantFromValue(srcRoute)); pup->addAction(wa); ++id; #else PopupMenu* subp = new PopupMenu(pup, true); subp->setTitle(md->name()); for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { act = subp->addAction(QString("Channel %1").arg(ch+1)); act->setCheckable(true); int chbit = 1 << ch; MusECore::Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position. act->setData(qVariantFromValue(srcRoute)); if(chanmask & chbit) // Is the channel already set? Show item check mark. act->setChecked(true); ++id; } //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. act = subp->addAction(QString("Toggle all")); //act->setCheckable(true); MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. act->setData(qVariantFromValue(togRoute)); ++id; pup->addMenu(subp); #endif // _USE_CUSTOM_WIDGET_ACTIONS_ } return id; } //====================== // RoutePopupMenu //====================== RoutePopupMenu::RoutePopupMenu(QWidget* parent, MusECore::Track* track, bool isOutput) //: _track(track), _isOutMenu(isOutput) : PopupMenu(parent, true), _track(track), _isOutMenu(isOutput) { init(); } RoutePopupMenu::RoutePopupMenu(const QString& title, QWidget* parent, MusECore::Track* track, bool isOutput) //: _track(track), _isOutMenu(isOutput) : PopupMenu(title, parent, true), _track(track), _isOutMenu(isOutput) { init(); } void RoutePopupMenu::init() { //printf("RoutePopupMenu::init this:%p\n", this); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); } void RoutePopupMenu::songChanged(MusECore::SongChangedFlags_t val) { if(val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) updateRouteMenus(); } void RoutePopupMenu::updateRouteMenus() { // NOTE: The purpose of this routine is to make sure the items actually reflect // the routing status. // In case for some reason a route could not be added (or removed). // Then the item will be properly un-checked (or checked) here. // TODO Fix this up a bit. It doesn't quite respond to complete removal, and other situations are a bit odd. // Best to ignore it for now since it only half-works. p4.0.42 /* //printf("RoutePopupMenu::updateRouteMenus\n"); if(!_track || actions().isEmpty() || !isVisible()) return; MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); // Clear all the action check marks. clearAllChecks(); // Take care of Midi Port to Audio Input routes first... if(_isOutMenu && _track->isMidiTrack()) { int port = ((MusECore::MidiTrack*)_track)->outPort(); if(port >= 0 && port < MIDI_PORTS) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::RouteList* mprl = mp->outRoutes(); for (MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track && ir->track->type() == MusECore::Track::AUDIO_INPUT) { for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { int chbits = 1 << ch; if(ir->channel & chbits) { MusECore::Route r(ir->track, chbits); //printf("RoutePopupMenu::updateRouteMenus MusECore::MidiPort to AudioInput chbits:%d\n", chbits); // QAction* act = findActionFromData(qVariantFromValue(r)); if(act) { //printf(" ... Found\n"); // act->setChecked(true); } } } } } } } // Now check the ones that are found in the route list. for(MusECore::ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { // Do MidiTrack to MidiPort routes... if(irl->type == MusECore::Route::MIDI_PORT_ROUTE) { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ // Widget action handles channels. Look for route with channels ignored and set to zero. MusECore::Route r(irl->midiPort, 0); QAction* act = findActionFromData(qVariantFromValue(r)); if(act) { //printf("RoutePopupMenu::updateRouteMenus found MidiTrack to MidiPort irl type:%d\n", irl->type); // // Check for custom widget actions first. PixmapButtonsWidgetAction* mc_wa = dynamic_cast(act); if(mc_wa) { //printf(" ... Found custom, setting current state\n"); // mc_wa->setCurrentState(irl->channel); // Set all channels at once. } } #else //printf("RoutePopupMenu::updateRouteMenus MIDI_PORT_ROUTE\n"); for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { int chbits = 1 << ch; if(irl->channel & chbits) { MusECore::Route r(irl->midiPort, chbits); //printf("RoutePopupMenu::updateRouteMenus MidiTrack to MidiPort irl type:%d\n", irl->type); // // If act is a PixmapButtonsWidgetAction, route channel is ignored and is zero. QAction* act = findActionFromData(qVariantFromValue(r)); if(act) { //printf(" ... Found\n"); // act->setChecked(true); } } } #endif // _USE_CUSTOM_WIDGET_ACTIONS_ } else // Do all other routes... { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ // Do MidiPort to MidiTrack routes... if(irl->type == MusECore::Route::TRACK_ROUTE && irl->track && irl->track->type() == MusECore::Track::AUDIO_INPUT) { // Widget action handles channels. Look for route with channels ignored and set to zero. MusECore::Route r(irl->track, 0); QAction* act = findActionFromData(qVariantFromValue(r)); if(act) { // Check for custom widget actions first. PixmapButtonsWidgetAction* mc_wa = dynamic_cast(act); if(mc_wa) { //printf("RoutePopupMenu::updateRouteMenus found custom irl type:%d\n", irl->type); // mc_wa->setCurrentState(irl->channel); // Set all channels at once. continue; } } } #endif // _USE_CUSTOM_WIDGET_ACTIONS_ printf("RoutePopupMenu::updateRouteMenus other irl type:%d\n", irl->type); // REMOVE TIm. if(act) { //printf("RoutePopupMenu::updateRouteMenus found other irl type:%d\n", irl->type); // act->setChecked(true); } } } */ } void RoutePopupMenu::popupActivated(QAction* action) { if(!action || !_track || actions().isEmpty()) return; if(_track->isMidiTrack()) { MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); // Take care of Route data items first... if(qVariantCanConvert(action->data())) { MusECore::Route aRoute = action->data().value(); // Support Midi Port to Audio Input track routes. if(aRoute.type == MusECore::Route::TRACK_ROUTE && aRoute.track && aRoute.track->type() == MusECore::Track::AUDIO_INPUT) { //if(gIsOutRoutingPopupMenu) // Try to avoid splitting like this. { int chbit = aRoute.channel; int port = ((MusECore::MidiTrack*)_track)->outPort(); if(port < 0 || port >= MIDI_PORTS) return; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; //MusECore::MidiDevice* md = mp->device(); // This is desirable, but could lead to 'hidden' routes unless we add more support // such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. //if(!md) // return; //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) // return; MusECore::Route bRoute(port, chbit); int chmask = 0; MusECore::RouteList* mprl = _isOutMenu ? mp->outRoutes() : mp->inRoutes(); MusECore::ciRoute ir = mprl->begin(); for (; ir != mprl->end(); ++ir) if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == aRoute.track) { // Is there already a route to this port? chmask = ir->channel; // Grab the channel mask. break; } if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? { // disconnect if(_isOutMenu) MusEGlobal::audio->msgRemoveRoute(bRoute, aRoute); else MusEGlobal::audio->msgRemoveRoute(aRoute, bRoute); } else { // connect if(_isOutMenu) MusEGlobal::audio->msgAddRoute(bRoute, aRoute); else MusEGlobal::audio->msgAddRoute(aRoute, bRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); } return; } // Support Audio Input track to Midi Port routes. else if(aRoute.type == MusECore::Route::MIDI_PORT_ROUTE) { // Check for custom midi channel select action. PixmapButtonsWidgetAction* cs_wa = dynamic_cast(action); if(cs_wa) { MusECore::Route aRoute = action->data().value(); int chbits = cs_wa->currentState(); aRoute.channel = chbits; // Restore the desired route channels from the custom widget action state. int mdidx = aRoute.midiPort; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mdidx]; MusECore::MidiDevice* md = mp->device(); //if(!md) // Rem. Allow connections to ports with no device. // return; //if(!(md->rwFlags() & 2)) //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2))) return; int chmask = 0; MusECore::ciRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) { // Is there already a route to this port? chmask = iir->channel; // Grab the channel mask. break; } // Only if something changed... if(chmask != chbits) { if(chmask != 0) { MusECore::Route bRoute(_track, chmask); // Disconnect all existing channels. if(_isOutMenu) MusEGlobal::audio->msgRemoveRoute(bRoute, *iir); else MusEGlobal::audio->msgRemoveRoute(*iir, bRoute); } if(chbits != 0) { // Connect desired channels. MusECore::Route bRoute(_track, chbits); if(_isOutMenu) MusEGlobal::audio->msgAddRoute(bRoute, aRoute); else MusEGlobal::audio->msgAddRoute(aRoute, bRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); } //return; } else { int chbit = aRoute.channel; MusECore::Route bRoute(_track, chbit); int mdidx = aRoute.midiPort; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mdidx]; MusECore::MidiDevice* md = mp->device(); //if(!md) // Rem. Allow connections to ports with no device. // return; //if(!(md->rwFlags() & 2)) //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2))) return; int chmask = 0; MusECore::ciRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) { if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? { chmask = iir->channel; // Grab the channel mask. break; } } if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? { // disconnect if(_isOutMenu) MusEGlobal::audio->msgRemoveRoute(bRoute, aRoute); else MusEGlobal::audio->msgRemoveRoute(aRoute, bRoute); } else { // connect if(_isOutMenu) MusEGlobal::audio->msgAddRoute(bRoute, aRoute); else MusEGlobal::audio->msgAddRoute(aRoute, bRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); } } } // ... now take care of integer data items. else if(qVariantCanConvert(action->data())) { int n = action->data().value(); if(!_isOutMenu && n == 0) MusEGlobal::muse->configMidiPorts(); return; } } else { MusECore::AudioTrack* t = (MusECore::AudioTrack*)_track; MusECore::RouteList* rl = _isOutMenu ? t->outRoutes() : t->inRoutes(); if(!qVariantCanConvert(action->data())) return; if(_isOutMenu) { MusECore::Route dstRoute = action->data().value(); MusECore::Route srcRoute(t, dstRoute.channel, dstRoute.channels); srcRoute.remoteChannel = dstRoute.remoteChannel; // check if route src->dst exists: MusECore::ciRoute irl = rl->begin(); for (; irl != rl->end(); ++irl) { if (*irl == dstRoute) break; } if (irl != rl->end()) { // disconnect if route exists MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); } else { // connect if route does not exist MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); } else { MusECore::Route srcRoute = action->data().value(); // Support Midi Port to Audio Input routes. if(_track->type() == MusECore::Track::AUDIO_INPUT && srcRoute.type == MusECore::Route::MIDI_PORT_ROUTE) { // Check for custom midi channel select action. PixmapButtonsWidgetAction* cs_wa = dynamic_cast(action); if(cs_wa) { int chbits = cs_wa->currentState(); srcRoute.channel = chbits; // Restore the desired route channels from the custom widget action state. int mdidx = srcRoute.midiPort; int chmask = 0; MusECore::ciRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) { // Is there already a route to this port? chmask = iir->channel; // Grab the channel mask. break; } // Only if something changed... if(chmask != chbits) { if(chmask != 0) { // Disconnect all existing channels. MusECore::Route dstRoute(t, chmask); MusEGlobal::audio->msgRemoveRoute(*iir, dstRoute); } if(chbits != 0) { // Connect desired channels. MusECore::Route dstRoute(t, chbits); MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); } return; } else { int chbit = srcRoute.channel; MusECore::Route dstRoute(t, chbit); int mdidx = srcRoute.midiPort; int chmask = 0; MusECore::ciRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) { if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? { chmask = iir->channel; // Grab the channel mask. break; } } if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? { //printf("routingPopupMenuActivated: removing src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel); MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); } else { //printf("routingPopupMenuActivated: adding src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel); MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); return; } } MusECore::Route dstRoute(t, srcRoute.channel, srcRoute.channels); dstRoute.remoteChannel = srcRoute.remoteChannel; MusECore::ciRoute irl = rl->begin(); for (; irl != rl->end(); ++irl) { if (*irl == srcRoute) break; } if (irl != rl->end()) { // disconnect MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); } else { // connect MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); } } //else //{ //} } void RoutePopupMenu::prepare() { ///disconnect(); ///clear(); if(!_track) return; connect(this, SIGNAL(triggered(QAction*)), SLOT(popupActivated(QAction*))); if(_track->isMidiTrack()) { MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); int gid = 0; QAction* act = 0; if(_isOutMenu) { // Support Midi Port to Audio Input track routes. int port = ((MusECore::MidiTrack*)_track)->outPort(); if(port >= 0 && port < MIDI_PORTS) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; // Do not list synth devices! Requiring valid device is desirable, // but could lead to 'hidden' routes unless we add more support // such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. if(mp->device() && !mp->device()->isSynti()) { MusECore::RouteList* mprl = mp->outRoutes(); int chbits = 1 << ((MusECore::MidiTrack*)_track)->outChannel(); //MusECore::MidiDevice* md = mp->device(); //if(!md) // continue; addSeparator(); addAction(new MenuTitleItem(tr("Soloing chain"), this)); PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(tr("Audio returns")); addMenu(subp); MusECore::InputList* al = MusEGlobal::song->inputs(); for (MusECore::ciAudioInput i = al->begin(); i != al->end(); ++i) { MusECore::Track* t = *i; QString s(t->name()); act = subp->addAction(s); act->setCheckable(true); MusECore::Route r(t, chbits); act->setData(qVariantFromValue(r)); for(MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == t && (ir->channel & chbits)) { act->setChecked(true); break; } } ++gid; } } } } else { // Warn if no devices available. Add an item to open midi config. int pi = 0; for( ; pi < MIDI_PORTS; ++pi) { MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); if(md && !md->isSynti() && (md->rwFlags() & 2)) //if(md && (md->rwFlags() & 2 || md->isSynti()) ) // p4.0.27 Reverted p4.0.35 break; } if(pi == MIDI_PORTS) { act = addAction(tr("Warning: No input devices!")); act->setCheckable(false); act->setData(-1); addSeparator(); } act = addAction(QIcon(*settings_midiport_softsynthsIcon), tr("Open midi config...")); act->setCheckable(false); act->setData(gid); addSeparator(); ++gid; #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ PixmapButtonsHeaderWidgetAction* wa_hdr = new PixmapButtonsHeaderWidgetAction("Input port/device", darkRedLedIcon, MIDI_CHANNELS, this); addAction(wa_hdr); ++gid; #else addAction(new MenuTitleItem("Input port/device", this)); #endif for(int i = 0; i < MIDI_PORTS; ++i) { // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick with ports. MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* md = mp->device(); //if(!md) // continue; // Do not list synth devices! if( md && (!(md->rwFlags() & 2) || md->isSynti()) ) // p4.0.27 Go ahead. Synths esp MESS send out stuff. Reverted p4.0.35 //if( md && !(md->rwFlags() & 2) && !md->isSynti() ) continue; //printf("MusE::prepareRoutingPopupMenu adding submenu portnum:%d\n", i); int chanmask = 0; // To reduce number of routes required, from one per channel to just one containing a channel mask. // Look for the first route to this midi port. There should always be only a single route for each midi port, now. MusECore::ciRoute ir = rl->begin(); for( ; ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) { // We have a route to the midi port. Grab the channel mask. chanmask = ir->channel; break; } } // List ports with no device, but with routes to this track, in the main popup. if(!md && ir == rl->end()) continue; #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ PixmapButtonsWidgetAction* wa = new PixmapButtonsWidgetAction(QString::number(i + 1) + ":" + (md ? md->name() : tr("")), redLedIcon, darkRedLedIcon, MIDI_CHANNELS, chanmask, this); MusECore::Route srcRoute(i, 0); // Ignore the routing channels - our action holds the channels. //wa->setData(id++); wa->setData(qVariantFromValue(srcRoute)); addAction(wa); ++gid; #else PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(QString("%1:").arg(i+1) + (md ? md->name() : tr(""))); for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { act = subp->addAction(QString("Channel %1").arg(ch+1)); act->setCheckable(true); int chbit = 1 << ch; MusECore::Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position. act->setData(qVariantFromValue(srcRoute)); if(chanmask & chbit) // Is the channel already set? Show item check mark. act->setChecked(true); ++gid; } //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. act = subp->addAction(tr("Toggle all")); //act->setCheckable(true); MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. act->setData(qVariantFromValue(togRoute)); ++gid; addMenu(subp); #endif // _USE_CUSTOM_WIDGET_ACTIONS_ } #if 0 // p4.0.17 List ports with no device and no in routes, in a separate popup. PopupMenu* morep = new PopupMenu(pup, true); morep->setTitle(tr("More...")); for(int i = 0; i < MIDI_PORTS; ++i) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; if(mp->device()) continue; PopupMenu* subp = new PopupMenu(morep, true); subp->setTitle(QString("%1:%2").arg(i).arg(tr(""))); // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! //connect(subp, SIGNAL(triggered(QAction*)), pup, SIGNAL(triggered(QAction*))); //connect(subp, SIGNAL(aboutToHide()), pup, SIGNAL(aboutToHide())); iRoute ir = rl->begin(); for( ; ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) break; } if(ir != rl->end()) continue; for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { act = subp->addAction(QString("Channel %1").arg(ch+1)); act->setCheckable(true); act->setData(gid); int chbit = 1 << ch; MusECore::Route srcRoute(i, chbit); // In accordance with new channel mask, use the bit position. gRoutingMenuMap.insert( pRouteMenuMap(gid, srcRoute) ); //if(chanmask & chbit) // Is the channel already set? Show item check mark. // act->setChecked(true); ++gid; } //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. act = subp->addAction(QString("Toggle all")); //act->setCheckable(true); act->setData(gid); MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); ++gid; morep->addMenu(subp); } pup->addMenu(morep); #endif } return; } else { MusECore::AudioTrack* t = (MusECore::AudioTrack*)_track; int channel = t->channels(); if(_isOutMenu) { MusECore::RouteList* orl = t->outRoutes(); QAction* act = 0; int gid = 0; gid = 0; switch(_track->type()) { case MusECore::Track::AUDIO_OUTPUT: { for(int i = 0; i < channel; ++i) { char buffer[128]; snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), i+1); MenuTitleItem* titel = new MenuTitleItem(QString(buffer), this); addAction(titel); if(!MusEGlobal::checkAudioDevice()) { clear(); return; } std::list ol = MusEGlobal::audioDevice->inputPorts(); for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) { act = addAction(*ip); act->setCheckable(true); MusECore::Route dst(*ip, true, i, MusECore::Route::JACK_ROUTE); act->setData(qVariantFromValue(dst)); ++gid; for(MusECore::ciRoute ir = orl->begin(); ir != orl->end(); ++ir) { if(*ir == dst) { act->setChecked(true); break; } } } if(i+1 != channel) addSeparator(); } // // Display using separate menu for audio inputs: // addSeparator(); addAction(new MenuTitleItem(tr("Soloing chain"), this)); PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(tr("Audio returns")); addMenu(subp); gid = addInPorts(t, subp, gid, -1, -1, true); // // Display all in the same menu: // //addSeparator(); //MenuTitleItem* title = new MenuTitleItem(tr("Audio returns"), this); //addAction(title); //gid = addInPorts(t, this, gid, -1, -1, true); } break; case MusECore::Track::AUDIO_SOFTSYNTH: gid = addMultiChannelPorts(t, this, gid, true); break; case MusECore::Track::AUDIO_INPUT: case MusECore::Track::WAVE: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: gid = addWavePorts( t, this, gid, -1, -1, true); gid = addOutPorts( t, this, gid, -1, -1, true); gid = addGroupPorts( t, this, gid, -1, -1, true); gid = nonSyntiTrackAddSyntis(t, this, gid, true); break; default: clear(); return; } } else { if(_track->type() == MusECore::Track::AUDIO_AUX) return; MusECore::RouteList* irl = t->inRoutes(); QAction* act = 0; int gid = 0; gid = 0; switch(_track->type()) { case MusECore::Track::AUDIO_INPUT: { for(int i = 0; i < channel; ++i) { char buffer[128]; snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), i+1); MenuTitleItem* titel = new MenuTitleItem(QString(buffer), this); addAction(titel); if(!MusEGlobal::checkAudioDevice()) { clear(); return; } std::list ol = MusEGlobal::audioDevice->outputPorts(); for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) { act = addAction(*ip); act->setCheckable(true); MusECore::Route dst(*ip, true, i, MusECore::Route::JACK_ROUTE); act->setData(qVariantFromValue(dst)); ++gid; for(MusECore::ciRoute ir = irl->begin(); ir != irl->end(); ++ir) { if(*ir == dst) { act->setChecked(true); break; } } } if(i+1 != channel) addSeparator(); } // // Display using separate menus for midi ports and audio outputs: // addSeparator(); addAction(new MenuTitleItem(tr("Soloing chain"), this)); PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(tr("Audio sends")); addMenu(subp); gid = addOutPorts(t, subp, gid, -1, -1, false); subp = new PopupMenu(this, true); subp->setTitle(tr("Midi port sends")); addMenu(subp); addMidiPorts(t, subp, gid, false); // // Display all in the same menu: // //addAction(new MenuTitleItem(tr("Audio sends"), this)); //gid = addOutPorts(t, this, gid, -1, -1, false); //addSeparator(); //addAction(new MenuTitleItem(tr("Midi sends"), this)); //addMidiPorts(t, this, gid, false); } break; case MusECore::Track::AUDIO_OUTPUT: gid = addWavePorts( t, this, gid, -1, -1, false); gid = addInPorts( t, this, gid, -1, -1, false); gid = addGroupPorts(t, this, gid, -1, -1, false); gid = addAuxPorts( t, this, gid, -1, -1, false); gid = nonSyntiTrackAddSyntis(t, this, gid, false); break; case MusECore::Track::WAVE: gid = addWavePorts( t, this, gid, -1, -1, false); gid = addInPorts( t, this, gid, -1, -1, false); gid = addGroupPorts(t, this, gid, -1, -1, false); gid = addAuxPorts( t, this, gid, -1, -1, false); gid = nonSyntiTrackAddSyntis(t, this, gid, false); break; case MusECore::Track::AUDIO_GROUP: gid = addWavePorts( t, this, gid, -1, -1, false); gid = addInPorts( t, this, gid, -1, -1, false); gid = addGroupPorts(t, this, gid, -1, -1, false); gid = addAuxPorts( t, this, gid, -1, -1, false); gid = nonSyntiTrackAddSyntis(t, this, gid, false); break; case MusECore::Track::AUDIO_SOFTSYNTH: gid = addMultiChannelPorts(t, this, gid, false); break; default: clear(); return; } } } } void RoutePopupMenu::exec(MusECore::Track* track, bool isOutput) { if(track) { _track = track; _isOutMenu = isOutput; } prepare(); PopupMenu::exec(); } void RoutePopupMenu::exec(const QPoint& p, MusECore::Track* track, bool isOutput) { if(track) { _track = track; _isOutMenu = isOutput; } prepare(); PopupMenu::exec(p); } void RoutePopupMenu::popup(const QPoint& p, MusECore::Track* track, bool isOutput) { if(track) { _track = track; _isOutMenu = isOutput; } prepare(); PopupMenu::popup(p); } } // namespace MusEGui