diff options
author | Florian Jung <flo@windfisch.org> | 2011-06-03 14:24:08 +0000 |
---|---|---|
committer | Florian Jung <flo@windfisch.org> | 2011-06-03 14:24:08 +0000 |
commit | db164b62e3892bd17d1a2eabca76bde3b67072df (patch) | |
tree | ab71ac4b4f05ab94c008d75ef7052bef5af6cc74 /muse2/muse | |
parent | def4fdb391f5207ebbe61881416f39f3d896cc5d (diff) | |
parent | 9187899632c14d64b3fae6477b7f941240f912a6 (diff) |
merged with trunk and adapted new functions
Diffstat (limited to 'muse2/muse')
138 files changed, 4780 insertions, 3264 deletions
diff --git a/muse2/muse/CMakeLists.txt b/muse2/muse/CMakeLists.txt index e6a90a59..cea95083 100644 --- a/muse2/muse/CMakeLists.txt +++ b/muse2/muse/CMakeLists.txt @@ -63,6 +63,7 @@ QT4_WRAP_CPP ( muse_moc_headers song.h transport.h value.h + steprec.h ) ## @@ -135,6 +136,7 @@ file (GLOB core_source_files waveevent.cpp wavetrack.cpp xml.cpp + steprec.cpp ) file (GLOB main_source_files main.cpp diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index adac840a..72cdad6a 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -43,7 +43,7 @@ #include "mixdowndialog.h" #include "pianoroll.h" #include "scoreedit.h" -#include "popupmenu.h" +#include "routepopup.h" #include "shortcutconfig.h" #include "songinfo.h" #include "ticksynth.h" @@ -93,7 +93,6 @@ static QString* projectList[PROJECT_LIST_LEN]; extern void initMidiSynth(); extern void exitJackAudio(); extern void exitDummyAudio(); -// p3.3.39 extern void exitOSC(); #ifdef HAVE_LASH @@ -809,7 +808,6 @@ MusE::MusE(int argc, char** argv) : QMainWindow() editInstrument = 0; routingPopupMenu = 0; progress = 0; - //routingPopupView = 0; appName = QString("MusE"); setWindowTitle(appName); @@ -1658,7 +1656,6 @@ void MusE::initMidiDevices() audio->msgInitMidiDevices(); - // Added by T356 //audio->msgIdle(false); } @@ -1815,7 +1812,7 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll } else { Xml xml(f); - read(xml, !loadAll); + read(xml, !loadAll, songTemplate); bool fileError = ferror(f); popenFlag ? pclose(f) : fclose(f); if (fileError) { @@ -2080,7 +2077,7 @@ bool MusE::save(const QString& name, bool overwriteWarn) if (ferror(f)) { QString s = "Write File\n" + name + "\nfailed: " //+ strerror(errno); - + QString(strerror(errno)); // p4.0.0 + + QString(strerror(errno)); QMessageBox::critical(this, tr("MusE: Write File failed"), s); popenFlag? pclose(f) : fclose(f); @@ -2172,10 +2169,10 @@ void MusE::closeEvent(QCloseEvent* event) printf("MusE: Exiting Metronome\n"); exitMetronome(); - // p3.3.47 - // Make sure to clear the menu, which deletes any sub menus. + // Make sure to delete the menu. ~routingPopupMenu() will NOT be called automatically. + // Even though it is a child of MusE, it just passes MusE onto the underlying PopupMenus. p4.0.26 if(routingPopupMenu) - routingPopupMenu->clear(); + delete routingPopupMenu; #if 0 if(routingPopupView) { @@ -2201,10 +2198,8 @@ void MusE::closeEvent(QCloseEvent* event) d.remove(f.completeBaseName() + ".wca"); } - // Added by Tim. p3.3.14 - #ifdef HAVE_LASH - // Disconnect gracefully from LASH. + // Disconnect gracefully from LASH. Tim. p3.3.14 if(lash_client) { if(debugMsg) @@ -2292,1080 +2287,18 @@ void MusE::showTransport(bool flag) //--------------------------------------------------------- // getRoutingPopupMenu +// Get the special common routing popup menu. Used (so far) +// by audio strip, midi strip, and midi trackinfo. //--------------------------------------------------------- -PopupMenu* MusE::getRoutingPopupMenu() +RoutePopupMenu* MusE::getRoutingPopupMenu() { if(!routingPopupMenu) - routingPopupMenu = new PopupMenu(this); + routingPopupMenu = new RoutePopupMenu(this); return routingPopupMenu; } //--------------------------------------------------------- -// updateRouteMenus -//--------------------------------------------------------- - -void MusE::updateRouteMenus(Track* track, QObject* master) -{ - // 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. - - //if(!track || track != gRoutingPopupMenuMaster || track->type() == Track::AUDIO_AUX) - //if(!track || track->type() == Track::AUDIO_AUX) - if(!track || gRoutingPopupMenuMaster != master) // p3.3.50 - return; - - PopupMenu* pup = getRoutingPopupMenu(); - - if(pup->actions().isEmpty()) - return; - - if(!pup->isVisible()) - return; - - //AudioTrack* t = (AudioTrack*)track; - RouteList* rl = gIsOutRoutingPopupMenu ? track->outRoutes() : track->inRoutes(); - - iRouteMenuMap imm = gRoutingMenuMap.begin(); - for(; imm != gRoutingMenuMap.end(); ++imm) - { - // p3.3.50 Ignore the 'toggle' items. - if(imm->second.type == Route::MIDI_PORT_ROUTE && - //imm->first >= (MIDI_PORTS * MIDI_CHANNELS) && imm->first < (MIDI_PORTS * MIDI_CHANNELS + MIDI_PORTS)) - imm->second.channel == (1 << MIDI_CHANNELS) - 1) // p4.0.14 See if all channels are set, rather than using ID. - continue; - - - // p4.0.14 TODO FIXME Couldn't quite figure out the logic yet. It should be possible (I hope). - // But not really required for now, as per above note. - if(gIsOutRoutingPopupMenu && track->isMidiTrack() && - imm->second.type == Route::TRACK_ROUTE && imm->second.track->type() == Track::AUDIO_INPUT) - return; - #if 0 - printf("imm route:\n"); - imm->second.dump(); - if(track->isMidiTrack()) - { - if(imm->second.type == Route::TRACK_ROUTE && imm->second.track->type() == Track::AUDIO_INPUT) - { - Route &aRoute = imm->second; - int chbit = aRoute.channel; - ///Route bRoute(track, chbit); - ///int mdidx = bRoute.midiPort; - int port = ((MidiTrack*)track)->outPort(); - if(port < 0 || port >= MIDI_PORTS) - continue; - int tchbit = 1 << ((MidiTrack*)track)->outChannel(); - - MidiPort* mp = &midiPorts[port]; - - ///Route bRoute(port, chbit); - - //int chmask = 0; - bool found = false; - RouteList* mprl = gIsOutRoutingPopupMenu ? mp->outRoutes() : mp->inRoutes(); - iRoute ir = mprl->begin(); - for(; ir != mprl->end(); ++ir) - { - printf("mp route:\n"); - ir->dump(); - ///if(aRoute.type == Route::TRACK_ROUTE) // Is the map route a track route? - { - if(ir->type == Route::TRACK_ROUTE && ir->track == aRoute.track) // Is the track route a midi port route? - //&& (ir->channel & chbit) == chbit) - //&& (ir->channel & tchbit)) // Is the exact channel mask bit(s) set? - { - printf("track matches\n"); - if(ir->channel & tchbit) - { - found = true; - printf("found: bit matches\n"); - } - break; - } - } - ///else - ///if(*ir == aRoute) - ///{ - //found = true; - /// break; - ///} - } - //pup->setItemChecked(imm->first, found); - //printf("MusE::updateRouteMenus setItemChecked\n"); - // TODO: MusE-2: Convert this, fastest way is to change the routing map, otherwise this requires a lookup. - //if(pup->isItemChecked(imm->first) != (irl != rl->end())) - // pup->setItemChecked(imm->first, irl != rl->end()); - QAction* act = pup->findActionFromData(imm->first); - //printf("set act checked to:%d\n", ir != mprl->end()); - //if(act && act->isChecked() != (ir != mprl->end())) - // act->setChecked(ir != mprl->end()); - printf("set act checked to:%d\n", found); - if(act && act->isChecked() != found) - act->setChecked(found); - - //return; - } - } - #endif - - //bool found = false; - iRoute irl = rl->begin(); - for(; irl != rl->end(); ++irl) - { - if(imm->second.type == Route::MIDI_PORT_ROUTE) // p3.3.50 Is the map route a midi port route? - { - if(irl->type == Route::MIDI_PORT_ROUTE && irl->midiPort == imm->second.midiPort // Is the track route a midi port route? - && (irl->channel & imm->second.channel) == imm->second.channel) // Is the exact channel mask bit(s) set? - { - //found = true; - break; - } - } - else - if(*irl == imm->second) - { - //found = true; - break; - } - } - //pup->setItemChecked(imm->first, found); - //printf("MusE::updateRouteMenus setItemChecked\n"); - // TODO: MusE-2: Convert this, fastest way is to change the routing map, otherwise this requires a lookup. - //if(pup->isItemChecked(imm->first) != (irl != rl->end())) - // pup->setItemChecked(imm->first, irl != rl->end()); - QAction* act = pup->findActionFromData(imm->first); - if(act && act->isChecked() != (irl != rl->end())) - act->setChecked(irl != rl->end()); - } -} - -//--------------------------------------------------------- -// routingPopupMenuActivated -//--------------------------------------------------------- - -void MusE::routingPopupMenuActivated(Track* track, int n) -{ - //if(!track || (track != gRoutingPopupMenuMaster)) - if(!track) - return; - - if(track->isMidiTrack()) - { - PopupMenu* pup = getRoutingPopupMenu(); - - if(pup->actions().isEmpty()) - return; - - //MidiTrack* t = (MidiTrack*)track; - RouteList* rl = gIsOutRoutingPopupMenu ? track->outRoutes() : track->inRoutes(); - - if(n == -1) - return; - - if(!gIsOutRoutingPopupMenu && n == 0) // p4.0.17 - { - muse->configMidiPorts(); - return; - } - - iRouteMenuMap imm = gRoutingMenuMap.find(n); - if(imm == gRoutingMenuMap.end()) - return; - - // Support Midi Port to Audio Input track routes. p4.0.14 Tim. - if(imm->second.type == Route::TRACK_ROUTE) - { - //if(gIsOutRoutingPopupMenu) // Try to avoid splitting like this. - { - Route &aRoute = imm->second; - int chbit = aRoute.channel; - int port = ((MidiTrack*)track)->outPort(); - if(port < 0 || port >= MIDI_PORTS) - return; - - MidiPort* mp = &midiPorts[port]; - ///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; - - //int channel = ((MidiTrack*)track->outChannel(); - Route bRoute(port, chbit); - - int chmask = 0; - RouteList* mprl = gIsOutRoutingPopupMenu ? mp->outRoutes() : mp->inRoutes(); - iRoute ir = mprl->begin(); - for (; ir != mprl->end(); ++ir) - { - if(ir->type == Route::TRACK_ROUTE && ir->track == aRoute.track) // Is there already a route to this port? - { - chmask = ir->channel; // Grab the channel mask. - break; - } - } - //if (iir != rl->end()) - if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? - { - // disconnect - if(gIsOutRoutingPopupMenu) - audio->msgRemoveRoute(bRoute, aRoute); - else - audio->msgRemoveRoute(aRoute, bRoute); - } - else - { - // connect - if(gIsOutRoutingPopupMenu) - audio->msgAddRoute(bRoute, aRoute); - else - audio->msgAddRoute(aRoute, bRoute); - } - - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - - } - return; - } - - - if(imm->second.type != Route::MIDI_PORT_ROUTE) - return; - Route &aRoute = imm->second; - int chbit = aRoute.channel; - Route bRoute(track, chbit); - int mdidx = aRoute.midiPort; - - MidiPort* mp = &midiPorts[mdidx]; - MidiDevice* md = mp->device(); - //if(!md) // Removed p4.0.17 Allow connections to ports with no device. - // return; - - //if(!(md->rwFlags() & 2)) - //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) - if(md && !(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) // p4.0.17 - return; - - int chmask = 0; - iRoute iir = rl->begin(); - for (; iir != rl->end(); ++iir) - { - //if(*iir == (dst ? bRoute : aRoute)) - //if(*iir == aRoute) - if(iir->type == Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // p3.3.50 Is there already a route to this port? - { - chmask = iir->channel; // p3.3.50 Grab the channel mask. - break; - } - } - //if (iir != rl->end()) - if ((chmask & chbit) == chbit) // p3.3.50 Is the channel's bit(s) set? - { - // disconnect - if(gIsOutRoutingPopupMenu) - audio->msgRemoveRoute(bRoute, aRoute); - else - audio->msgRemoveRoute(aRoute, bRoute); - } - else - { - // connect - if(gIsOutRoutingPopupMenu) - audio->msgAddRoute(bRoute, aRoute); - else - audio->msgAddRoute(aRoute, bRoute); - } - - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - } - else - { - // TODO: Try to move code from AudioStrip::routingPopupMenuActivated into here. - - /* - PopupMenu* pup = getRoutingPopupMenu(); - - printf("MusE::routingPopupMenuActivated audio n:%d count:%d\n", n, pup->count()); - - if(pup->count() == 0) - return; - - AudioTrack* t = (AudioTrack*)track; - RouteList* rl = gIsOutRoutingPopupMenu ? t->outRoutes() : t->inRoutes(); - - //QPoint ppt = QCursor::pos(); - - if(n == -1) - { - //printf("MusE::routingPopupMenuActivated audio n = -1 deleting popup...\n"); - printf("MusE::routingPopupMenuActivated audio n = -1\n"); - ///delete pup; - ///pup = 0; - return; - } - else - //if(n == 0) - //{ - //printf("MusE::routingPopupMenuActivated audio n = 0 = tearOffHandle\n"); - //oR->setDown(false); - // return; - //} - //else - { - if(gIsOutRoutingPopupMenu) - { - QString s(pup->text(n)); - - //printf("AudioStrip::routingPopupMenuActivated audio text:%s\n", s.toLatin1().constData()); - - if(track->type() == Track::AUDIO_OUTPUT) - { - ///delete orpup; - - int chan = n & 0xf; - - //Route srcRoute(t, -1); - //Route srcRoute(t, chan, chans); - //Route srcRoute(t, chan, 1); - Route srcRoute(t, chan); - - //Route dstRoute(s, true, -1); - Route dstRoute(s, true, -1, Route::JACK_ROUTE); - //Route dstRoute(s, true, 0, Route::JACK_ROUTE); - - //srcRoute.channel = dstRoute.channel = chan; - dstRoute.channel = chan; - //dstRoute.channels = 1; - - // check if route src->dst exists: - iRoute irl = rl->begin(); - for (; irl != rl->end(); ++irl) { - if (*irl == dstRoute) - break; - } - if (irl != rl->end()) { - // disconnect if route exists - audio->msgRemoveRoute(srcRoute, dstRoute); - } - else { - // connect if route does not exist - audio->msgAddRoute(srcRoute, dstRoute); - } - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - - // p3.3.47 - //pup->popup(ppt, 0); - - //oR->setDown(false); - return; - - // p3.3.46 - ///goto _redisplay; - } - - iRouteMenuMap imm = gRoutingMenuMap.find(n); - if(imm == gRoutingMenuMap.end()) - { - ///delete orpup; - //oR->setDown(false); // orpup->exec() catches mouse release event - return; - } - - //int chan = n >> 16; - //int chans = (chan >> 15) + 1; // Bit 31 MSB: Mono or stereo. - //chan &= 0xffff; - //int chan = imm->second.channel; - //int chans = imm->second.channels; - - //Route srcRoute(t, -1); - //srcRoute.remoteChannel = chan; - //Route srcRoute(t, chan, chans); - Route srcRoute(t, imm->second.channel, imm->second.channels); - //Route srcRoute(t, imm->second.channel); - srcRoute.remoteChannel = imm->second.remoteChannel; - - //Route dstRoute(s, true, -1); - //Route dstRoute(s, true, -1, Route::TRACK_ROUTE); - Route &dstRoute = imm->second; - - // check if route src->dst exists: - iRoute irl = rl->begin(); - for (; irl != rl->end(); ++irl) { - if (*irl == dstRoute) - break; - } - if (irl != rl->end()) { - // disconnect if route exists - audio->msgRemoveRoute(srcRoute, dstRoute); - } - else { - // connect if route does not exist - audio->msgAddRoute(srcRoute, dstRoute); - } - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - - // p3.3.46 - //oR->setDown(false); - ///goto _redisplay; - - // p3.3.47 - //pup->popup(ppt, 0); - } - else - { - QString s(pup->text(n)); - - if(track->type() == Track::AUDIO_INPUT) - { - ///delete pup; - int chan = n & 0xf; - - Route srcRoute(s, false, -1, Route::JACK_ROUTE); - Route dstRoute(t, chan); - - srcRoute.channel = chan; - - iRoute irl = rl->begin(); - for(; irl != rl->end(); ++irl) - { - if(*irl == srcRoute) - break; - } - if(irl != rl->end()) - // disconnect - audio->msgRemoveRoute(srcRoute, dstRoute); - else - // connect - audio->msgAddRoute(srcRoute, dstRoute); - - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - //iR->setDown(false); // pup->exec() catches mouse release event - return; - - // p3.3.46 - ///goto _redisplay; - } - - iRouteMenuMap imm = gRoutingMenuMap.find(n); - if(imm == gRoutingMenuMap.end()) - { - //delete pup; - //iR->setDown(false); // pup->exec() catches mouse release event - return; - } - - //int chan = n >> 16; - //int chans = (chan >> 15) + 1; // Bit 31 MSB: Mono or stereo. - //chan &= 0xffff; - //int chan = imm->second.channel; - //int chans = imm->second.channels; - - //Route srcRoute(s, false, -1); - //Route srcRoute(s, false, -1, Route::TRACK_ROUTE); - Route &srcRoute = imm->second; - - //Route dstRoute(t, -1); - //Route dstRoute(t, chan, chans); - Route dstRoute(t, imm->second.channel, imm->second.channels); - //Route dstRoute(t, imm->second.channel); - dstRoute.remoteChannel = imm->second.remoteChannel; - - iRoute irl = rl->begin(); - for (; irl != rl->end(); ++irl) { - if (*irl == srcRoute) - break; - } - if (irl != rl->end()) { - // disconnect - audio->msgRemoveRoute(srcRoute, dstRoute); - } - else { - // connect - audio->msgAddRoute(srcRoute, dstRoute); - } - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - - // p3.3.46 - //iR->setDown(false); - ///goto _redisplay; - - - - - } - - } - */ - - } - //else - //{ - //} -} - -//--------------------------------------------------------- -// routingPopupMenuAboutToHide -//--------------------------------------------------------- - -void MusE::routingPopupMenuAboutToHide() -{ - // Hmm, can't do this? Sub-menus stay open with this. Re-arranged, testing... Nope. - //PopupMenu* pup = muse->getRoutingPopupMenu(); - //pup->disconnect(); - //pup->clear(); - - gRoutingMenuMap.clear(); - gRoutingPopupMenuMaster = 0; -} - -//--------------------------------------------------------- -// prepareRoutingPopupMenu -//--------------------------------------------------------- - -PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) -{ - if(!track) - return 0; - - if(track->isMidiTrack()) - { - RouteList* rl = dst ? track->outRoutes() : track->inRoutes(); - //Route dst(track, -1); - - PopupMenu* pup = getRoutingPopupMenu(); - pup->disconnect(); - //connect(pup, SIGNAL(activated(int)), SLOT(routingPopupMenuActivated(int))); - //connect(pup, SIGNAL(aboutToHide()), SLOT(routingPopupMenuAboutToHide())); - - pup->clear(); - gRoutingMenuMap.clear(); - - int gid = 0; - QAction* act = 0; - - if(dst) - { - // Support Midi Port to Audio Input track routes. p4.0.14 Tim. - int port = ((MidiTrack*)track)->outPort(); - if(port >= 0 && port < MIDI_PORTS) - { - MidiPort* mp = &midiPorts[port]; - - // p4.0.17 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()) - { - RouteList* mprl = mp->outRoutes(); - int chbits = 1 << ((MidiTrack*)track)->outChannel(); - //MidiDevice* md = mp->device(); - //if(!md) - // continue; - - pup->addSeparator(); - pup->addAction(new MenuTitleItem(tr("Soloing chain"), pup)); - PopupMenu* subp = new PopupMenu(pup); - subp->setTitle(tr("Audio returns")); - pup->addMenu(subp); - - InputList* al = song->inputs(); - for (iAudioInput i = al->begin(); i != al->end(); ++i) - { - Track* t = *i; - QString s(t->name()); - - act = subp->addAction(s); - act->setData(gid); - act->setCheckable(true); - - Route r(t, chbits); - - gRoutingMenuMap.insert( pRouteMenuMap(gid, r) ); - - for(iRoute ir = mprl->begin(); ir != mprl->end(); ++ir) - { - if(ir->type == 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. p4.0.17 - int pi = 0; - for( ; pi < MIDI_PORTS; ++pi) - { - MidiDevice* md = midiPorts[pi].device(); - if(md && !md->isSynti() && (md->rwFlags() & 2)) - break; - } - if(pi == MIDI_PORTS) - { - act = pup->addAction(tr("Warning: No midi input devices!")); - act->setCheckable(false); - act->setData(-1); - pup->addSeparator(); - } - act = pup->addAction(QIcon(*settings_midiport_softsynthsIcon), tr("Open midi config...")); - act->setCheckable(false); - act->setData(gid); - pup->addSeparator(); - ++gid; - - pup->addAction(new MenuTitleItem("Midi input ports", pup)); - - for(int i = 0; i < MIDI_PORTS; ++i) - { - // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick with ports. - MidiPort* mp = &midiPorts[i]; - MidiDevice* md = mp->device(); - //if(!md) - // continue; - - // p4.0.17 Do not list synth devices! - if(md && md->isSynti()) - continue; - - if(md && !(md->rwFlags() & 2)) - continue; - - //printf("MusE::prepareRoutingPopupMenu adding submenu portnum:%d\n", i); - - // 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())); - - int chanmask = 0; - // p3.3.50 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. - iRoute ir = rl->begin(); - for( ; ir != rl->end(); ++ir) - { - if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i) - { - // We have a route to the midi port. Grab the channel mask. - chanmask = ir->channel; - break; - } - } - // p4.0.17 List ports with no device, but with routes to this track, in the main popup. - if(!md && ir == rl->end()) - continue; - - PopupMenu* subp = new PopupMenu(pup); - subp->setTitle(QString("%1:").arg(i+1) + (md ? md->name() : tr("<none>"))); - - 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; - Route srcRoute(i, chbit); // p3.3.50 In accordance with new channel mask, use the bit position. - - gRoutingMenuMap.insert( pRouteMenuMap(gid, srcRoute) ); - - if(chanmask & chbit) // p3.3.50 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); - act->setData(gid); - Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. - gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); - ++gid; - pup->addMenu(subp); - } - - #if 0 - // p4.0.17 List ports with no device and no in routes, in a separate popup. - PopupMenu* morep = new PopupMenu(pup); - morep->setTitle(tr("More...")); - for(int i = 0; i < MIDI_PORTS; ++i) - { - MidiPort* mp = &midiPorts[i]; - if(mp->device()) - continue; - - PopupMenu* subp = new PopupMenu(morep); - subp->setTitle(QString("%1:").arg(i) + tr("<none>")); - - // 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 == 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; - 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); - Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. - gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); - ++gid; - morep->addMenu(subp); - } - pup->addMenu(morep); - #endif - - } - - if(pup->actions().isEmpty()) - { - gRoutingPopupMenuMaster = 0; - //pup->clear(); - //pup->disconnect(); - gRoutingMenuMap.clear(); - //oR->setDown(false); - return 0; - } - - gIsOutRoutingPopupMenu = dst; - return pup; - } - - return 0; -} - -#if 0 -//--------------------------------------------------------- -// getRoutingPopupView -//--------------------------------------------------------- - -PopupView* MusE::getRoutingPopupView() -{ - if(!routingPopupView) - //routingPopupView = new PopupView(this); - routingPopupView = new PopupView(); - return routingPopupView; -} - -//--------------------------------------------------------- -// routingPopupViewActivated -//--------------------------------------------------------- - -void MusE::routingPopupViewActivated(Track* track, int n) -{ - //if(!track || (track != gRoutingPopupMenuMaster)) - if(!track) - return; - - if(track->isMidiTrack()) - { - PopupView* pup = getRoutingPopupView(); - - //printf("MusE::routingPopupMenuActivated midi n:%d count:%d\n", n, pup->count()); - - if(pup->model()->rowCount() == 0) - return; - - //MidiTrack* t = (MidiTrack*)track; - RouteList* rl = gIsOutRoutingPopupMenu ? track->outRoutes() : track->inRoutes(); - - if(n == -1) - return; - - iRouteMenuMap imm = gRoutingMenuMap.find(n); - if(imm == gRoutingMenuMap.end()) - return; - if(imm->second.type != Route::MIDI_PORT_ROUTE) - return; - Route &aRoute = imm->second; - int chbit = aRoute.channel; - Route bRoute(track, chbit); - int mdidx = aRoute.midiPort; - - MidiPort* mp = &midiPorts[mdidx]; - MidiDevice* md = mp->device(); - if(!md) - return; - - //if(!(md->rwFlags() & 2)) - if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) - return; - - int chmask = 0; - iRoute iir = rl->begin(); - for (; iir != rl->end(); ++iir) - { - //if(*iir == (dst ? bRoute : aRoute)) - //if(*iir == aRoute) - if(iir->type == Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // p3.3.50 Is there already a route to this port? - { - chmask = iir->channel; // p3.3.50 Grab the channel mask. - break; - } - } - //if (iir != rl->end()) - if ((chmask & chbit) == chbit) // p3.3.50 Is the channel's bit(s) set? - { - // disconnect - if(gIsOutRoutingPopupMenu) - audio->msgRemoveRoute(bRoute, aRoute); - else - audio->msgRemoveRoute(aRoute, bRoute); - } - else - { - // connect - if(gIsOutRoutingPopupMenu) - audio->msgAddRoute(bRoute, aRoute); - else - audio->msgAddRoute(aRoute, bRoute); - } - - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - } - else - { - // TODO: Try to move code from AudioStrip::routingPopupMenuActivated into here. - } - //else - //{ - //} -} - -//--------------------------------------------------------- -// prepareRoutingPopupView -//--------------------------------------------------------- - -PopupView* MusE::prepareRoutingPopupView(Track* track, bool dst) -{ - if(!track) - return 0; - - //QPoint ppt = QCursor::pos(); - - if(track->isMidiTrack()) - { - - //QPoint ppt = parent->rect().bottomLeft(); - - //if(dst) - //{ - // TODO - - //} - //else - //{ - RouteList* rl = dst ? track->outRoutes() : track->inRoutes(); - //Route dst(track, -1); - - ///QPopupMenu* pup = new QPopupMenu(parent); - - PopupView* pup = getRoutingPopupView(); - pup->disconnect(); - //connect(pup, SIGNAL(activated(int)), SLOT(routingPopupMenuActivated(int))); - //connect(pup, SIGNAL(aboutToHide()), SLOT(routingPopupMenuAboutToHide())); - - ///pup->setCheckable(true); - - int gid = 0; - //int n; - - // Routes can't be re-read until the message sent from msgAddRoute1() - // has had time to be sent and actually affected the routes. - ///_redisplay: - - pup->clear(); - gRoutingMenuMap.clear(); - gid = 0; - - //MidiInPortList* tl = song->midiInPorts(); - //for(iMidiInPort i = tl->begin();i != tl->end(); ++i) - for(int i = 0; i < MIDI_PORTS; ++i) - { - //MidiInPort* track = *i; - // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick with ports. - MidiPort* mp = &midiPorts[i]; - MidiDevice* md = mp->device(); - if(!md) - continue; - - if(!(md->rwFlags() & (dst ? 1 : 2))) - continue; - - //printf("MusE::prepareRoutingPopupMenu adding submenu portnum:%d\n", i); - - //QMenu* m = menu->addMenu(track->name()); - //QPopupMenu* subp = new QPopupMenu(parent); - //PopupMenu* subp = new PopupMenu(this); - QStandardItem* subp = new QStandardItem(QT_TRANSLATE_NOOP("@default", md->name())); -/// connect(subp, SIGNAL(activated(int)), pup, SIGNAL(activated(int))); - //connect(subp, SIGNAL(aboutToHide()), pup, SIGNAL(aboutToHide())); - - int chanmask = 0; - // p3.3.50 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(iRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i) - { - // We have a route to the midi port. Grab the channel mask. - chanmask = ir->channel; - break; - } - } - - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - { - //QAction* a = m->addAction(QString("Channel %1").arg(ch+1)); - //subp->insertItem(QT_TRANSLATE_NOOP("@default", QString("Channel %1").arg(ch+1)), i * MIDI_CHANNELS + ch); - gid = i * MIDI_CHANNELS + ch; - - //printf("MusE::prepareRoutingPopupMenu inserting gid:%d\n", gid); - -/// subp->insertItem(QString("Channel %1").arg(ch+1), gid); - QStandardItem* sti = new QStandardItem(QString("Channel %1").arg(ch+1)); - sti->setCheckable(true); - sti->setData(gid); - subp->appendRow(sti); - - //a->setCheckable(true); - //Route src(track, ch, RouteNode::TRACK); - //Route src(md, ch); - //Route r = Route(src, dst); - //a->setData(QVariant::fromValue(r)); - //a->setChecked(rl->indexOf(r) != -1); - - //Route srcRoute(md, ch); - //Route srcRoute(i, ch); // p3.3.49 New: Midi port route. - int chbit = 1 << ch; - Route srcRoute(i, chbit); // p3.3.50 In accordance with new channel mask, use the bit position. - - gRoutingMenuMap.insert( pRouteMenuMap(gid, srcRoute) ); - - //for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) // p3.3.50 Removed. - //{ - //if(*ir == dst) - // if(*ir == srcRoute) - // { - // subp->setItemChecked(id, true); - // break; - // } - //} - if(chanmask & chbit) // p3.3.50 Is the channel already set? Show item check mark. -/// subp->setItemChecked(gid, true); - sti->setCheckState(Qt::Checked); - } - //subp->insertItem(QString("Toggle all"), 1000+i); - // p3.3.50 One route with all channel bits set. - gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. -/// subp->insertItem(QString("Toggle all"), gid); - QStandardItem* sti = new QStandardItem(QString("Toggle all")); - sti->setData(gid); - subp->appendRow(sti); - - Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. - gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); - -/// pup->insertItem(QT_TRANSLATE_NOOP("@default", md->name()), subp); - pup->model()->appendRow(subp); - pup->updateView(); - } - - /* - QPopupMenu* pup = new QPopupMenu(iR); - pup->setCheckable(true); - //MidiTrack* t = (MidiTrack*)track; - RouteList* irl = track->inRoutes(); - - MidiTrack* t = (MidiTrack*)track; - int gid = 0; - 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)); - pup->insertItem(titel); - - if (!checkAudioDevice()) return; - std::list<QString> ol = audioDevice->outputPorts(); - for (std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) { - int id = pup->insertItem(*ip, (gid * 16) + i); - Route dst(*ip, true, i); - ++gid; - for (iRoute ir = irl->begin(); ir != irl->end(); ++ir) { - if (*ir == dst) { - pup->setItemChecked(id, true); - break; - } - } - } - if (i+1 != channel) - pup->addSeparator(); - } - */ - -/// if(pup->count() == 0) - if(pup->model()->rowCount() == 0) - { - ///delete pup; - gRoutingPopupMenuMaster = 0; - //pup->clear(); - //pup->disconnect(); - gRoutingMenuMap.clear(); - //oR->setDown(false); - return 0; - } - - gIsOutRoutingPopupMenu = dst; - return pup; - } - - return 0; -} -#endif - -//--------------------------------------------------------- // saveAs //--------------------------------------------------------- diff --git a/muse2/muse/app.h b/muse2/muse/app.h index 70aac8fc..256154e2 100644 --- a/muse2/muse/app.h +++ b/muse2/muse/app.h @@ -36,8 +36,7 @@ class Transport; class BigTime; class Arranger; class Instrument; -class PopupMenu; -class PopupView; +class RoutePopupMenu; class Track; class PrinterConfig; class MidiSyncConfig; @@ -69,8 +68,6 @@ class ScoreEdit; #define MENU_ADD_SYNTH_ID_BASE 0x1000 - - //--------------------------------------------------------- // MusE //--------------------------------------------------------- @@ -164,10 +161,8 @@ class MusE : public QMainWindow QMenu* menu_functions, *menuScriptPlugins; QMenu* select, *master, *midiEdit, *addTrack; - // Special 'stay-open' menu for routes. - PopupMenu* routingPopupMenu; - //PopupView* routingPopupView; - + // Special common menu for routes. Used (so far) by audio and midi strip, and midi trackinfo. + RoutePopupMenu* routingPopupMenu; QMenu* follow; QMenu* midiInputPlugins; @@ -195,7 +190,7 @@ class MusE : public QMainWindow QMenu* openRecent; bool readMidi(FILE*); - void read(Xml& xml, bool skipConfig); + void read(Xml& xml, bool skipConfig, bool isTemplate); void processTrack(MidiTrack* track); void write(Xml& xml) const; @@ -286,7 +281,7 @@ class MusE : public QMainWindow void startMidiTransformer(); void writeGlobalConfiguration() const; - void startEditInstrument(); + //void startEditInstrument(); void startClipList(bool); void openRecentMenu(); @@ -337,7 +332,6 @@ class MusE : public QMainWindow private: void adjustGlobalLists(int startPos, int diff); - public slots: bool saveAs(); void bounceToFile(AudioOutput* ao = 0); @@ -357,8 +351,7 @@ class MusE : public QMainWindow void importMidi(const QString &file); void setUsedTool(int); void showDidYouKnowDialog(); - - void routingPopupMenuAboutToHide(); + void startEditInstrument(); void configMidiPorts(); public: @@ -370,7 +363,6 @@ class MusE : public QMainWindow bool importMidi(const QString name, bool merge); void kbAccel(int); void changeConfig(bool writeFlag); - void seqStop(); bool seqStart(); void setHeartBeat(); @@ -383,18 +375,8 @@ class MusE : public QMainWindow QWidget* bigtimeWindow(); bool importWaveToTrack(QString& name, unsigned tick=0, Track* track=NULL); void importPartToTrack(QString& filename, unsigned tick, Track* track); - void showTransport(bool flag); - - // Special 'stay-open' menu for routes. - PopupMenu* getRoutingPopupMenu(); - PopupMenu* prepareRoutingPopupMenu(Track* /*track*/, bool /*dst*/); - void routingPopupMenuActivated(Track* /*track*/, int /*id*/); - void updateRouteMenus(Track* /*track*/, QObject* /*master*/); - // Testing... - //PopupView* getRoutingPopupView(); - //PopupView* prepareRoutingPopupView(Track* /*track*/, bool /*dst*/); - //void routingPopupViewActivated(Track* /*track*/, int /*id*/); + RoutePopupMenu* getRoutingPopupMenu(); #ifdef HAVE_LASH void lash_idle_cb (); diff --git a/muse2/muse/appearance.h b/muse2/muse/appearance.h index ef99adbe..ec266f8e 100644 --- a/muse2/muse/appearance.h +++ b/muse2/muse/appearance.h @@ -15,6 +15,10 @@ class GlobalConfigValues; //--------------------------------------------------------- class Appearance : public QDialog, public Ui::AppearanceDialogBase { + + Q_OBJECT + + private: Arranger* arr; QColor* color; GlobalConfigValues* config; @@ -24,7 +28,7 @@ class Appearance : public QDialog, public Ui::AppearanceDialogBase { QTreeWidgetItem* lastSelectedBgItem; QTreeWidgetItem* lastSelectedColorItem; - Q_OBJECT + void updateFonts(); void updateColor(); diff --git a/muse2/muse/arranger/arranger.cpp b/muse2/muse/arranger/arranger.cpp index d74bed78..e1205d6f 100644 --- a/muse2/muse/arranger/arranger.cpp +++ b/muse2/muse/arranger/arranger.cpp @@ -69,6 +69,7 @@ void Arranger::setHeaderToolTips() header->setToolTip(COL_OPORT, tr("Midi output port or synth midi port")); header->setToolTip(COL_TIMELOCK, tr("Time Lock")); header->setToolTip(COL_AUTOMATION, tr("Automation parameter selection")); + header->setToolTip(COL_CLEF, tr("Notation clef")); } @@ -87,6 +88,7 @@ void Arranger::setHeaderWhatsThis() header->setWhatsThis(COL_OCHANNEL, tr("Midi/drum track: Output channel number.\nAudio track: Channels.\nMid/right-click to change.")); header->setWhatsThis(COL_OPORT, tr("Midi/drum track: Output port.\nSynth track: Assigned midi port.\nLeft-click to change.\nRight-click to show GUI.")); header->setWhatsThis(COL_TIMELOCK, tr("Time lock")); + header->setToolTip(COL_CLEF, tr("Notation clef. Select this tracks notation clef.")); } //--------------------------------------------------------- @@ -290,6 +292,7 @@ Arranger::Arranger(QMainWindow* parent, const char* name) header->setColumnLabel(tr("Ch"), COL_OCHANNEL, 30); header->setColumnLabel(tr("T"), COL_TIMELOCK, fm1.width('T')+fw); header->setColumnLabel(tr("Automation"), COL_AUTOMATION, 75); + header->setColumnLabel(tr("Clef"), COL_CLEF, 75); header->setResizeMode(COL_RECORD, QHeaderView::Fixed); header->setResizeMode(COL_MUTE, QHeaderView::Fixed); header->setResizeMode(COL_SOLO, QHeaderView::Fixed); @@ -299,6 +302,7 @@ Arranger::Arranger(QMainWindow* parent, const char* name) header->setResizeMode(COL_OCHANNEL, QHeaderView::Fixed); header->setResizeMode(COL_TIMELOCK, QHeaderView::Fixed); header->setResizeMode(COL_AUTOMATION, QHeaderView::Interactive); + header->setResizeMode(COL_CLEF, QHeaderView::Interactive); setHeaderToolTips(); setHeaderWhatsThis(); diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index 006b9333..d65b8957 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -1624,7 +1624,7 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi int hoffset = (mt->height() - th ) / 2; // offset from bottom if (ctrl_type == CTRL_PITCH) - p.drawLine(t, hoffset + r.y() + th/2, t, hoffset + r.y() + val*th/8192/2 + th/2); + p.drawLine(t, hoffset + r.y() + th/2, t, hoffset + r.y() - val*th/8192/2 + th/2); } } @@ -1641,7 +1641,7 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi int hoffset = (mt->height() - th ) / 2; // offset from bottom if (ctrl_type == 10) - p.drawLine(t, hoffset + r.y() + val*th/127, t, hoffset + r.y() + th); + p.drawLine(t, hoffset + r.y() + th - val*th/127, t, hoffset + r.y() + th); } } @@ -1658,7 +1658,7 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi int hoffset = (mt->height() - th ) / 2; // offset from bottom if (ctrl_type == 7) - p.drawLine(t, hoffset + r.y() + val*th/127, t, hoffset + r.y() + th); + p.drawLine(t, hoffset + r.y() + th - val*th/127, t, hoffset + r.y() + th); } } @@ -1690,7 +1690,7 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi if (config.canvasShowPartType & 4) //y-stretch? { - for (iEvent i = events->begin(); i != ito; ++i) + for (iEvent i = events->begin(); i != events->end(); ++i) { if (i->second.type()==Note) { @@ -1725,6 +1725,19 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi lowest_pitch--; highest_pitch++; } + + if (heavyDebugMsg) + { + if (!isdrum) + printf("DEBUG: arranger: cakewalk enabled, y-stretching from %i to %i. eventlist=%p\n",lowest_pitch, highest_pitch, events); + else + { + printf("DEBUG: arranger: cakewalk enabled, y-stretching drums: ");; + for (map<int,int>::iterator it=y_mapper.begin(); it!=y_mapper.end(); it++) + printf("%i ", it->first); + printf("; eventlist=%p\n",events); + } + } } else { @@ -1734,6 +1747,8 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi if (isdrum) for (int cnt=0;cnt<127;cnt++) y_mapper[cnt]=cnt; + + if (heavyDebugMsg) printf("DEBUG: arranger: cakewalk enabled, y-stretch disabled\n"); } p.setPen(QColor(color_brightness,color_brightness,color_brightness)); diff --git a/muse2/muse/arranger/pcanvas.h b/muse2/muse/arranger/pcanvas.h index e48af2d4..18e47426 100644 --- a/muse2/muse/arranger/pcanvas.h +++ b/muse2/muse/arranger/pcanvas.h @@ -57,6 +57,7 @@ class CtrlVal; //--------------------------------------------------------- class PartCanvas : public Canvas { + Q_OBJECT int* _raster; TrackList* tracks; @@ -69,7 +70,7 @@ class PartCanvas : public Canvas { AutomationObject automation; //std::vector<TrackAutomationView*> automationViews; - Q_OBJECT + virtual void keyPress(QKeyEvent*); virtual void mousePress(QMouseEvent*); virtual void mouseMove(QMouseEvent* event); diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index 4b531607..4f050c46 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -11,7 +11,7 @@ #include <QKeyEvent> #include <QLineEdit> -#include <QMenu> +//#include <QMenu> #include <QMessageBox> #include <QMouseEvent> #include <QPainter> @@ -21,6 +21,7 @@ #include <QScrollBar> #include <QWheelEvent> #include <QIcon> +#include <QSpinBox> #include "popupmenu.h" #include "globals.h" @@ -44,6 +45,7 @@ #include "midiedit/drummap.h" #include "synth.h" #include "config.h" +#include "popupmenu.h" #ifdef DSSI_SUPPORT #include "dssihost.h" @@ -230,13 +232,15 @@ void TList::paint(const QRect& r) switch (section) { case COL_RECORD: - if (track->canRecord()) { + if (track->canRecord() && !header->isSectionHidden(COL_RECORD)) { drawCenteredPixmap(p, track->recordFlag() ? record_on_Icon : record_off_Icon, r); } break; case COL_CLASS: { + if (header->isSectionHidden(COL_CLASS)) + break; const QPixmap* pm = 0; switch(type) { case Track::MIDI: @@ -297,7 +301,11 @@ void TList::paint(const QRect& r) { QString s; int n; - if (track->isMidiTrack()) { + if (track->isMidiTrack() && track->type() == Track::DRUM) { + p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, "-"); + break; + } + else if (track->isMidiTrack()) { n = ((MidiTrack*)track)->outChannel() + 1; } else { @@ -356,6 +364,18 @@ void TList::paint(const QRect& r) p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); } break; + case COL_CLEF: + if (track->isMidiTrack()) { + QString s = tr("no clef"); + if (((MidiTrack*)track)->getClef() == trebleClef) + s=tr("Treble"); + else if (((MidiTrack*)track)->getClef() == bassClef) + s=tr("Bass"); + else if (((MidiTrack*)track)->getClef() == grandStaff) + s=tr("Grand"); + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); + } + break; default: break; } @@ -419,6 +439,20 @@ void TList::returnPressed() setFocus(); } +void TList::chanValueChanged(int val) +{ + Track* track = editTrack->clone(false); + ((MidiTrack*)editTrack)->setOutChannel(val-1); + audio->msgChangeTrack(track, editTrack); +} + +void TList::chanValueFinished() +{ + editTrack = 0; + chan_edit->hide(); + setFocus(); +} + //--------------------------------------------------------- // adjustScrollbar //--------------------------------------------------------- @@ -472,7 +506,7 @@ void TList::mouseDoubleClickEvent(QMouseEvent* ev) if (section == COL_NAME) { editTrack = t; if (editor == 0) { - editor = new QLineEdit(this); + editor = new QLineEdit(this); /*connect(editor, SIGNAL(returnPressed()), SLOT(returnPressed()));*/ editor->setFrame(true); @@ -483,6 +517,25 @@ void TList::mouseDoubleClickEvent(QMouseEvent* ev) editMode = true; editor->show(); } + else if (section == COL_OCHANNEL) { + if (t->isMidiTrack() && t->type() != Track::DRUM) + { + editTrack=t; + if (chan_edit==0) { + chan_edit=new QSpinBox(this); + chan_edit->setMinimum(1); + chan_edit->setMaximum(16); + connect(chan_edit, SIGNAL(valueChanged(int)), SLOT(chanValueChanged(int))); + connect(chan_edit, SIGNAL(editingFinished()), SLOT(chanValueFinished())); + } + chan_edit->setValue(((MidiTrack*)editTrack)->outChannel()+1); + int w=colw; + if (w < chan_edit->sizeHint().width()) w=chan_edit->sizeHint().width(); + chan_edit->setGeometry(colx, coly, w, colh); + chan_edit->show(); + chan_edit->setFocus(); + } + } else mousePressEvent(ev); } @@ -883,9 +936,11 @@ void TList::changeAutomationColor(QAction* act) //--------------------------------------------------------- // colorMenu //--------------------------------------------------------- -QMenu* TList::colorMenu(QColor c, int id) +//QMenu* TList::colorMenu(QColor c, int id) +PopupMenu* TList::colorMenu(QColor c, int id) { - QMenu * m = new QMenu(this); + //QMenu * m = new QMenu(this); + PopupMenu * m = new PopupMenu(this); //, true); TODO for (int i = 0; i< 6; i++) { QPixmap pix(10,10); QPainter p(&pix); @@ -1054,11 +1109,39 @@ void TList::mousePressEvent(QMouseEvent* ev) mode = START_DRAG; switch (col) { + case COL_CLEF: + if (t->isMidiTrack()) { + QMenu* p = new QMenu; + p->addAction(tr("Treble clef"))->setData(0); + p->addAction(tr("Bass clef"))->setData(1); + p->addAction(tr("Grand Staff"))->setData(2); + + // Show the menu + QAction* act = p->exec(ev->globalPos(), 0); + if (act) { + switch (act->data().toInt()) { + case 0: + ((MidiTrack*)t)->setClef(trebleClef); + break; + case 1: + ((MidiTrack*)t)->setClef(bassClef); + break; + case 2: + ((MidiTrack*)t)->setClef(grandStaff); + break; + default: + break; + } + } + delete p; + } + + break; case COL_AUTOMATION: { if (!t->isMidiTrack()) { editAutomation = t; - PopupMenu* p = new PopupMenu(); + PopupMenu* p = new PopupMenu(true); p->disconnect(); p->clear(); p->setTitle(tr("Viewable automation")); @@ -1075,12 +1158,11 @@ void TList::mousePressEvent(QMouseEvent* ev) int data = cl->id() * 256; // shift 8 bits data += 150; // illegal color > 100 act->setData(data); - QMenu *m = colorMenu(cl->color(), cl->id()); + //QMenu *m = colorMenu(cl->color(), cl->id()); + PopupMenu *m = colorMenu(cl->color(), cl->id()); act->setMenu(m); } connect(p, SIGNAL(triggered(QAction*)), SLOT(changeAutomation(QAction*))); - //connect(p, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); - //p->popup(QCursor::pos()); p->exec(QCursor::pos()); delete p; @@ -1236,8 +1318,10 @@ void TList::mousePressEvent(QMouseEvent* ev) { MidiTrack* mt = dynamic_cast<MidiTrack*>(t); if (mt == 0) - break; - + break; + if (mt->type() == Track::DRUM) + break; + int channel = mt->outChannel(); channel += delta; if(channel >= MIDI_CHANNELS) @@ -1511,6 +1595,9 @@ void TList::wheelEvent(QWheelEvent* ev) case COL_OCHANNEL: if (t->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)t; + if (mt && mt->type() == Track::DRUM) + break; + int channel = mt->outChannel() + delta; if (channel >= MIDI_CHANNELS) diff --git a/muse2/muse/arranger/tlist.h b/muse2/muse/arranger/tlist.h index 8bebef95..607ca8c0 100644 --- a/muse2/muse/arranger/tlist.h +++ b/muse2/muse/arranger/tlist.h @@ -14,12 +14,14 @@ class QKeyEvent; class QLineEdit; +class QSpinBox; class QMouseEvent; class QPaintEvent; class QResizeEvent; class QScrollBar; class QWheelEvent; -class QMenu; +//class QMenu; +class PopupMenu; class ScrollScale; class Track; @@ -36,6 +38,7 @@ enum TrackColumn { COL_OCHANNEL, COL_TIMELOCK, COL_AUTOMATION, + COL_CLEF, COL_NONE = -1 }; @@ -55,6 +58,7 @@ class TList : public QWidget { Header* header; QScrollBar* _scroll; QLineEdit* editor; + QSpinBox* chan_edit; Track* editTrack; Track* editAutomation; @@ -85,10 +89,13 @@ class TList : public QWidget { void classesPopupMenu(Track*, int x, int y); TrackList getRecEnabledTracks(); void setHeaderToolTips(); - QMenu* colorMenu(QColor c, int id); + //QMenu* colorMenu(QColor c, int id); + PopupMenu* colorMenu(QColor c, int id); private slots: void returnPressed(); + void chanValueChanged(int); + void chanValueFinished(); void songChanged(int flags); void changeAutomation(QAction*); void changeAutomationColor(QAction*); diff --git a/muse2/muse/audioconvert.cpp b/muse2/muse/audioconvert.cpp index 552b5e95..ced8e703 100644 --- a/muse2/muse/audioconvert.cpp +++ b/muse2/muse/audioconvert.cpp @@ -5,7 +5,8 @@ // // (C) Copyright 1999-2009 Werner Schweer (ws@seh.de) // -// Audio converter module created by Tim terminator356 +// Audio converter module created by Tim +// (C) Copyright 2009-2011 Tim E. Real (terminator356 A T sourceforge D O T net) //========================================================= #include <math.h> diff --git a/muse2/muse/audioconvert.h b/muse2/muse/audioconvert.h index 039af912..0933de60 100644 --- a/muse2/muse/audioconvert.h +++ b/muse2/muse/audioconvert.h @@ -5,7 +5,8 @@ // // (C) Copyright 1999-2009 Werner Schweer (ws@seh.de) // -// Audio converter module created by Tim terminator356 +// Audio converter module created by Tim +// (C) Copyright 2009-2011 Tim E. Real (terminator356 A T sourceforge D O T net) //========================================================= #ifndef __AUDIOCONVERT_H__ diff --git a/muse2/muse/audiotrack.cpp b/muse2/muse/audiotrack.cpp index c427a55c..b004638f 100644 --- a/muse2/muse/audiotrack.cpp +++ b/muse2/muse/audiotrack.cpp @@ -34,6 +34,7 @@ bool WaveTrack::_isVisible=true; // Jack often shuts down during file save, causing the routes to be lost in the file. // cacheJackRouteNames() is ONLY called from MusE::save() in app.cpp // Update: Not required any more because the real problem was Jack RT priority, which has been fixed. +// Keep this around for now. It may come in handy if we want to preserve route names with dummy audio driver! /* typedef std::multimap <const int, QString> jackRouteNameMap; std::map <const AudioTrack*, jackRouteNameMap > jackRouteNameCache; diff --git a/muse2/muse/cleftypes.h b/muse2/muse/cleftypes.h new file mode 100644 index 00000000..8c14a6d3 --- /dev/null +++ b/muse2/muse/cleftypes.h @@ -0,0 +1,13 @@ +//========================================================= +// MusE +// Linux Music Editor +// cleftypes.h +// (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) +//========================================================= + +#ifndef __CLEFTYPES_H__ +#define __CLEFTYPES_H__ + +enum clefTypes { trebleClef, bassClef, grandStaff }; + +#endif diff --git a/muse2/muse/cliplist/cliplist.cpp b/muse2/muse/cliplist/cliplist.cpp index 967c608d..96636463 100644 --- a/muse2/muse/cliplist/cliplist.cpp +++ b/muse2/muse/cliplist/cliplist.cpp @@ -235,7 +235,7 @@ void ClipListEdit::clipSelectionChanged() editor->start->setEnabled(false); editor->len->setEnabled(false); return; -#if 0 +/* } editor->start->setEnabled(true); editor->len->setEnabled(true); @@ -246,7 +246,7 @@ void ClipListEdit::clipSelectionChanged() len.setFrame(curClip.lenFrame()); editor->start->setValue(pos); editor->len->setValue(len); -#endif +*/ } //--------------------------------------------------------- diff --git a/muse2/muse/conf.cpp b/muse2/muse/conf.cpp index e7eef503..89cdd04d 100644 --- a/muse2/muse/conf.cpp +++ b/muse2/muse/conf.cpp @@ -481,6 +481,8 @@ static void readSeqConfiguration(Xml& xml) rcGotoLeftMarkNote = xml.parseInt(); else if (tag == "rcPlay") rcPlayNote = xml.parseInt(); + else if (tag == "rcSteprec") + rcSteprecNote = xml.parseInt(); else xml.unknown("Seq"); break; @@ -946,6 +948,9 @@ void readConfiguration(Xml& xml, bool readOnlySequencer) config.projectStoreInFolder = xml.parseInt(); else if (tag == "useProjectSaveDialog") config.useProjectSaveDialog = xml.parseInt(); + else if (tag == "popupsDefaultStayOpen") + config.popupsDefaultStayOpen = xml.parseInt(); + else xml.unknown("configuration"); break; @@ -1061,6 +1066,7 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) xml.intTag(level, "rcRecord", rcRecordNote); xml.intTag(level, "rcGotoLeft", rcGotoLeftMarkNote); xml.intTag(level, "rcPlay", rcPlayNote); + xml.intTag(level, "rcSteprec", rcSteprecNote); if (writePortInfo) { // @@ -1214,7 +1220,6 @@ void MusE::writeGlobalConfiguration(int level, Xml& xml) const xml.intTag(level, "midiFilterCtrl2", midiFilterCtrl2); xml.intTag(level, "midiFilterCtrl3", midiFilterCtrl3); xml.intTag(level, "midiFilterCtrl4", midiFilterCtrl4); - // Removed by Tim. p3.3.6 //xml.intTag(level, "txDeviceId", txDeviceId); //xml.intTag(level, "rxDeviceId", rxDeviceId); @@ -1223,7 +1228,8 @@ void MusE::writeGlobalConfiguration(int level, Xml& xml) const xml.strTag(level, "externalWavEditor", config.externalWavEditor); xml.intTag(level, "useOldStyleStopShortCut", config.useOldStyleStopShortCut); xml.intTag(level, "moveArmedCheckBox", config.moveArmedCheckBox); - + xml.intTag(level, "popupsDefaultStayOpen", config.popupsDefaultStayOpen); + //for (int i = 0; i < 6; ++i) { for (int i = 0; i < NUM_FONTS; ++i) { char buffer[32]; @@ -1276,39 +1282,8 @@ void MusE::writeGlobalConfiguration(int level, Xml& xml) const xml.colorTag(level, "auxTrackBg", config.auxTrackBg); xml.colorTag(level, "synthTrackBg", config.synthTrackBg); - // Changed by Tim. p3.3.6 - + // Removed by Tim. p3.3.6 //xml.intTag(level, "txSyncPort", txSyncPort); - /* - // To keep old muse versions happy... - bool mcsync = mmc = mtc = false; - for(int sp = 0; sp < MIDI_PORTS; ++sp) - { - MidiSyncTxPort* txPort = &midiSyncTxPorts[sp]; - if(txPort->doMCSync() || txPort->doMMC() || txPort->doMTC()) - { - if(txPort->doMCSync()) - mcsync = true; - if(txPort->doMMC()) - mmc = true; - if(txPort->doMTC()) - mtc = true; - xml.intTag(level, "txSyncPort", sp); - break; - } - } - */ - - // Added by Tim. p3.3.6 - - //xml.tag(level++, "midiSyncInfo"); - //for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) - //{ - // MidiDevice* md = *id; - // (*id)->syncInfo().write(level, xml, md); - //} - //xml.etag(level, "midiSyncInfo"); - //xml.intTag(level, "rxSyncPort", rxSyncPort); xml.intTag(level, "mtctype", mtcType); xml.nput(level, "<mtcoffset>%02d:%02d:%02d:%02d:%02d</mtcoffset>\n", diff --git a/muse2/muse/confmport.cpp b/muse2/muse/confmport.cpp index 8c27d9eb..8b323fc1 100644 --- a/muse2/muse/confmport.cpp +++ b/muse2/muse/confmport.cpp @@ -507,7 +507,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) //Route dst(*ip, true, i); //Route rt(*ip, (dev->rwFlags() & 1), -1, Route::JACK_ROUTE); Route rt(*ip, (col == DEVCOL_OUTROUTES), -1, Route::JACK_ROUTE); - for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if (*ir == rt) { @@ -553,7 +553,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) Route srcRoute(dev, -1); Route dstRoute(s, true, -1, Route::JACK_ROUTE); - iRoute iir = rl->begin(); + ciRoute iir = rl->begin(); for(; iir != rl->end(); ++iir) { if(*iir == dstRoute) @@ -573,7 +573,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) Route srcRoute(s, false, -1, Route::JACK_ROUTE); Route dstRoute(dev, -1); - iRoute iir = rl->begin(); + ciRoute iir = rl->begin(); for(; iir != rl->end(); ++iir) { if(*iir == srcRoute) @@ -609,7 +609,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) return; #else { - defpup = new PopupMenu(this); + defpup = new PopupMenu(this, true); defpup->addAction(new MenuTitleItem("Channel", defpup)); QAction* act = 0; int chbits = midiPorts[no].defaultInChannels(); @@ -649,7 +649,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) return; #else { - defpup = new PopupMenu(this); + defpup = new PopupMenu(this, true); defpup->addAction(new MenuTitleItem("Channel", defpup)); QAction* act = 0; int chbits = midiPorts[no].defaultOutChannels(); diff --git a/muse2/muse/confmport.h b/muse2/muse/confmport.h index 3c139ee2..6901035a 100644 --- a/muse2/muse/confmport.h +++ b/muse2/muse/confmport.h @@ -28,6 +28,7 @@ class Xml; //--------------------------------------------------------- class MPConfig : public QDialog, Ui::SynthConfigBase { + Q_OBJECT QMenu* instrPopup; //QMenu* popup; PopupMenu* defpup; @@ -36,7 +37,7 @@ class MPConfig : public QDialog, Ui::SynthConfigBase { void setToolTip(QTableWidgetItem *item, int col); void addItem(int row, int col, QTableWidgetItem *item, QTableWidget *table); - Q_OBJECT + private slots: void rbClicked(QTableWidgetItem*); diff --git a/muse2/muse/ctrl/ctrlcanvas.h b/muse2/muse/ctrl/ctrlcanvas.h index 300cac19..e6864003 100644 --- a/muse2/muse/ctrl/ctrlcanvas.h +++ b/muse2/muse/ctrl/ctrlcanvas.h @@ -81,6 +81,8 @@ class CEventList: public std::list<CEvent*> { //--------------------------------------------------------- class CtrlCanvas : public View { + Q_OBJECT + MidiEditor* editor; MidiTrack* curTrack; MidiPart* curPart; @@ -120,7 +122,7 @@ class CtrlCanvas : public View { void pdrawItems(QPainter&, const QRect&, const MidiPart*, bool, bool); void partControllers(const MidiPart*, int, int*, int*, MidiController**, MidiCtrlValList**); - Q_OBJECT + protected: enum DragMode { DRAG_OFF, DRAG_NEW, DRAG_MOVE_START, DRAG_MOVE, diff --git a/muse2/muse/ctrl/ctrledit.cpp b/muse2/muse/ctrl/ctrledit.cpp index 8842ba97..c4e33822 100644 --- a/muse2/muse/ctrl/ctrledit.cpp +++ b/muse2/muse/ctrl/ctrledit.cpp @@ -69,7 +69,8 @@ void CtrlEdit::writeStatus(int level, Xml& xml) { if (canvas->controller()) { xml.tag(level++, "ctrledit"); - xml.strTag(level, "ctrl", canvas->controller()->name()); + //xml.strTag(level, "ctrl", canvas->controller()->name()); + xml.intTag(level, "ctrlnum", canvas->controller()->num()); xml.tag(level, "/ctrledit"); } } @@ -89,6 +90,8 @@ void CtrlEdit::readStatus(Xml& xml) return; case Xml::TagStart: if (tag == "ctrl") { + xml.parse1(); // Obsolete. + /* QString name = xml.parse1(); int portno = canvas->track()->outPort(); MidiPort* port = &midiPorts[portno]; @@ -101,6 +104,11 @@ void CtrlEdit::readStatus(Xml& xml) break; } } + */ + } + else if (tag == "ctrlnum") { + int num = xml.parseInt(); + canvas->setController(num); } else xml.unknown("CtrlEdit"); diff --git a/muse2/muse/ctrl/ctrledit.h b/muse2/muse/ctrl/ctrledit.h index eec235b1..c5f5935e 100644 --- a/muse2/muse/ctrl/ctrledit.h +++ b/muse2/muse/ctrl/ctrledit.h @@ -24,10 +24,11 @@ class Xml; //--------------------------------------------------------- class CtrlEdit : public QWidget { + Q_OBJECT CtrlCanvas* canvas; CtrlPanel* panel; - Q_OBJECT + private slots: void destroy(); diff --git a/muse2/muse/ctrl/ctrlpanel.cpp b/muse2/muse/ctrl/ctrlpanel.cpp index 9e990861..b23ce855 100644 --- a/muse2/muse/ctrl/ctrlpanel.cpp +++ b/muse2/muse/ctrl/ctrlpanel.cpp @@ -11,7 +11,8 @@ #include "ctrlpanel.h" #include "ctrlcanvas.h" -#include <QMenu> +//#include <QMenu> +#include <QAction> #include <QPushButton> #include <QSizePolicy> #include <QHBoxLayout> @@ -20,10 +21,12 @@ #include <math.h> +#include "app.h" #include "globals.h" #include "midictrl.h" #include "instruments/minstrument.h" #include "midiport.h" +#include "mididev.h" #include "xml.h" #include "icons.h" #include "event.h" @@ -37,6 +40,8 @@ #include "doublelabel.h" #include "midi.h" #include "audio.h" +#include "menutitleitem.h" +#include "popupmenu.h" //--------------------------------------------------------- // CtrlPanel @@ -47,6 +52,8 @@ CtrlPanel::CtrlPanel(QWidget* parent, MidiEditor* e, const char* name) { setObjectName(name); inHeartBeat = true; + //ctrlMainPop = 0; + //ctrlSubPop = 0; editor = e; setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QVBoxLayout* vbox = new QVBoxLayout; @@ -514,6 +521,7 @@ void CtrlPanel::setHeight(int h) setFixedHeight(h); } +#if 0 struct CI { QString s; bool used; @@ -673,6 +681,261 @@ void CtrlPanel::ctrlPopup() } } +#else // p4.0.25 Tim +struct CI { + int num; + QString s; + bool used; + bool instrument; + CI(int n, const QString& ss, bool u, bool i) : num(n), s(ss), used(u), instrument(i) {} + }; + +void CtrlPanel::ctrlPopup() + { + //--------------------------------------------------- + // build list of midi controllers for current + // MidiPort/channel + //--------------------------------------------------- + + PartList* parts = editor->parts(); + Part* part = editor->curCanvasPart(); + MidiTrack* track = (MidiTrack*)(part->track()); + int channel = track->outChannel(); + MidiPort* port = &midiPorts[track->outPort()]; + int curDrumInstrument = editor->curDrumInstrument(); + bool isDrum = track->type() == Track::DRUM; + MidiInstrument* instr = port->instrument(); + MidiControllerList* mcl = instr->controller(); + + MidiCtrlValListList* cll = port->controller(); + int min = channel << 24; + int max = min + 0x1000000; + + std::list<CI> sList; + typedef std::list<CI>::iterator isList; + + for (iMidiCtrlValList i = cll->lower_bound(min); i != cll->lower_bound(max); ++i) { + MidiCtrlValList* cl = i->second; + MidiController* c = port->midiController(cl->num()); + // dont show drum specific controller if not a drum track + if ((c->num() & 0xff) == 0xff) { + if (!isDrum) + continue; + // only show controller for curDrumInstrument: + if ((cl->num() & 0xff) != drumMap[curDrumInstrument].anote) { + continue; + } + } + isList i = sList.begin(); + for (; i != sList.end(); ++i) { + //if (i->s == c->name()) + if (i->num == c->num()) + break; + } + if (i == sList.end()) { + bool used = false; + for (iPart ip = parts->begin(); ip != parts->end(); ++ip) { + EventList* el = ip->second->events(); + for (iEvent ie = el->begin(); ie != el->end(); ++ie) { + Event e = ie->second; + if ((e.type() == Controller) && (e.dataA() == cl->num())) { + used = true; + break; + } + } + if (used) + break; + } + //sList.push_back(CI(c->name(), used)); + bool isinstr = ( mcl->find(c->num()) != mcl->end() ); + int cnum = c->num(); + // Need to distinguish between global default controllers and + // instrument defined controllers. Instrument takes priority over global + // ie they 'overtake' definition of a global controller such that the + // global def is no longer available. + sList.push_back(CI(cnum, + isinstr ? midiCtrlNumString(cnum, true) + c->name() : midiCtrlName(cnum, true), + used, isinstr)); + } + } + + PopupMenu* ctrlMainPop = new PopupMenu; + + //ctrlMainPop->addSeparator(); + ctrlMainPop->addAction(new MenuTitleItem(tr("Instrument-defined"), ctrlMainPop)); + + //ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 1); + + // Add instrument-defined controllers. + for (isList i = sList.begin(); i != sList.end(); ++i) + { + if(!i->instrument) + continue; + if (i->used) + ctrlMainPop->addAction(QIcon(*greendotIcon), i->s)->setData(i->num); + else + ctrlMainPop->addAction(i->s)->setData(i->num); + } + + ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 1); + //ctrlMainPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instruments"))->setData(max + 2); + + ctrlMainPop->addSeparator(); + ctrlMainPop->addAction(new MenuTitleItem(tr("Others"), ctrlMainPop)); + + //ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 3); + + ctrlMainPop->addAction(tr("Velocity"))->setData(max); + + // Add global default controllers (all controllers not found in instrument). + for (isList i = sList.begin(); i != sList.end(); ++i) + { + if(i->instrument) + continue; + if (i->used) + ctrlMainPop->addAction(QIcon(*greendotIcon), i->s)->setData(i->num); + else + ctrlMainPop->addAction(i->s)->setData(i->num); + } + + ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 3); + + //connect(ctrlMainPop, SIGNAL(hovered(QAction*)), SLOT(ctrlMainPopHovered(QAction*))); + + QAction *act = ctrlMainPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); + selCtrl->setDown(false); + + if (!act) + { + delete ctrlMainPop; + return; + } + + int rv = act->data().toInt(); + delete ctrlMainPop; + + if (rv == max) { // special case velocity + emit controllerChanged(CTRL_VELOCITY); + } + else if (rv == max + 1) { // add new instrument controller + + PopupMenu * ctrlSubPop = new PopupMenu(this); + ctrlSubPop->addAction(new MenuTitleItem(tr("Instrument-defined"), ctrlSubPop)); + + // + // populate popup with all controllers available for + // current instrument + // + + //ctrlSubPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instruments"))->setData(max + 2); + + for (iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + int num = ci->second->num(); + if((num & 0xff) == 0xff) + { + // dont show drum specific controller if not a drum track + if(!isDrum) + continue; + num = (num & ~0xff) + drumMap[curDrumInstrument].anote; + } + + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(midiCtrlNumString(num, true) + ci->second->name())->setData(num); + } + + // Don't allow editing instrument if it's a synth + if(!port->device() || port->device()->deviceType() != MidiDevice::SYNTH_MIDI) + ctrlSubPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instrument ..."))->setData(max + 2); + + //connect(ctrlSubPop, SIGNAL(hovered(QAction*)), SLOT(ctrlSubPopHovered(QAction*))); + + QAction *act2 = ctrlSubPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); + if (act2) + { + int rv2 = act2->data().toInt(); + + if (rv2 == max + 2) // edit instrument + muse->startEditInstrument(); + else // select new instrument control + { + MidiController* c; + for (iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + c = ci->second; + int num = c->num(); + if (isDrum && ((num & 0xff) == 0xff)) + num = (num & ~0xff) + drumMap[curDrumInstrument].anote; + + if(num != rv2) + continue; + + if(cll->find(channel, num) == cll->end()) + { + MidiCtrlValList* vl = new MidiCtrlValList(num); + + cll->add(channel, vl); + emit controllerChanged(c->num()); + //song->update(SC_MIDI_CONTROLLER_ADD); + } + else + emit controllerChanged(c->num()); + break; + } + } + } + delete ctrlSubPop; + } + + //else if (rv == max + 2) // edit instrument + // muse->startEditInstrument(); + + else if (rv == max + 3) { // add new other controller + PopupMenu* ctrlSubPop = new PopupMenu(this); + ctrlSubPop->addAction(new MenuTitleItem(tr("Common Controls"), ctrlSubPop)); + + for(int num = 0; num < 127; ++num) + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(midiCtrlName(num, true))->setData(num); + QAction *act2 = ctrlSubPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); + if (act2) { + int rv2 = act2->data().toInt(); + int num = rv2; + if (isDrum && ((num & 0xff) == 0xff)) + num = (num & ~0xff) + drumMap[curDrumInstrument].anote; + if(cll->find(channel, num) == cll->end()) + { + MidiCtrlValList* vl = new MidiCtrlValList(num); + + cll->add(channel, vl); + emit controllerChanged(rv2); + //song->update(SC_MIDI_CONTROLLER_ADD); + } + else + emit controllerChanged(rv2); + } + delete ctrlSubPop; + } + else { // Select a control + //QString s = act->text(); + iMidiCtrlValList i = cll->begin(); + for (; i != cll->end(); ++i) { + MidiCtrlValList* cl = i->second; + MidiController* c = port->midiController(cl->num()); + //if (c->name() == s) { + if (c->num() == rv) { + emit controllerChanged(c->num()); + break; + } + } + if (i == cll->end()) { + //printf("CtrlPanel: controller %s not found!", s.toLatin1().constData()); + printf("CtrlPanel: controller number %d not found!", rv); + } + } + } +#endif + //--------------------------------------------------------- // ctrlRightClicked //--------------------------------------------------------- diff --git a/muse2/muse/ctrl/ctrlpanel.h b/muse2/muse/ctrl/ctrlpanel.h index a0e5f915..3f6de205 100644 --- a/muse2/muse/ctrl/ctrlpanel.h +++ b/muse2/muse/ctrl/ctrlpanel.h @@ -12,7 +12,6 @@ class MidiController; -class QMenu; class QPushButton; class MidiEditor; @@ -26,7 +25,9 @@ class MidiTrack; //--------------------------------------------------------- class CtrlPanel: public QWidget { - ///QMenu* pop; + Q_OBJECT + + //QMenu* pop; QPushButton* selCtrl; MidiEditor* editor; @@ -38,7 +39,7 @@ class CtrlPanel: public QWidget { DoubleLabel* _dl; int _val; - Q_OBJECT + signals: void destroyPanel(); diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp index f70cf3d3..c4d7a8ca 100644 --- a/muse2/muse/driver/jack.cpp +++ b/muse2/muse/driver/jack.cpp @@ -711,7 +711,7 @@ void JackAudioDevice::connectJackMidiPorts() if(port) // { RouteList* rl = md->outRoutes(); - for (iRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = rl->begin(); r != rl->end(); ++r) connect(port, r->jackPort); } } @@ -724,7 +724,7 @@ void JackAudioDevice::connectJackMidiPorts() if(port) // { RouteList* rl = md->inRoutes(); - for (iRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = rl->begin(); r != rl->end(); ++r) connect(r->jackPort, port); } } @@ -915,7 +915,7 @@ void JackAudioDevice::graphChanged() // the "right" amount for (int i = 0;i < 20;i++) { erased = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { if (irl->channel != channel) continue; QString name = irl->name(); @@ -953,7 +953,7 @@ void JackAudioDevice::graphChanged() const char** pn = ports; while (*pn) { bool found = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { if (irl->channel != channel) continue; QString name = irl->name(); @@ -1002,7 +1002,7 @@ void JackAudioDevice::graphChanged() // the "right" amount for (int i = 0; i < 20 ; i++) { erased = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { if (irl->channel != channel) continue; QString name = irl->name(); @@ -1039,7 +1039,7 @@ void JackAudioDevice::graphChanged() const char** pn = ports; while (*pn) { bool found = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { if (irl->channel != channel) continue; QString name = irl->name(); @@ -1113,7 +1113,7 @@ void JackAudioDevice::graphChanged() for (int i = 0; i < 20 ; i++) { erased = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1155,7 +1155,7 @@ void JackAudioDevice::graphChanged() const char** pn = ports; while (*pn) { bool found = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1212,7 +1212,7 @@ void JackAudioDevice::graphChanged() for (int i = 0; i < 20 ; i++) { erased = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1253,7 +1253,7 @@ void JackAudioDevice::graphChanged() const char** pn = ports; while (*pn) { bool found = false; - for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1473,7 +1473,7 @@ void JackAudioDevice::start(int /*priority*/) for (int ch = 0; ch < channel; ++ch) { RouteList* rl = ai->inRoutes(); void* port = ai->jackPort(ch); - for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { + for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if (ir->channel == ch) connect(ir->jackPort, port); } @@ -1486,7 +1486,7 @@ void JackAudioDevice::start(int /*priority*/) for (int ch = 0; ch < channel; ++ch) { RouteList* rl = ai->outRoutes(); void* port = ai->jackPort(ch); - for (iRoute r = rl->begin(); r != rl->end(); ++r) { + for (ciRoute r = rl->begin(); r != rl->end(); ++r) { if (r->channel == ch) { connect(port, r->jackPort); } diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index c950e096..1765fabb 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -974,13 +974,17 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event) } // - // transfer noteOn events to gui for step recording and keyboard - // remote control + // transfer noteOn and Off events to gui for step recording and keyboard + // remote control (changed by flo93: added noteOff-events) // if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); song->putEvent(pv); } + else if (typ == ME_NOTEOFF) { + int pv = ((event.dataA() & 0xff)<<8) + (0x00); //send an event with velo=0 + song->putEvent(pv); + } //if(_recordFifo.put(MidiPlayEvent(event))) // printf("MidiJackDevice::recordEvent: fifo overflow\n"); diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp index 49a63643..2384ed02 100644 --- a/muse2/muse/dssihost.cpp +++ b/muse2/muse/dssihost.cpp @@ -37,7 +37,7 @@ #include <QDir> #include <QFileInfo> -#include <QMenu> +//#include <QMenu> #include "dssihost.h" #include "synth.h" @@ -61,6 +61,8 @@ #include "globaldefs.h" //#include "al/dsp.h" #include "gconfig.h" +#include "popupmenu.h" + /* static lo_server_thread serverThread; @@ -3388,7 +3390,7 @@ const char* DssiSynthIF::getPatchName(int /*chan*/, int prog, MType /*type*/, bo //--------------------------------------------------------- //void DssiSynthIF::populatePatchPopup(QMenu* menu, int) -void DssiSynthIF::populatePatchPopup(QMenu* menu, int /*ch*/, MType /*type*/, bool /*drum*/) +void DssiSynthIF::populatePatchPopup(PopupMenu* menu, int /*ch*/, MType /*type*/, bool /*drum*/) { // The plugin can change the programs, patches etc. // So make sure we're up to date by calling queryPrograms. diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h index 096c84c7..b917bbf6 100644 --- a/muse2/muse/dssihost.h +++ b/muse2/muse/dssihost.h @@ -42,7 +42,8 @@ #include "plugin.h" -#include <QMenu> +//#include <QMenu> +#include "popupmenu.h" #define DSSI_PARAMSAVE_VERSION_MAJOR 0 #define DSSI_PARAMSAVE_VERSION_MINOR 1 @@ -199,7 +200,8 @@ class DssiSynthIF : public SynthIF, public PluginIBase virtual const char* getPatchName(int, int, MType, bool); //virtual void populatePatchPopup(QMenu*, int); - virtual void populatePatchPopup(QMenu*, int, MType, bool); + //virtual void populatePatchPopup(QMenu*, int, MType, bool); + virtual void populatePatchPopup(PopupMenu*, int, MType, bool); //virtual void write(Xml& xml) const; virtual void write(int level, Xml& xml) const; diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index ba16640c..4a65d19c 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -13,9 +13,19 @@ #include "audio.h" #include "gconfig.h" +#include <values.h> #include <iostream> - +#include <errno.h> +#include <values.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <QMimeData> +#include <QByteArray> +#include <QDrag> #include <QMessageBox> +#include <QClipboard> using namespace std; @@ -28,6 +38,7 @@ Setlen* set_notelen_dialog=NULL; Move* move_notes_dialog=NULL; Transpose* transpose_dialog=NULL; Crescendo* crescendo_dialog=NULL; +Legato* legato_dialog=NULL; void init_function_dialogs(QWidget* parent) { @@ -40,6 +51,7 @@ void init_function_dialogs(QWidget* parent) move_notes_dialog = new Move(parent); transpose_dialog = new Transpose(parent); crescendo_dialog = new Crescendo(parent); + legato_dialog = new Legato(parent); } set<Part*> partlist_to_set(PartList* pl) @@ -52,6 +64,13 @@ set<Part*> partlist_to_set(PartList* pl) return result; } +set<Part*> part_to_set(Part* p) +{ + set<Part*> result; + result.insert(p); + return result; +} + bool is_relevant(const Event& event, const Part* part, int range) { unsigned tick; @@ -109,7 +128,8 @@ bool quantize_notes(const set<Part*>& parts) return false; quantize_notes(parts, quantize_dialog->range, (config.division*4)/(1<<quantize_dialog->raster_power2), - quantize_dialog->strength, quantize_dialog->swing, quantize_dialog->threshold); + quantize_dialog->quant_len, quantize_dialog->strength, quantize_dialog->swing, + quantize_dialog->threshold); return true; } @@ -119,7 +139,8 @@ bool erase_notes(const set<Part*>& parts) if (!erase_dialog->exec()) return false; - erase_notes(parts,erase_dialog->range); + erase_notes(parts,erase_dialog->range, erase_dialog->velo_threshold, erase_dialog->velo_thres_used, + erase_dialog->len_threshold, erase_dialog->len_thres_used ); return true; } @@ -180,9 +201,19 @@ bool crescendo(const set<Part*>& parts) return true; } +bool legato(const set<Part*>& parts) +{ + if (!legato_dialog->exec()) + return false; + + legato(parts,legato_dialog->range, legato_dialog->min_len, !legato_dialog->allow_shortening); + + return true; +} + -void modify_velocity(const set<Part*>& parts, int range, int rate, int offset) +bool modify_velocity(const set<Part*>& parts, int range, int rate, int offset) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -212,12 +243,13 @@ void modify_velocity(const set<Part*>& parts, int range, int rate, int offset) } } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void modify_off_velocity(const set<Part*>& parts, int range, int rate, int offset) +bool modify_off_velocity(const set<Part*>& parts, int range, int rate, int offset) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -247,12 +279,13 @@ void modify_off_velocity(const set<Part*>& parts, int range, int rate, int offse } } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void modify_notelen(const set<Part*>& parts, int range, int rate, int offset) +bool modify_notelen(const set<Part*>& parts, int range, int rate, int offset) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -280,14 +313,15 @@ void modify_notelen(const set<Part*>& parts, int range, int rate, int offset) } } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void set_notelen(const set<Part*>& parts, int range, int len) +bool set_notelen(const set<Part*>& parts, int range, int len) { - modify_notelen(parts, range, 0, len); + return modify_notelen(parts, range, 0, len); } unsigned quantize_tick(unsigned tick, unsigned raster, int swing) @@ -312,7 +346,7 @@ unsigned quantize_tick(unsigned tick, unsigned raster, int swing) return tick_dest3; } -void quantize_notes(const set<Part*>& parts, int range, int raster, int strength, int swing, int threshold) +bool quantize_notes(const set<Part*>& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -336,7 +370,7 @@ void quantize_notes(const set<Part*>& parts, int range, int raster, int strength unsigned end_tick = begin_tick + len; int len_diff = quantize_tick(end_tick, raster, swing) - end_tick; - if (abs(len_diff) > threshold) + if ((abs(len_diff) > threshold) && quant_len) len = len + len_diff*strength/100; if (len <= 0) @@ -352,12 +386,13 @@ void quantize_notes(const set<Part*>& parts, int range, int raster, int strength } } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void erase_notes(const set<Part*>& parts, int range) +bool erase_notes(const set<Part*>& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -369,15 +404,19 @@ void erase_notes(const set<Part*>& parts, int range) Event& event=*(it->first); Part* part=it->second; - operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false)); + if ( (!velo_thres_used && !len_thres_used) || + (velo_thres_used && event.velo() < velo_threshold) || + (len_thres_used && int(event.lenTick()) < len_threshold) ) + operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false)); } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void transpose_notes(const set<Part*>& parts, int range, signed int halftonesteps) +bool transpose_notes(const set<Part*>& parts, int range, signed int halftonesteps) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -397,12 +436,13 @@ void transpose_notes(const set<Part*>& parts, int range, signed int halftonestep operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void crescendo(const set<Part*>& parts, int range, int start_val, int end_val, bool absolute) +bool crescendo(const set<Part*>& parts, int range, int start_val, int end_val, bool absolute) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -434,12 +474,13 @@ void crescendo(const set<Part*>& parts, int range, int start_val, int end_val, b operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO FINDMICH: safety checks +bool move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO: clipping { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -450,18 +491,36 @@ void move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO FIN { Event& event=*(it->first); Part* part=it->second; + bool del=false; Event newEvent = event.clone(); - newEvent.setTick(event.tick()+ticks); - operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); + if ((signed)event.tick()+ticks < 0) //don't allow moving before the part's begin + newEvent.setTick(0); + else + newEvent.setTick(event.tick()+ticks); + + if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end, clip + { + if (part->lenTick() > newEvent.tick()) + newEvent.setLenTick(part->lenTick() - newEvent.tick()); + else + del=true; //if the new length would be <= 0, erase the note + } + + if (del==false) + operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); + else + operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false)); } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); } + else + return false; } -void delete_overlaps(const set<Part*>& parts, int range) + +bool delete_overlaps(const set<Part*>& parts, int range) { map<Event*, Part*> events = get_events(parts, range); Undo operations; @@ -510,11 +569,266 @@ void delete_overlaps(const set<Part*>& parts, int range) } } - if (!operations.empty()) - song->applyOperationGroup(operations); + return song->applyOperationGroup(operations); + } + else + return false; +} + +bool legato(const set<Part*>& parts, int range, int min_len, bool dont_shorten) +{ + map<Event*, Part*> events = get_events(parts, range); + Undo operations; + + if (min_len<=0) min_len=1; + + if (!events.empty()) + { + for (map<Event*, Part*>::iterator it1=events.begin(); it1!=events.end(); it1++) + { + Event& event1=*(it1->first); + Part* part1=it1->second; + + unsigned len=MAXINT; + // we may NOT optimize by letting it2 start at (it1 +1); this optimisation + // is only allowed when events was sorted by time. it is, however, sorted + // randomly by pointer. + for (map<Event*, Part*>::iterator it2=events.begin(); it2!=events.end(); it2++) + { + Event& event2=*(it2->first); + Part* part2=it2->second; + + bool relevant = (event2.tick() >= event1.tick() + min_len); + if (dont_shorten) + relevant = relevant && (event2.tick() >= event1.endTick()); + + if ( (part1->events()==part2->events()) && // part1 and part2 are the same or are duplicates + relevant && // they're not too near (respect min_len and dont_shorten) + (event2.tick()-event1.tick() < len ) ) // that's the nearest relevant following note + len=event2.tick()-event1.tick(); + } + + if (len==MAXINT) len=event1.lenTick(); // if no following note was found, keep the length + + if (event1.lenTick() != len) + { + Event new_event1 = event1.clone(); + new_event1.setLenTick(len); + + operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, event1, part1, false, false)); + } + } + + return song->applyOperationGroup(operations); + } + else + return false; +} + + + +void copy_notes(const set<Part*>& parts, int range) +{ + QMimeData* drag = selected_events_to_mime(parts,range); + + if (drag) + QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard); +} + +void paste_notes(Part* dest_part) +{ + QString tmp="x-muse-eventlist"; // QClipboard::text() expects a QString&, not a QString :( + QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim. + paste_at(dest_part, s, song->cpos()); +} + +QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) +{ + map<Event*, Part*> events=get_events(parts,range); + + //--------------------------------------------------- + // generate event list from selected events + //--------------------------------------------------- + + EventList el; + unsigned startTick = MAXINT; //will be the tick of the first event or MAXINT if no events are there + + for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++) + { + Event& e = *it->first; + + if (e.tick() < startTick) + startTick = e.tick(); + + el.add(e); + } + + //--------------------------------------------------- + // write events as XML into tmp file + //--------------------------------------------------- + + FILE* tmp = tmpfile(); + if (tmp == 0) + { + fprintf(stderr, "EventCanvas::getTextDrag() fopen failed: %s\n", strerror(errno)); + return 0; + } + + Xml xml(tmp); + int level = 0; + + xml.tag(level++, "eventlist"); + for (ciEvent e = el.begin(); e != el.end(); ++e) + e->second.write(level, xml, -startTick); + xml.etag(--level, "eventlist"); + + //--------------------------------------------------- + // read tmp file into drag Object + //--------------------------------------------------- + + fflush(tmp); + struct stat f_stat; + if (fstat(fileno(tmp), &f_stat) == -1) + { + fprintf(stderr, "PianoCanvas::copy() fstat failed:<%s>\n", + strerror(errno)); + fclose(tmp); + return 0; + } + int n = f_stat.st_size; + char* fbuf = (char*)mmap(0, n+1, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fileno(tmp), 0); + fbuf[n] = 0; + + QByteArray data(fbuf); + QMimeData* md = new QMimeData(); + + md->setData("text/x-muse-eventlist", data); + + munmap(fbuf, n); + fclose(tmp); + + return md; +} + +void paste_at(Part* dest_part, const QString& pt, int pos) +{ + Undo operations; + + Xml xml(pt.toLatin1().constData()); + for (;;) + { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + goto end_of_paste_at; + + case Xml::TagStart: + if (tag == "eventlist") + { + EventList el; + el.read(xml, "eventlist", true); + for (iEvent i = el.begin(); i != el.end(); ++i) + { + Event e = i->second; + int tick = e.tick() + pos - dest_part->tick(); + if (tick<0) + { + printf("ERROR: trying to add event before current part!\n"); + goto end_of_paste_at; + } + + e.setTick(tick); + e.setSelected(true); + int diff = e.endTick()-dest_part->lenTick(); + if (diff > 0) // too short part? extend it + { + Part* newPart = dest_part->clone(); + newPart->setLenTick(newPart->lenTick()+diff); + // Indicate no undo, and do port controller values but not clone parts. + operations.push_back(UndoOp(UndoOp::ModifyPart,dest_part, newPart, true, false)); //FINDMICHJETZT oder andersrum? + dest_part = newPart; // reassign TODO FINDME does this work, or has dest_part to be a nonconst reference? + } + // Indicate no undo, and do not do port controller values and clone parts. + operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false)); + } + song->applyOperationGroup(operations); + goto end_of_paste_at; + } + else + xml.unknown("pasteAt"); + break; + + case Xml::Attribut: + case Xml::TagEnd: + default: + break; + } } + + end_of_paste_at: + song->update(SC_SELECTION); +} + +void select_all(const std::set<Part*>& parts) +{ + for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(true); + } + song->update(SC_SELECTION); } +void select_none(const std::set<Part*>& parts) +{ + for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(false); + } + song->update(SC_SELECTION); +} + +void select_invert(const std::set<Part*>& parts) +{ + for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(!event.selected()); + } + song->update(SC_SELECTION); +} + +void select_in_loop(const std::set<Part*>& parts) +{ + select_none(parts); + for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected((event.tick()>=song->lpos() && event.endTick()<=song->rpos())); + } + song->update(SC_SELECTION); +} + +void select_not_in_loop(const std::set<Part*>& parts) +{ + select_none(parts); + for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(!(event.tick()>=song->lpos() && event.endTick()<=song->rpos())); + } + song->update(SC_SELECTION); +} void read_function_dialog_config(Xml& xml) @@ -554,6 +868,8 @@ void read_function_dialog_config(Xml& xml) transpose_dialog->read_configuration(xml); else if (tag == "crescendo") crescendo_dialog->read_configuration(xml); + else if (tag == "legato") + legato_dialog->read_configuration(xml); else xml.unknown("function_dialogs"); break; @@ -581,6 +897,7 @@ void write_function_dialog_config(int level, Xml& xml) move_notes_dialog->write_configuration(level, xml); transpose_dialog->write_configuration(level, xml); crescendo_dialog->write_configuration(level, xml); + legato_dialog->write_configuration(level, xml); xml.tag(level, "/dialogs"); } diff --git a/muse2/muse/functions.h b/muse2/muse/functions.h index 2b6dc711..bc2bb8f4 100644 --- a/muse2/muse/functions.h +++ b/muse2/muse/functions.h @@ -17,10 +17,13 @@ #include "widgets/function_dialogs/setlen.h" #include "widgets/function_dialogs/move.h" #include "widgets/function_dialogs/deloverlaps.h" +#include "widgets/function_dialogs/legato.h" #include <set> #include "part.h" +class QString; +class QMimeData; extern GateTime* gatetime_dialog; extern Velocity* velocity_dialog; @@ -31,24 +34,27 @@ extern Setlen* set_notelen_dialog; extern Move* move_notes_dialog; extern Transpose* transpose_dialog; extern Crescendo* crescendo_dialog; +extern Legato* legato_dialog; void init_function_dialogs(QWidget* parent); std::set<Part*> partlist_to_set(PartList* pl); +std::set<Part*> part_to_set(Part* p); std::map<Event*, Part*> get_events(const std::set<Part*>& parts, int range); //these functions simply do their job, non-interactively -void modify_velocity(const std::set<Part*>& parts, int range, int rate, int offset=0); -void modify_off_velocity(const std::set<Part*>& parts, int range, int rate, int offset=0); -void modify_notelen(const std::set<Part*>& parts, int range, int rate, int offset=0); -void quantize_notes(const std::set<Part*>& parts, int range, int raster, int strength=100, int swing=0, int threshold=0); -void erase_notes(const std::set<Part*>& parts, int range); -void delete_overlaps(const std::set<Part*>& parts, int range); -void set_notelen(const std::set<Part*>& parts, int range, int len); -void move_notes(const std::set<Part*>& parts, int range, signed int ticks); -void transpose_notes(const std::set<Part*>& parts, int range, signed int halftonesteps); -void crescendo(const std::set<Part*>& parts, int range, int start_val, int end_val, bool absolute); +bool modify_velocity(const std::set<Part*>& parts, int range, int rate, int offset=0); +bool modify_off_velocity(const std::set<Part*>& parts, int range, int rate, int offset=0); +bool modify_notelen(const std::set<Part*>& parts, int range, int rate, int offset=0); +bool quantize_notes(const std::set<Part*>& parts, int range, int raster, bool len=false, int strength=100, int swing=0, int threshold=0); +bool erase_notes(const std::set<Part*>& parts, int range, int velo_threshold=0, bool velo_thres_used=false, int len_threshold=0, bool len_thres_used=false); +bool delete_overlaps(const std::set<Part*>& parts, int range); +bool set_notelen(const std::set<Part*>& parts, int range, int len); +bool move_notes(const std::set<Part*>& parts, int range, signed int ticks); +bool transpose_notes(const std::set<Part*>& parts, int range, signed int halftonesteps); +bool crescendo(const std::set<Part*>& parts, int range, int start_val, int end_val, bool absolute); +bool legato(const std::set<Part*>& parts, int range, int min_len=1, bool dont_shorten=false); //the below functions automatically open the dialog @@ -62,8 +68,21 @@ bool transpose_notes(const std::set<Part*>& parts); bool crescendo(const std::set<Part*>& parts); bool erase_notes(const std::set<Part*>& parts); bool delete_overlaps(const std::set<Part*>& parts); +bool legato(const std::set<Part*>& parts); +//functions for copy'n'paste +void copy_notes(const std::set<Part*>& parts, int range); +void paste_notes(Part* dest_part); +QMimeData* selected_events_to_mime(const std::set<Part*>& parts, int range); +void paste_at(Part* dest_part, const QString& pt, int pos); + +//functions for selections +void select_all(const std::set<Part*>& parts); +void select_none(const std::set<Part*>& parts); +void select_invert(const std::set<Part*>& parts); +void select_in_loop(const std::set<Part*>& parts); +void select_not_in_loop(const std::set<Part*>& parts); //functions for reading and writing default values class Xml; diff --git a/muse2/muse/gconfig.cpp b/muse2/muse/gconfig.cpp index 4d22ad4c..49a6d572 100644 --- a/muse2/muse/gconfig.cpp +++ b/muse2/muse/gconfig.cpp @@ -168,6 +168,7 @@ GlobalConfigValues config = { QString("./"), // projectBaseFolder true, // projectStoreInFolder true, // useProjectSaveDialog - 64 // minControlProcessPeriod + 64, // minControlProcessPeriod + false // popupsDefaultStayOpen }; diff --git a/muse2/muse/gconfig.h b/muse2/muse/gconfig.h index cd236b36..acf39782 100644 --- a/muse2/muse/gconfig.h +++ b/muse2/muse/gconfig.h @@ -143,6 +143,7 @@ struct GlobalConfigValues { bool projectStoreInFolder; bool useProjectSaveDialog; unsigned long minControlProcessPeriod; + bool popupsDefaultStayOpen; }; extern GlobalConfigValues config; diff --git a/muse2/muse/globals.cpp b/muse2/muse/globals.cpp index f8ae4454..7f346f87 100644 --- a/muse2/muse/globals.cpp +++ b/muse2/muse/globals.cpp @@ -347,12 +347,9 @@ unsigned char rcStopNote = 28; unsigned char rcRecordNote = 31; unsigned char rcGotoLeftMarkNote = 33; unsigned char rcPlayNote = 29; +unsigned char rcSteprecNote = 36; bool automation = true; -QObject* gRoutingPopupMenuMaster = 0; -RouteMenuMap gRoutingMenuMap; -bool gIsOutRoutingPopupMenu = false; - uid_t euid, ruid; // effective user id, real user id bool midiSeqRunning = false; diff --git a/muse2/muse/globals.h b/muse2/muse/globals.h index 894f1baf..cee5e815 100644 --- a/muse2/muse/globals.h +++ b/muse2/muse/globals.h @@ -13,7 +13,6 @@ #include "value.h" #include "mtc.h" -#include "route.h" #include <unistd.h> @@ -167,19 +166,11 @@ extern unsigned char rcStopNote; extern unsigned char rcRecordNote; extern unsigned char rcGotoLeftMarkNote; extern unsigned char rcPlayNote; +extern unsigned char rcSteprecNote; extern bool midiSeqRunning; extern bool automation; -class QObject; -// Which audio strip, midi strip, or midi track info strip -// was responsible for popping up the routing menu. -extern QObject* gRoutingPopupMenuMaster; -// Map of routing popup menu item IDs to Routes. -extern RouteMenuMap gRoutingMenuMap; -// Whether the routes popup was shown by clicking the output routes button, or input routes button. -extern bool gIsOutRoutingPopupMenu; - // p3.3.55 #define JACK_MIDI_OUT_PORT_SUFFIX "_out" #define JACK_MIDI_IN_PORT_SUFFIX "_in" diff --git a/muse2/muse/helper.cpp b/muse2/muse/helper.cpp index 605d6f5c..be45e048 100644 --- a/muse2/muse/helper.cpp +++ b/muse2/muse/helper.cpp @@ -6,6 +6,9 @@ //========================================================= #include "helper.h" +#include "part.h" +#include "track.h" +#include "song.h" extern bool hIsB; static const char* vall[] = { @@ -38,3 +41,20 @@ QString pitch2string(int v) } + + +Part* partFromSerialNumber(int serial) +{ + TrackList* tl = song->tracks(); + for (iTrack it = tl->begin(); it != tl->end(); ++it) + { + PartList* pl = (*it)->parts(); + iPart ip; + for (ip = pl->begin(); ip != pl->end(); ++ip) + if (ip->second->sn() == serial) + return ip->second; + } + + printf("ERROR: partFromSerialNumber(%i) wasn't able to find an appropriate part!\n",serial); + return NULL; +} diff --git a/muse2/muse/helper.h b/muse2/muse/helper.h index f772ebf6..109ecbee 100644 --- a/muse2/muse/helper.h +++ b/muse2/muse/helper.h @@ -10,7 +10,11 @@ #include <QString> +class Part; + extern QString pitch2string(int v); +Part* partFromSerialNumber(int serial); + #endif diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp index 4fde7bf3..10cb3ec2 100644 --- a/muse2/muse/instruments/minstrument.cpp +++ b/muse2/muse/instruments/minstrument.cpp @@ -11,7 +11,7 @@ #include <QAction> #include <QDir> #include <QFileInfo> -#include <QMenu> +//#include <QMenu> #include <QMessageBox> #include "minstrument.h" @@ -25,6 +25,7 @@ #include "mpevent.h" #include "midictrl.h" #include "gconfig.h" +#include "popupmenu.h" MidiInstrumentList midiInstruments; MidiInstrument* genericMidiInstrument; @@ -881,7 +882,7 @@ QString MidiInstrument::getPatchName(int channel, int prog, MType mode, bool dru // populatePatchPopup //--------------------------------------------------------- -void MidiInstrument::populatePatchPopup(QMenu* menu, int chan, MType songType, bool drum) +void MidiInstrument::populatePatchPopup(PopupMenu* menu, int chan, MType songType, bool drum) { menu->clear(); int mask = 0; @@ -905,7 +906,9 @@ void MidiInstrument::populatePatchPopup(QMenu* menu, int chan, MType songType, b if (pg.size() > 1) { for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { PatchGroup* pgp = *i; - QMenu* pm = menu->addMenu(pgp->name); + //QMenu* pm = menu->addMenu(pgp->name); + PopupMenu* pm = new PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here. + menu->addMenu(pm); pm->setFont(config.fonts[0]); const PatchList& pl = pgp->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { diff --git a/muse2/muse/instruments/minstrument.h b/muse2/muse/instruments/minstrument.h index a8fb1168..15942537 100644 --- a/muse2/muse/instruments/minstrument.h +++ b/muse2/muse/instruments/minstrument.h @@ -14,7 +14,8 @@ #include <vector> class MidiPort; -class QMenu; +//class QMenu; +class PopupMenu; class MidiPlayEvent; class Xml; class EventList; @@ -118,7 +119,8 @@ class MidiInstrument { virtual void reset(int, MType); virtual QString getPatchName(int,int,MType,bool); - virtual void populatePatchPopup(QMenu*, int, MType, bool); + //virtual void populatePatchPopup(QMenu*, int, MType, bool); + virtual void populatePatchPopup(PopupMenu*, int, MType, bool); void read(Xml&); void write(int level, Xml&); diff --git a/muse2/muse/liste/editevent.cpp b/muse2/muse/liste/editevent.cpp index b162a3e6..fd74d5d3 100644 --- a/muse2/muse/liste/editevent.cpp +++ b/muse2/muse/liste/editevent.cpp @@ -12,7 +12,7 @@ #include <QGridLayout> #include <QLabel> #include <QListWidget> -#include <QMenu> +//#include <QMenu> #include <QMessageBox> #include <QPushButton> #include <QRadioButton> @@ -36,6 +36,7 @@ #include "midiedit/drummap.h" #include "instruments/minstrument.h" #include "midi.h" +#include "popupmenu.h" //--------------------------------------------------------- // string2qhex @@ -679,7 +680,8 @@ EditCtrlDialog::EditCtrlDialog(int tick, const Event& event, void EditCtrlDialog::newController() { - QMenu* pup = new QMenu(this); + //QMenu* pup = new QMenu(this); + PopupMenu* pup = new PopupMenu(this); //pup->setCheckable(this);//not necessary in Qt4 // // populate popup with all controllers available for @@ -715,7 +717,9 @@ void EditCtrlDialog::newController() cll->add(channel, vl); //song->update(SC_MIDI_CONTROLLER_ADD); } - for (int idx = 0; ;++idx) { + //for (int idx = 0; ;++idx) { + int idx = 0; + for (; idx < ctrlList->count() ;++idx) { // p4.0.25 Fix segfault QString str = ctrlList->item(idx)->text(); if (s == str) { @@ -723,13 +727,20 @@ void EditCtrlDialog::newController() ctrlListClicked(ctrlList->item(idx)); break; } - if (str.isNull()) { - ctrlList->addItem(s); - ctrlList->item(idx)->setSelected(true); - ctrlListClicked(ctrlList->item(idx)); - break; - } + //if (str.isNull()) { + // ctrlList->addItem(s); + // ctrlList->item(idx)->setSelected(true); + // ctrlListClicked(ctrlList->item(idx)); + // break; + // } } + if (idx >= ctrlList->count()) { // p4.0.25 Fix segfault + ctrlList->addItem(s); + ctrlList->item(idx)->setSelected(true); + ctrlListClicked(ctrlList->item(idx)); + break; + } + break; } @@ -844,7 +855,8 @@ void EditCtrlDialog::instrPopup() MidiInstrument* instr = midiPorts[port].instrument(); ///instr->populatePatchPopup(pop, channel, song->mtype(), track->type() == Track::DRUM); - QMenu* pup = new QMenu(this); + //QMenu* pup = new QMenu(this); + PopupMenu* pup = new PopupMenu(this); instr->populatePatchPopup(pup, channel, song->mtype(), track->type() == Track::DRUM); ///if(pop->actions().count() == 0) diff --git a/muse2/muse/liste/listedit.h b/muse2/muse/liste/listedit.h index 5cf60a59..397a5e08 100644 --- a/muse2/muse/liste/listedit.h +++ b/muse2/muse/liste/listedit.h @@ -32,6 +32,8 @@ class Xml; //--------------------------------------------------------- class ListEdit : public MidiEditor { + Q_OBJECT + QTreeWidget* liste; QMenu* menuEdit; QActionGroup* insertItems; @@ -43,7 +45,7 @@ class ListEdit : public MidiEditor { enum { CMD_DELETE }; - Q_OBJECT + virtual void closeEvent(QCloseEvent*); virtual void keyPressEvent(QKeyEvent*); void initShortcuts(); diff --git a/muse2/muse/marker/markerview.h b/muse2/muse/marker/markerview.h index a271873c..5ad4f4bd 100644 --- a/muse2/muse/marker/markerview.h +++ b/muse2/muse/marker/markerview.h @@ -49,6 +49,8 @@ class MarkerItem : public QTreeWidgetItem { //--------------------------------------------------------- class MarkerView : public TopWin { + Q_OBJECT + QTreeWidget* table; QLineEdit* editName; ///PosEdit* editSMPTE; @@ -58,7 +60,7 @@ class MarkerView : public TopWin { QToolButton* lock; QToolBar* tools; - Q_OBJECT + virtual void closeEvent(QCloseEvent*); private slots: diff --git a/muse2/muse/master/lmaster.h b/muse2/muse/master/lmaster.h index 150e8236..b2919b23 100644 --- a/muse2/muse/master/lmaster.h +++ b/muse2/muse/master/lmaster.h @@ -114,13 +114,15 @@ class LMasterSigEventItem : public LMasterLViewItem { //--------------------------------------------------------- class LMaster : public MidiEditor { + Q_OBJECT + QTreeWidget* view; QToolBar* tools; QMenu* menuEdit; enum { CMD_DELETE, CMD_INSERT_SIG, CMD_INSERT_TEMPO, CMD_EDIT_BEAT, CMD_EDIT_VALUE, CMD_INSERT_KEY }; - Q_OBJECT + virtual void closeEvent(QCloseEvent*); void updateList(); void insertTempo(const TEvent*); diff --git a/muse2/muse/master/master.h b/muse2/muse/master/master.h index 52040aeb..2415b15e 100644 --- a/muse2/muse/master/master.h +++ b/muse2/muse/master/master.h @@ -26,6 +26,7 @@ class ScrollScale; //--------------------------------------------------------- class Master : public View { + Q_OBJECT enum DragMode { DRAG_OFF, DRAG_NEW, DRAG_MOVE_START, DRAG_MOVE, DRAG_DELETE, DRAG_COPY_START, DRAG_COPY, DRAG_RESIZE, DRAG_LASSO_START, DRAG_LASSO @@ -37,7 +38,7 @@ class Master : public View { DragMode drag; MidiEditor* editor; - Q_OBJECT + virtual void pdraw(QPainter&, const QRect&); virtual void viewMouseMoveEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); diff --git a/muse2/muse/master/masteredit.h b/muse2/muse/master/masteredit.h index 59a5ab05..b2b06291 100644 --- a/muse2/muse/master/masteredit.h +++ b/muse2/muse/master/masteredit.h @@ -40,6 +40,8 @@ class TempoLabel; //--------------------------------------------------------- class MasterEdit : public MidiEditor { + Q_OBJECT + Master* canvas; ScrollScale* hscroll; ScrollScale* vscroll; @@ -62,7 +64,7 @@ class MasterEdit : public MidiEditor { static int _widthInit, _heightInit; static QByteArray _toolbarInit; - Q_OBJECT + virtual void closeEvent(QCloseEvent*); virtual void resizeEvent(QResizeEvent*); virtual void focusOutEvent(QFocusEvent*); diff --git a/muse2/muse/master/tscale.h b/muse2/muse/master/tscale.h index 35fa39f2..d7ce86c9 100644 --- a/muse2/muse/master/tscale.h +++ b/muse2/muse/master/tscale.h @@ -15,8 +15,9 @@ //--------------------------------------------------------- class TScale : public View { - double curTempo; Q_OBJECT + + double curTempo; virtual void viewMouseMoveEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); diff --git a/muse2/muse/midictrl.cpp b/muse2/muse/midictrl.cpp index 66f8d87e..4cf1886a 100644 --- a/muse2/muse/midictrl.cpp +++ b/muse2/muse/midictrl.cpp @@ -139,15 +139,85 @@ void initMidiController() } //--------------------------------------------------------- +// midiCtrlNumString +//--------------------------------------------------------- + +QString midiCtrlNumString(int ctrl, bool fullyQualified) +{ + int h = (ctrl >> 8) & 0xff; + int l = ctrl & 0xff; + QString s1 = QString("%1").arg(h); + QString s2 = ( l == 0xff ? QString("* ") : QString("%1 ").arg(l) ); + MidiController::ControllerType type = midiControllerType(ctrl); + switch (type) + { + case MidiController::Controller7: + if(fullyQualified) + return s2; + else + return QString(); + case MidiController::Controller14: + return s1 + QString("CF") + s2; + case MidiController::RPN: + return s1 + QString("R") + s2; + case MidiController::NRPN: + return s1 + QString("N") + s2; + case MidiController::Pitch: // Don't show internal controller numbers. + return QString(); + case MidiController::Program: + return QString(); + case MidiController::Velo: + return QString(); + case MidiController::RPN14: + return s1 + QString("RF") + s2; + case MidiController::NRPN14: + return s1 + QString("NF") + s2; + } + return s1 + QString("?") + s2; +} + +//--------------------------------------------------------- // midiCtrlName //--------------------------------------------------------- -QString midiCtrlName(int ctrl) +QString midiCtrlName(int ctrl, bool fullyQualified) +{ + //if (ctrl < 0x10000) + // return QString(ctrlName[ctrl]); + //return QString("?N?"); + + // p4.0.25 Tim + int h = (ctrl >> 8) & 0xff; + int l = ctrl & 0xff; + QString s1 = QString("%1").arg(h); + QString s2 = ( l == 0xff ? QString("*") : QString("%1").arg(l) ); + MidiController::ControllerType type = midiControllerType(ctrl); + switch (type) { - if (ctrl < 0x10000) - return QString(ctrlName[ctrl]); - return QString("?N?"); + case MidiController::Controller7: + if(fullyQualified) + return s2 + QString(" ") + QString(ctrlName[l]); + else + return QString(ctrlName[l]); + case MidiController::Controller14: + return s1 + QString("CF") + s2; + case MidiController::RPN: + return s1 + QString("R") + s2; + case MidiController::NRPN: + return s1 + QString("N") + s2; + case MidiController::Pitch: + return QString("Pitch"); + case MidiController::Program: + return QString("Program"); + case MidiController::Velo: + return QString("Velocity"); + case MidiController::RPN14: + return s1 + QString("RF") + s2; + case MidiController::NRPN14: + return s1 + QString("NF") + s2; } + return s1 + QString("?") + s2; +} //--------------------------------------------------------- // MidiController diff --git a/muse2/muse/midictrl.h b/muse2/muse/midictrl.h index 27f8e7be..3b18ba91 100644 --- a/muse2/muse/midictrl.h +++ b/muse2/muse/midictrl.h @@ -243,7 +243,8 @@ extern MidiController::ControllerType midiControllerType(int num); extern const QString& int2ctrlType(int n); extern MidiController::ControllerType ctrlType2Int(const QString& s); -extern QString midiCtrlName(int ctrl); +extern QString midiCtrlName(int ctrl, bool fullyQualified = false); +extern QString midiCtrlNumString(int ctrl, bool fullyQualified = false); extern MidiController veloCtrl; diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index b5445b71..b13f571b 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -315,7 +315,7 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) if (filterEvent(event, midiRecordType, false)) return; - + if (!applyMidiInputTransformation(event)) { if (midiInputTrace) printf(" midi input transformation: event filtered\n"); @@ -323,13 +323,17 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) } // - // transfer noteOn events to gui for step recording and keyboard - // remote control + // transfer noteOn and Off events to gui for step recording and keyboard + // remote control (changed by flo93: added noteOff-events) // if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); song->putEvent(pv); } + else if (typ == ME_NOTEOFF) { + int pv = ((event.dataA() & 0xff)<<8) + (0x00); //send an event with velo=0 + song->putEvent(pv); + } ///if(_recBufFlipped) /// _recordEvents2.add(event); // add event to secondary list of recorded events diff --git a/muse2/muse/midiedit/dcanvas.cpp b/muse2/muse/midiedit/dcanvas.cpp index 61e98aea..92e514af 100644 --- a/muse2/muse/midiedit/dcanvas.cpp +++ b/muse2/muse/midiedit/dcanvas.cpp @@ -34,6 +34,7 @@ #include "audio.h" #include "shortcuts.h" #include "icons.h" +#include "functions.h" #define CARET 10 #define CARET2 5 @@ -88,7 +89,11 @@ DrumCanvas::DrumCanvas(MidiEditor* pr, QWidget* parent, int sx, setVirt(false); cursorPos= QPoint(0,0); _stepSize=1; + + steprec=new StepRec(NULL); + songChanged(SC_TRACK_INSERTED); + connect(song, SIGNAL(midiNote(int, int)), SLOT(midiNote(int,int))); } //--------------------------------------------------------- @@ -538,26 +543,7 @@ int DrumCanvas::pitch2y(int pitch) const void DrumCanvas::cmd(int cmd) { - switch(cmd) { - case CMD_CUT: - copy(); - song->startUndo(); - for (iCItem i = items.begin(); i != items.end(); ++i) { - if (!i->second->isSelected()) - continue; - DEvent* e = (DEvent*)(i->second); - Event event = e->event(); - // Indicate no undo, and do not do port controller values and clone parts. - audio->msgDeleteEvent(event, e->part(), false, false, false); - } - song->endUndo(SC_EVENT_REMOVED); - break; - case CMD_COPY: - copy(); - break; - case CMD_PASTE: - paste(); - break; + switch (cmd) { case CMD_SELECT_ALL: // select all for (iCItem k = items.begin(); k != items.end(); ++k) { if (!k->second->isSelected()) @@ -698,41 +684,12 @@ void DrumCanvas::cmd(int cmd) //--------------------------------------------------------- -// copy -// cut copy paste -//--------------------------------------------------------- - -void DrumCanvas::copy() - { - QMimeData* md = getTextDrag(); - - if (md) - QApplication::clipboard()->setMimeData(md, QClipboard::Clipboard); - } - - -//--------------------------------------------------------- -// paste -// paste events -//--------------------------------------------------------- - -void DrumCanvas::paste() - { - QString stype("x-muse-eventlist"); - - //QString s = QApplication::clipboard()->text(stype, QClipboard::Selection); - QString s = QApplication::clipboard()->text(stype, QClipboard::Clipboard); // TODO CHECK Tim. - - pasteAt(s, song->cpos()); - } - -//--------------------------------------------------------- // startDrag //--------------------------------------------------------- void DrumCanvas::startDrag(CItem* /* item*/, bool copymode) { - QMimeData* md = getTextDrag(); + QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); if (md) { // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. @@ -788,6 +745,10 @@ void DrumCanvas::keyPressed(int index, int velocity) // play note: MidiPlayEvent e(0, port, channel, 0x90, pitch, velocity); audio->msgPlayMidiEvent(&e); + + if (_steprec && pos[0] >= start_tick && pos[0] < end_tick && curPart) + steprec->record(curPart,index,drumMap[index].len,editor->raster(),velocity,globalKeyState&Qt::ControlModifier,globalKeyState&Qt::ShiftModifier); + } //--------------------------------------------------------- @@ -818,7 +779,7 @@ void DrumCanvas::mapChanged(int spitch, int dpitch) typedef std::vector< std::pair<Part*, Event*> >::iterator idel_ev; typedef std::vector< std::pair<Part*, Event> >::iterator iadd_ev; - + MidiTrackList* tracks = song->midis(); for (ciMidiTrack t = tracks->begin(); t != tracks->end(); t++) { MidiTrack* curTrack = *t; @@ -1163,3 +1124,19 @@ void DrumCanvas::moveAwayUnused() used.erase(it++); } } + + +//--------------------------------------------------------- +// midiNote +//--------------------------------------------------------- +void DrumCanvas::midiNote(int pitch, int velo) + { + if (debugMsg) printf("DrumCanvas::midiNote: pitch=%i, velo=%i\n", pitch, velo); + + if (_midiin && _steprec && curPart + && !audio->isPlaying() && velo && pos[0] >= start_tick + && pos[0] < end_tick + && !(globalKeyState & Qt::AltModifier)) { + steprec->record(curPart,drumInmap[pitch],drumMap[(int)drumInmap[pitch]].len,editor->raster(),velo,globalKeyState&Qt::ControlModifier,globalKeyState&Qt::ShiftModifier); + } + } diff --git a/muse2/muse/midiedit/dcanvas.h b/muse2/muse/midiedit/dcanvas.h index cc3b8fff..868113a6 100644 --- a/muse2/muse/midiedit/dcanvas.h +++ b/muse2/muse/midiedit/dcanvas.h @@ -10,6 +10,7 @@ #include "ecanvas.h" #include "song.h" +#include "steprec.h" #define TH 18 @@ -40,12 +41,15 @@ class PianoRoll; //--------------------------------------------------------- class DrumCanvas : public EventCanvas { - + Q_OBJECT + + StepRec* steprec; + // Cursor tool position QPoint cursorPos; int _stepSize; - Q_OBJECT + virtual void drawCanvas(QPainter&, const QRect&); virtual void drawItem(QPainter&, const CItem*, const QRect&); void drawTopItem(QPainter& p, const QRect& rect); @@ -61,8 +65,6 @@ class DrumCanvas : public EventCanvas { int y2pitch(int y) const; int pitch2y(int pitch) const; - void copy(); - void paste(); void startDrag(CItem*, bool copymode); void dragEnterEvent(QDragEnterEvent* event); void dragMoveEvent(QDragMoveEvent*); @@ -75,6 +77,9 @@ class DrumCanvas : public EventCanvas { signals: void newWidth(int); + private slots: + void midiNote(int pitch, int velo); + public slots: void mapChanged(int, int); void keyPressed(int, int); diff --git a/muse2/muse/midiedit/dlist.cpp b/muse2/muse/midiedit/dlist.cpp index 0b8bf3aa..66922e83 100644 --- a/muse2/muse/midiedit/dlist.cpp +++ b/muse2/muse/midiedit/dlist.cpp @@ -23,7 +23,6 @@ #include "song.h" #include "scrollscale.h" - //--------------------------------------------------------- // draw //--------------------------------------------------------- @@ -244,7 +243,7 @@ void DList::viewMousePressEvent(QMouseEvent* ev) dm->mute = !dm->mute; break; case COL_PORT: - if (button == Qt::RightButton) { + if ((button == Qt::RightButton) || (button == Qt::LeftButton)) { bool changeAll = ev->modifiers() & Qt::ControlModifier; devicesPopupMenu(dm, mapx(x), mapy(pitch * TH), changeAll); } @@ -296,7 +295,6 @@ void DList::viewMousePressEvent(QMouseEvent* ev) if(val != dm->anote) { audio->msgIdle(true); - //audio->msgRemapPortDrumCtlEvents(pitch, val, -1, -1); song->remapPortDrumCtrlEvents(pitch, val, -1, -1); audio->msgIdle(false); dm->anote = val; @@ -372,24 +370,7 @@ void DList::viewMousePressEvent(QMouseEvent* ev) case COL_NAME: emit keyPressed(pitch, 100); //Mapping done on other side, send index break; -#if 0 - case COL_CHANNEL: - { - int channel = t->channel(); - if (button == Qt::RightButton) { - if (channel < 15) - ++channel; - } - else if (button == Qt::MidButton) { - if (channel > 0) - --channel; - } - if (channel != t->channel()) { - t->setChannel(channel); - emit channelChanged(); - } - } -#endif + default: break; } @@ -409,10 +390,13 @@ void DList::viewMouseDoubleClickEvent(QMouseEvent* ev) int section = header->logicalIndexAt(x); if ((section == COL_NAME || section == COL_VOL || section == COL_LEN || section == COL_LV1 || - section == COL_LV2 || section == COL_LV3 || section == COL_LV4) && (ev->button() == Qt::LeftButton)) + section == COL_LV2 || section == COL_LV3 || section == COL_LV4 || section == COL_CHANNEL || + section == COL_QNT) && (ev->button() == Qt::LeftButton)) { lineEdit(pitch, section); } + else if ((section == COL_ANOTE || section == COL_ENOTE) && (ev->button() == Qt::LeftButton)) + pitchEdit(pitch, section); else viewMousePressEvent(ev); } @@ -467,6 +451,14 @@ void DList::lineEdit(int line, int section) case COL_LV4: editor->setText(QString::number(dm->lv4)); break; + + case COL_QNT: + editor->setText(QString::number(dm->quant)); + break; + + case COL_CHANNEL: + editor->setText(QString::number(dm->channel+1)); + break; } editor->end(false); @@ -479,6 +471,40 @@ void DList::lineEdit(int line, int section) } +//--------------------------------------------------------- +// pitchEdit +//--------------------------------------------------------- +void DList::pitchEdit(int line, int section) + { + DrumMap* dm = &drumMap[line]; + editEntry = dm; + if (pitch_editor == 0) { + pitch_editor = new DPitchEdit(this); + connect(pitch_editor, SIGNAL(editingFinished()), + SLOT(pitchEdited())); + pitch_editor->setFrame(true); + } + int colx = mapx(header->sectionPosition(section)); + int colw = rmapx(header->sectionSize(section)); + int coly = mapy(line * TH); + int colh = rmapy(TH); + selectedColumn = section; //Store selected column to have an idea of which one was selected when return is pressed + switch (section) { + case COL_ENOTE: + pitch_editor->setValue(dm->enote); + break; + + case COL_ANOTE: + pitch_editor->setValue(dm->anote); + break; + } + + pitch_editor->setGeometry(colx, coly, colw, colh); + pitch_editor->show(); + pitch_editor->setFocus(); + + } + //--------------------------------------------------------- // x2col @@ -534,22 +560,35 @@ void DList::returnPressed() { ///val = atoi(editor->text().ascii()); val = atoi(editor->text().toAscii().constData()); - if (selectedColumn != COL_LEN) + + switch (selectedColumn) { - if(selectedColumn == COL_VOL) - { + case COL_VOL: if (val > 200) //Check bounds for volume val = 200; if (val < 0) val = 0; - } - else - { + break; + + case COL_LV1: + case COL_LV2: + case COL_LV3: + case COL_LV4: if (val > 127) //Check bounds for lv1-lv4 values val = 127; if (val < 0) val = 0; - } + break; + + case COL_CHANNEL: + val--; + if (val >= 16) + val = 15; + if (val < 0) + val = 0; + break; + + default: break; } } @@ -583,6 +622,14 @@ void DList::returnPressed() editEntry->lv4 = val; break; + case COL_QNT: + editEntry->quant = val; + break; + + case COL_CHANNEL: + editEntry->channel = val; + break; + default: printf("Return pressed in unknown column\n"); break; @@ -595,6 +642,52 @@ void DList::returnPressed() } //--------------------------------------------------------- +// pitchValueChanged +//--------------------------------------------------------- + +void DList::pitchEdited() +{ + int val=pitch_editor->value(); + int pitch=(editEntry-drumMap); + + switch(selectedColumn) { + case COL_ANOTE: + if(val != editEntry->anote) + { + audio->msgIdle(true); + song->remapPortDrumCtrlEvents(pitch, val, -1, -1); + audio->msgIdle(false); + editEntry->anote = val; + song->update(SC_DRUMMAP); + } + break; + + case COL_ENOTE: + //Check if there is any other drumMap with the same inmap value (there should be one (and only one):-) + //If so, switch the inmap between the instruments + for (int i=0; i<DRUM_MAPSIZE; i++) { + if (drumMap[i].enote == val && &drumMap[i] != editEntry) { + drumInmap[int(editEntry->enote)] = i; + drumMap[i].enote = editEntry->enote; + break; + } + } + //TODO: Set all the notes on the track with pitch=dm->enote to pitch=val + editEntry->enote = val; + drumInmap[val] = pitch; + break; + default: + printf("Value changed in unknown column\n"); + break; + } + selectedColumn = -1; + pitch_editor->hide(); + editEntry = 0; + setFocus(); + redraw(); + } + +//--------------------------------------------------------- // moved //--------------------------------------------------------- @@ -641,6 +734,7 @@ DList::DList(QHeaderView* h, QWidget* parent, int ymag) setFocusPolicy(Qt::StrongFocus); drag = NORMAL; editor = 0; + pitch_editor = 0; editEntry = 0; // always select a drum instrument currentlySelected = &drumMap[0]; @@ -696,9 +790,9 @@ void DList::viewMouseReleaseEvent(QMouseEvent* ev) emit mapChanged(sPitch, dPitch); //Track pitch change done in canvas } drag = NORMAL; -//?? redraw(); - if (editEntry) - editor->setFocus(); +//?? redraw(); //commented out NOT by flo93; was already commented out +// if (editEntry) //removed by flo93; seems to work without it +// editor->setFocus(); //and causes segfaults after adding the pitchedits int x = ev->x(); int y = ev->y(); bool shift = ev->modifiers() & Qt::ShiftModifier; diff --git a/muse2/muse/midiedit/dlist.h b/muse2/muse/midiedit/dlist.h index f57b7501..00f21c55 100644 --- a/muse2/muse/midiedit/dlist.h +++ b/muse2/muse/midiedit/dlist.h @@ -11,6 +11,7 @@ #include <QKeyEvent> #include <QLineEdit> +#include "awl/pitchedit.h" #include "view.h" #define TH 18 // normal Track-hight @@ -46,13 +47,35 @@ class DLineEdit: public QLineEdit }; //--------------------------------------------------------- +// DPitchEdit +//--------------------------------------------------------- +class DPitchEdit: public Awl::PitchEdit +{ + public: + DPitchEdit(QWidget* parent) : PitchEdit(parent) {} + virtual ~DPitchEdit() {}; + + virtual void keyPressEvent(QKeyEvent* keyItem) { + if ((keyItem->key() == Qt::Key_Escape) || (keyItem->key() == Qt::Key_Return)) { + parentWidget()->setFocus(); + hide(); + } + else + PitchEdit::keyPressEvent(keyItem); + } +}; + +//--------------------------------------------------------- // DList //--------------------------------------------------------- class DList : public View { + Q_OBJECT + QHeaderView* header; ScrollScale* scroll; QLineEdit* editor; + DPitchEdit* pitch_editor; DrumMap* editEntry; DrumMap* currentlySelected; int selectedColumn; @@ -71,12 +94,13 @@ class DList : public View { int x2col(int x) const; void devicesPopupMenu(DrumMap* t, int x, int y, bool changeAll); - Q_OBJECT + //void setCurDrumInstrument(int n); private slots: void sizeChange(int, int, int); void returnPressed(); + void pitchEdited(); void moved(int, int, int); signals: @@ -91,6 +115,7 @@ class DList : public View { void songChanged(int); public: void lineEdit(int line, int section); + void pitchEdit(int line, int section); void setCurDrumInstrument(int n); DList(QHeaderView*, QWidget* parent, int ymag); ~DList(); diff --git a/muse2/muse/midiedit/drumedit.cpp b/muse2/muse/midiedit/drumedit.cpp index 9e64d7a7..1e678432 100644 --- a/muse2/muse/midiedit/drumedit.cpp +++ b/muse2/muse/midiedit/drumedit.cpp @@ -302,7 +302,7 @@ DrumEdit::DrumEdit(PartList* pl, QWidget* parent, const char* name, unsigned ini midiin->setIcon(*midiinIcon); midiin->setCheckable(true); tools->addWidget(midiin); - + tools2 = new EditToolBar(this, drumeditTools); addToolBar(tools2); @@ -903,12 +903,27 @@ void DrumEdit::reset() void DrumEdit::cmd(int cmd) { switch(cmd) { + case DrumCanvas::CMD_CUT: + copy_notes(partlist_to_set(parts()), 1); + erase_notes(partlist_to_set(parts()), 1); + break; + case DrumCanvas::CMD_COPY: copy_notes(partlist_to_set(parts()), 1); break; + case DrumCanvas::CMD_PASTE: + ((DrumCanvas*)canvas)->cmd(DrumCanvas::CMD_SELECT_NONE); + paste_notes(canvas->part()); + break; case DrumCanvas::CMD_LOAD: load(); break; case DrumCanvas::CMD_SAVE: save(); break; case DrumCanvas::CMD_RESET: reset(); break; case DrumCanvas::CMD_MODIFY_VELOCITY: modify_velocity(partlist_to_set(parts())); break; case DrumCanvas::CMD_CRESCENDO: crescendo(partlist_to_set(parts())); break; - case DrumCanvas::CMD_QUANTIZE: quantize_notes(partlist_to_set(parts())); break; + case DrumCanvas::CMD_QUANTIZE: + if (quantize_dialog->exec()) + quantize_notes(partlist_to_set(parts()), quantize_dialog->range, + (config.division*4)/(1<<quantize_dialog->raster_power2), + /* quant_len= */false, quantize_dialog->strength, + quantize_dialog->swing, quantize_dialog->threshold); + break; case DrumCanvas::CMD_ERASE_EVENT: erase_notes(partlist_to_set(parts())); break; case DrumCanvas::CMD_DEL: erase_notes(partlist_to_set(parts()),1); break; //delete selected events case DrumCanvas::CMD_DELETE_OVERLAPS: delete_overlaps(partlist_to_set(parts())); break; diff --git a/muse2/muse/midiedit/drumedit.h b/muse2/muse/midiedit/drumedit.h index 30fe8487..64390cd9 100644 --- a/muse2/muse/midiedit/drumedit.h +++ b/muse2/muse/midiedit/drumedit.h @@ -49,6 +49,8 @@ class SNode; //--------------------------------------------------------- class DrumEdit : public MidiEditor { + Q_OBJECT + Event selEvent; MidiPart* selPart; int selTick; @@ -79,7 +81,7 @@ class DrumEdit : public MidiEditor { QAction *sallAction, *snoneAction, *invAction, *inAction , *outAction; QAction *prevAction, *nextAction; - Q_OBJECT + void initShortcuts(); virtual void closeEvent(QCloseEvent*); diff --git a/muse2/muse/midiedit/ecanvas.cpp b/muse2/muse/midiedit/ecanvas.cpp index 9ce84147..ef47e0d6 100644 --- a/muse2/muse/midiedit/ecanvas.cpp +++ b/muse2/muse/midiedit/ecanvas.cpp @@ -25,6 +25,7 @@ #include "event.h" #include "shortcuts.h" #include "audio.h" +#include "functions.h" //--------------------------------------------------------- // EventCanvas @@ -364,137 +365,6 @@ void EventCanvas::keyPress(QKeyEvent* event) event->ignore(); } -//--------------------------------------------------------- -// getTextDrag -//--------------------------------------------------------- - -//QDrag* EventCanvas::getTextDrag(QWidget* parent) -QMimeData* EventCanvas::getTextDrag() - { - //--------------------------------------------------- - // generate event list from selected events - //--------------------------------------------------- - - EventList el; - unsigned startTick = MAXINT; - for (iCItem i = items.begin(); i != items.end(); ++i) { - if (!i->second->isSelected()) - continue; - ///NEvent* ne = (NEvent*)(i->second); - CItem* ne = i->second; - Event e = ne->event(); - if (startTick == MAXINT) - startTick = e.tick(); - el.add(e); - } - - //--------------------------------------------------- - // write events as XML into tmp file - //--------------------------------------------------- - - FILE* tmp = tmpfile(); - if (tmp == 0) { - fprintf(stderr, "EventCanvas::getTextDrag() fopen failed: %s\n", - strerror(errno)); - return 0; - } - Xml xml(tmp); - - int level = 0; - xml.tag(level++, "eventlist"); - for (ciEvent e = el.begin(); e != el.end(); ++e) - e->second.write(level, xml, -startTick); - xml.etag(--level, "eventlist"); - - //--------------------------------------------------- - // read tmp file into drag Object - //--------------------------------------------------- - - fflush(tmp); - struct stat f_stat; - if (fstat(fileno(tmp), &f_stat) == -1) { - fprintf(stderr, "PianoCanvas::copy() fstat failes:<%s>\n", - strerror(errno)); - fclose(tmp); - return 0; - } - int n = f_stat.st_size; - char* fbuf = (char*)mmap(0, n+1, PROT_READ|PROT_WRITE, - MAP_PRIVATE, fileno(tmp), 0); - fbuf[n] = 0; - - QByteArray data(fbuf); - QMimeData* md = new QMimeData(); - //QDrag* drag = new QDrag(parent); - - md->setData("text/x-muse-eventlist", data); - //drag->setMimeData(md); - - munmap(fbuf, n); - fclose(tmp); - - //return drag; - return md; - } - -//--------------------------------------------------------- -// pasteAt -//--------------------------------------------------------- - -void EventCanvas::pasteAt(const QString& pt, int pos) - { - QByteArray ba = pt.toLatin1(); - const char* p = ba.constData(); - Xml xml(p); - for (;;) { - Xml::Token token = xml.parse(); - const QString& tag = xml.s1(); - switch (token) { - case Xml::Error: - case Xml::End: - return; - case Xml::TagStart: - if (tag == "eventlist") { - Undo operations; - EventList* el = new EventList(); - el->read(xml, "eventlist", true); - int modified = SC_EVENT_INSERTED; - for (iEvent i = el->begin(); i != el->end(); ++i) { - Event e = i->second; - int tick = e.tick() + pos - curPart->tick(); - if (tick<0) { - printf("ERROR: trying to add event before current part!\n"); - delete el; - return; - } - - e.setTick(tick); - int diff = e.endTick()-curPart->lenTick(); - if (diff > 0) {// too short part? extend it - Part* newPart = curPart->clone(); - newPart->setLenTick(newPart->lenTick()+diff); - // Do port controller values but not clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart, curPart, newPart, true, false)); - modified=modified|SC_PART_MODIFIED; - curPart = newPart; // reassign - } - // Do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::AddEvent, e, curPart, false, false)); - } - song->applyOperationGroup(operations); - delete el; - return; - } - else - xml.unknown("pasteAt"); - break; - case Xml::Attribut: - case Xml::TagEnd: - default: - break; - } - } - } //--------------------------------------------------------- // dropEvent @@ -515,7 +385,7 @@ void EventCanvas::viewDropEvent(QDropEvent* event) int x = editor->rasterVal(event->pos().x()); if (x < 0) x = 0; - pasteAt(text, x); + paste_at(curPart, text, x); //event->accept(); // TODO } else { diff --git a/muse2/muse/midiedit/ecanvas.h b/muse2/muse/midiedit/ecanvas.h index b3275607..b847f0f9 100644 --- a/muse2/muse/midiedit/ecanvas.h +++ b/muse2/muse/midiedit/ecanvas.h @@ -80,9 +80,6 @@ class EventCanvas : public Canvas { void range(int* s, int* e) const { *s = start_tick; *e = end_tick; } void playEvents(bool flag) { _playEvents = flag; } void selectAtTick(unsigned int tick); - //QDrag* getTextDrag(QWidget* parent); - QMimeData* getTextDrag(); - void pasteAt(const QString& pt, int pos); void viewDropEvent(QDropEvent* event); virtual void modifySelected(NoteInfo::ValType, int) {} virtual void keyPress(QKeyEvent*); diff --git a/muse2/muse/midiedit/piano.h b/muse2/muse/midiedit/piano.h index 35106d64..f8deec52 100644 --- a/muse2/muse/midiedit/piano.h +++ b/muse2/muse/midiedit/piano.h @@ -23,6 +23,8 @@ class QPixmap; class Piano : public View { + Q_OBJECT + int curPitch; QPixmap* octave; QPixmap* c_keys[10]; @@ -34,7 +36,7 @@ class Piano : public View bool shift; int button; - Q_OBJECT + int y2pitch(int) const; int pitch2y(int) const; void viewMouseMoveEvent(QMouseEvent* event); diff --git a/muse2/muse/midiedit/pianoroll.cpp b/muse2/muse/midiedit/pianoroll.cpp index 9105a446..b2fe55ee 100644 --- a/muse2/muse/midiedit/pianoroll.cpp +++ b/muse2/muse/midiedit/pianoroll.cpp @@ -198,11 +198,11 @@ PianoRoll::PianoRoll(PartList* pl, QWidget* parent, const char* name, unsigned i mapper->setMapping(funcTransposeAction, PianoCanvas::CMD_TRANSPOSE); connect(funcTransposeAction, SIGNAL(triggered()), mapper, SLOT(map())); - funcEraseEventAction = menuFunctions->addAction(tr("Erase Event")); + funcEraseEventAction = menuFunctions->addAction(tr("Erase Events")); mapper->setMapping(funcEraseEventAction, PianoCanvas::CMD_ERASE_EVENT); connect(funcEraseEventAction, SIGNAL(triggered()), mapper, SLOT(map())); - funcNoteShiftAction = menuFunctions->addAction(tr("Note Shift")); + funcNoteShiftAction = menuFunctions->addAction(tr("Move Notes")); mapper->setMapping(funcNoteShiftAction, PianoCanvas::CMD_NOTE_SHIFT); connect(funcNoteShiftAction, SIGNAL(triggered()), mapper, SLOT(map())); @@ -213,7 +213,12 @@ PianoRoll::PianoRoll(PartList* pl, QWidget* parent, const char* name, unsigned i funcDelOverlapsAction = menuFunctions->addAction(tr("Delete Overlaps")); mapper->setMapping(funcDelOverlapsAction, PianoCanvas::CMD_DELETE_OVERLAPS); connect(funcDelOverlapsAction, SIGNAL(triggered()), mapper, SLOT(map())); - + + QAction* funcLegatoAction = menuFunctions->addAction(tr("Legato")); + mapper->setMapping(funcLegatoAction, PianoCanvas::CMD_LEGATO); + connect(funcLegatoAction, SIGNAL(triggered()), mapper, SLOT(map())); + + menuPlugins = menuBar()->addMenu(tr("&Plugins")); song->populateScriptMenu(menuPlugins, this); @@ -600,6 +605,15 @@ void PianoRoll::cmd(int cmd) { switch (cmd) { + case PianoCanvas::CMD_CUT: + copy_notes(partlist_to_set(parts()), 1); + erase_notes(partlist_to_set(parts()), 1); + break; + case PianoCanvas::CMD_COPY: copy_notes(partlist_to_set(parts()), 1); break; + case PianoCanvas::CMD_PASTE: + ((PianoCanvas*)canvas)->cmd(PianoCanvas::CMD_SELECT_NONE); + paste_notes(canvas->part()); + break; case PianoCanvas::CMD_MODIFY_GATE_TIME: modify_notelen(partlist_to_set(parts())); break; case PianoCanvas::CMD_MODIFY_VELOCITY: modify_velocity(partlist_to_set(parts())); break; case PianoCanvas::CMD_CRESCENDO: crescendo(partlist_to_set(parts())); break; @@ -610,6 +624,7 @@ void PianoRoll::cmd(int cmd) case PianoCanvas::CMD_NOTE_SHIFT: move_notes(partlist_to_set(parts())); break; case PianoCanvas::CMD_FIXED_LEN: set_notelen(partlist_to_set(parts())); break; case PianoCanvas::CMD_DELETE_OVERLAPS: delete_overlaps(partlist_to_set(parts())); break; + case PianoCanvas::CMD_LEGATO: legato(partlist_to_set(parts())); break; default: ((PianoCanvas*)canvas)->cmd(cmd); } diff --git a/muse2/muse/midiedit/pianoroll.h b/muse2/muse/midiedit/pianoroll.h index 58c2487a..1f53254d 100644 --- a/muse2/muse/midiedit/pianoroll.h +++ b/muse2/muse/midiedit/pianoroll.h @@ -51,6 +51,8 @@ class QScrollArea; //--------------------------------------------------------- class PianoRoll : public MidiEditor { + Q_OBJECT + Event selEvent; MidiPart* selPart; int selTick; @@ -125,7 +127,7 @@ class PianoRoll : public MidiEditor { //QScrollBar* infoScroll; QScrollArea* infoScroll; - Q_OBJECT + void initShortcuts(); void setEventColorMode(int); QWidget* genToolbar(QWidget* parent); diff --git a/muse2/muse/midiedit/prcanvas.cpp b/muse2/muse/midiedit/prcanvas.cpp index 05fe8252..091582ef 100644 --- a/muse2/muse/midiedit/prcanvas.cpp +++ b/muse2/muse/midiedit/prcanvas.cpp @@ -15,6 +15,8 @@ #include <QDropEvent> #include <QMouseEvent> +#include <set> + #include <values.h> #include <stdio.h> #include <math.h> @@ -84,14 +86,10 @@ PianoCanvas::PianoCanvas(MidiEditor* pr, QWidget* parent, int sx, int sy) { colorMode = 0; playedPitch = -1; + for (int i=0;i<128;i++) noteHeldDown[i]=false; - chordTimer = new QTimer(this); - chordTimer->setSingleShot(true); - chordTimer->setInterval(CHORD_TIMEOUT); - chordTimer->stop(); + steprec=new StepRec(noteHeldDown); - connect(chordTimer, SIGNAL(timeout()), SLOT(chordTimerTimedOut())); - songChanged(SC_TRACK_INSERTED); connect(song, SIGNAL(midiNote(int, int)), SLOT(midiNote(int,int))); } @@ -669,28 +667,10 @@ void PianoCanvas::pianoPressed(int pitch, int velocity, bool shift) // play note: MidiPlayEvent e(0, port, channel, 0x90, pitch, velocity); audio->msgPlayMidiEvent(&e); - - if (_steprec && pos[0] >= start_tick && pos[0] < end_tick) { - if (curPart) { - int len = editor->raster(); - unsigned tick = pos[0] - curPart->tick(); //CDW - if (shift) - tick -= editor->rasterStep(tick); - Event e(Note); - e.setTick(tick); - e.setPitch(pitch); - e.setVelo(127); - e.setLenTick(len); - // Indicate do undo, and do not do port controller values and clone parts. - audio->msgAddEvent(e, curPart, true, false, false); - tick += editor->rasterStep(tick) + curPart->tick(); - if (tick != song->cpos()) { - Pos p(tick, true); - song->setPos(0, p, true, false, true); - } - } + + if (_steprec && pos[0] >= start_tick && pos[0] < end_tick && curPart) { + steprec->record(curPart,pitch,editor->raster(),editor->raster(),velocity,globalKeyState&Qt::ControlModifier,shift); } - } //--------------------------------------------------------- @@ -796,16 +776,6 @@ void PianoCanvas::drawCanvas(QPainter& p, const QRect& rect) void PianoCanvas::cmd(int cmd) { switch (cmd) { - case CMD_CUT: - copy(); - erase_notes(partlist_to_set(editor->parts()),1); //FINDMICH is this correct? or must i do this on current_part? - break; - case CMD_COPY: - copy(); - break; - case CMD_PASTE: - paste(); - break; case CMD_SELECT_ALL: // select all for (iCItem k = items.begin(); k != items.end(); ++k) { if (!k->second->isSelected()) @@ -904,125 +874,40 @@ void PianoCanvas::cmd(int cmd) //--------------------------------------------------------- // midiNote //--------------------------------------------------------- - void PianoCanvas::midiNote(int pitch, int velo) { - if (_midiin && _steprec && curPart - && !audio->isPlaying() && velo && pos[0] >= start_tick - && pos[0] < end_tick - && !(globalKeyState & Qt::AltModifier)) { - chordTimer->stop(); - - //len has been changed by flo: set to raster() instead of quant() - //reason: the quant-toolbar has been removed; the flexibility you - //lose with this can be re-gained by applying a "modify note len" - //on the notes you have entered. - unsigned int len = editor->raster();//prevent compiler warning: comparison singed/unsigned - unsigned tick = pos[0]; //CDW - unsigned starttick = tick; - - // - // extend len of last note? - // - EventList* events = curPart->events(); - if (globalKeyState & Qt::ControlModifier) { - for (iEvent i = events->begin(); i != events->end(); ++i) { - Event ev = i->second; - if (!ev.isNote()) - continue; - if (ev.pitch() == pitch && ((ev.tick() + ev.lenTick()) == /*(int)*/starttick)) { - Event e = ev.clone(); - e.setLenTick(ev.lenTick() + editor->rasterStep(starttick)); - // Indicate do undo, and do not do port controller values and clone parts. - audio->msgChangeEvent(ev, e, curPart, true, false, false); - - if (! (globalKeyState & Qt::ShiftModifier)) { - chordTimer_setToTick = tick + editor->rasterStep(tick); - chordTimer->start(); - } - return; - } - } - } + if (debugMsg) printf("PianoCanvas::midiNote: pitch=%i, velo=%i\n", pitch, velo); - // - // if we already entered the note, delete it - // - EventRange range = events->equal_range(tick); - for (iEvent i = range.first; i != range.second; ++i) { - Event ev = i->second; - if (ev.isNote() && ev.pitch() == pitch) { - // Indicate do undo, and do not do port controller values and clone parts. - audio->msgDeleteEvent(ev, curPart, true, false, false); - - if (! (globalKeyState & Qt::ShiftModifier)) { - chordTimer_setToTick = tick + editor->rasterStep(tick); - chordTimer->start(); - } - - return; - } - } - Event e(Note); - e.setTick(tick - curPart->tick()); - e.setPitch(pitch); - e.setVelo(velo); - e.setLenTick(len); - // Indicate do undo, and do not do port controller values and clone parts. - audio->msgAddEvent(e, curPart, true, false, false); - - if (! (globalKeyState & Qt::ShiftModifier)) { - chordTimer_setToTick = tick + editor->rasterStep(tick); - chordTimer->start(); - } - } - } - -void PianoCanvas::chordTimerTimedOut() -{ - if (chordTimer_setToTick != song->cpos()) - { - Pos p(chordTimer_setToTick, true); - song->setPos(0, p, true, false, true); - } -} - -//--------------------------------------------------------- -// copy -// cut copy paste -//--------------------------------------------------------- + if (velo) + noteHeldDown[pitch]=true; + else + noteHeldDown[pitch]=false; -void PianoCanvas::copy() + if (heavyDebugMsg) { - //QDrag* drag = getTextDrag(); - QMimeData* drag = getTextDrag(); - - if (drag) - QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard); + printf(" held down notes are: "); + for (int i=0;i<128;i++) + if (noteHeldDown[i]) + printf("%i ",i); + printf("\n"); } - -//--------------------------------------------------------- -// paste -// paste events -//--------------------------------------------------------- - -void PianoCanvas::paste() - { - QString stype("x-muse-eventlist"); - - //QString s = QApplication::clipboard()->text(stype, QClipboard::Selection); - QString s = QApplication::clipboard()->text(stype, QClipboard::Clipboard); // TODO CHECK Tim. - pasteAt(s, song->cpos()); + if (_midiin && _steprec && curPart + && !audio->isPlaying() && velo && pos[0] >= start_tick + && pos[0] < end_tick + && !(globalKeyState & Qt::AltModifier)) { + steprec->record(curPart,pitch,editor->raster(),editor->raster(),velo,globalKeyState&Qt::ControlModifier,globalKeyState&Qt::ShiftModifier); + } } + //--------------------------------------------------------- // startDrag //--------------------------------------------------------- void PianoCanvas::startDrag(CItem* /* item*/, bool copymode) { - QMimeData* md = getTextDrag(); + QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); if (md) { // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. diff --git a/muse2/muse/midiedit/prcanvas.h b/muse2/muse/midiedit/prcanvas.h index 7c77b229..c6e47c9e 100644 --- a/muse2/muse/midiedit/prcanvas.h +++ b/muse2/muse/midiedit/prcanvas.h @@ -17,6 +17,8 @@ #include <QDragLeaveEvent> #include <QTimer> +#include "steprec.h" + #define KH 13 //--------------------------------------------------------- @@ -38,13 +40,16 @@ class QRect; //--------------------------------------------------------- class PianoCanvas : public EventCanvas { + Q_OBJECT + int colorMode; int playedPitch; - QTimer* chordTimer; - unsigned chordTimer_setToTick; + bool noteHeldDown[128]; + + StepRec* steprec; - Q_OBJECT + virtual void viewMouseDoubleClickEvent(QMouseEvent*); virtual void drawItem(QPainter&, const CItem*, const QRect&); void drawTopItem(QPainter &p, const QRect &rect); @@ -64,8 +69,6 @@ class PianoCanvas : public EventCanvas { int y2pitch(int) const; int pitch2y(int) const; virtual void drawCanvas(QPainter&, const QRect&); - void copy(); - void paste(); virtual void itemPressed(const CItem*); virtual void itemReleased(const CItem*, const QPoint&); virtual void itemMoved(const CItem*, const QPoint&); @@ -74,7 +77,6 @@ class PianoCanvas : public EventCanvas { private slots: void midiNote(int pitch, int velo); - void chordTimerTimedOut(); signals: void quantChanged(int); @@ -96,7 +98,7 @@ class PianoCanvas : public EventCanvas { CMD_TRANSPOSE, CMD_THIN_OUT, CMD_ERASE_EVENT, CMD_NOTE_SHIFT, CMD_MOVE_CLOCK, CMD_COPY_MEASURE, CMD_ERASE_MEASURE, CMD_DELETE_MEASURE, CMD_CREATE_MEASURE, - CMD_FIXED_LEN, CMD_DELETE_OVERLAPS + CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO }; PianoCanvas(MidiEditor*, QWidget*, int, int); diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index 7f7382f4..0eb61554 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -51,13 +51,13 @@ using namespace std; #include "icons.h" #include "audio.h" #include "functions.h" - +#include "helper.h" #include "cmd.h" #include "sig.h" #include "song.h" +#include "shortcuts.h" //#include "../ctrl/ctrledit.h" -//#include "shortcuts.h" string IntToStr(int i); @@ -211,6 +211,16 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) undo_tools->addActions(undoRedo->actions()); addToolBar(undo_tools); + QToolBar* steprec_tools=addToolBar(tr("Step recording tools")); + steprec_tools->setObjectName("Step recording tools"); + srec = new QToolButton(); + srec->setToolTip(tr("Step Record")); + srec->setIcon(*steprecIcon); + srec->setCheckable(true); + steprec_tools->addWidget(srec); + connect(srec, SIGNAL(toggled(bool)), score_canvas, SLOT(set_steprec(bool))); + + edit_tools = new EditToolBar(this, PointerTool | PencilTool | RubberTool); addToolBar(edit_tools); edit_tools->set(PointerTool); @@ -323,9 +333,59 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) quant_toolbar->addWidget(px_per_whole_spinbox); px_per_whole_spinbox->setValue(300); + QMenu* edit_menu = menuBar()->addMenu(tr("&Edit")); + + edit_menu->addActions(undoRedo->actions()); + edit_menu->addSeparator(); + + cut_action = edit_menu->addAction(QIcon(*editcutIconSet), tr("C&ut")); + menu_mapper->setMapping(cut_action, CMD_CUT); + connect(cut_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + copy_action = edit_menu->addAction(QIcon(*editcopyIconSet), tr("&Copy")); + menu_mapper->setMapping(copy_action, CMD_COPY); + connect(copy_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + paste_action = edit_menu->addAction(QIcon(*editpasteIconSet), tr("&Paste")); + menu_mapper->setMapping(paste_action, CMD_PASTE); + connect(paste_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + edit_menu->addSeparator(); + + del_action = edit_menu->addAction(tr("Delete &Events")); + menu_mapper->setMapping(del_action, CMD_DEL); + connect(del_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + edit_menu->addSeparator(); + + QMenu* select_menu = edit_menu->addMenu(QIcon(*selectIcon), tr("&Select")); + + select_all_action = select_menu->addAction(QIcon(*select_allIcon), tr("Select &All")); + menu_mapper->setMapping(select_all_action, CMD_SELECT_ALL); + connect(select_all_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_none_action = select_menu->addAction(QIcon(*select_deselect_allIcon), tr("&Deselect All")); + menu_mapper->setMapping(select_none_action, CMD_SELECT_NONE); + connect(select_none_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_invert_action = select_menu->addAction(QIcon(*select_invert_selectionIcon), tr("Invert &Selection")); + menu_mapper->setMapping(select_invert_action, CMD_SELECT_INVERT); + connect(select_invert_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_menu->addSeparator(); + + select_iloop_action = select_menu->addAction(QIcon(*select_inside_loopIcon), tr("&Inside Loop")); + menu_mapper->setMapping(select_iloop_action, CMD_SELECT_ILOOP); + connect(select_iloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_oloop_action = select_menu->addAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop")); + menu_mapper->setMapping(select_oloop_action, CMD_SELECT_OLOOP); + connect(select_oloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + QMenu* settings_menu = menuBar()->addMenu(tr("&Settings")); - QMenu* color_menu = settings_menu->addMenu(tr("Note head &colors")); + color_menu = settings_menu->addMenu(tr("Note head &colors")); color_actions = new QActionGroup(this); color_black_action = color_menu->addAction(tr("&Black"), menu_mapper, SLOT(map())); color_velo_action = color_menu->addAction(tr("&Velocity"), menu_mapper, SLOT(map())); @@ -361,14 +421,36 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) QMenu* functions_menu = menuBar()->addMenu(tr("&Functions")); - QAction* func_quantize_action = functions_menu->addAction(tr("&Quantize"), menu_mapper, SLOT(map())); - QAction* func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); - QAction* func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); - QAction* func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); + func_quantize_action = functions_menu->addAction(tr("&Quantize"), menu_mapper, SLOT(map())); + func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); + func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); + func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); + func_transpose_action = functions_menu->addAction(tr("Transpose"), menu_mapper, SLOT(map())); + func_erase_action = functions_menu->addAction(tr("Erase Events"), menu_mapper, SLOT(map())); + func_move_action = functions_menu->addAction(tr("Move Notes"), menu_mapper, SLOT(map())); + func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length"), menu_mapper, SLOT(map())); + func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps"), menu_mapper, SLOT(map())); + func_legato_action = functions_menu->addAction(tr("Legato"), menu_mapper, SLOT(map())); menu_mapper->setMapping(func_quantize_action, CMD_QUANTIZE); menu_mapper->setMapping(func_notelen_action, CMD_NOTELEN); menu_mapper->setMapping(func_velocity_action, CMD_VELOCITY); menu_mapper->setMapping(func_cresc_action, CMD_CRESCENDO); + menu_mapper->setMapping(func_transpose_action, CMD_TRANSPOSE); + menu_mapper->setMapping(func_erase_action, CMD_ERASE); + menu_mapper->setMapping(func_move_action, CMD_MOVE); + menu_mapper->setMapping(func_fixed_len_action, CMD_FIXED_LEN); + menu_mapper->setMapping(func_del_overlaps_action, CMD_DELETE_OVERLAPS); + menu_mapper->setMapping(func_legato_action, CMD_LEGATO); + + init_shortcuts(); + + connect(muse, SIGNAL(configChanged()), SLOT(init_shortcuts())); + + QClipboard* cb = QApplication::clipboard(); + connect(cb, SIGNAL(dataChanged()), SLOT(clipboard_changed())); + + clipboard_changed(); + selection_changed(); if (!default_toolbar_state.isEmpty()) restoreState(default_toolbar_state); @@ -387,6 +469,32 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) apply_velo=true; } +void ScoreEdit::init_shortcuts() +{ + cut_action->setShortcut(shortcuts[SHRT_CUT].key); + copy_action->setShortcut(shortcuts[SHRT_COPY].key); + paste_action->setShortcut(shortcuts[SHRT_PASTE].key); + del_action->setShortcut(shortcuts[SHRT_DELETE].key); + + select_all_action->setShortcut(shortcuts[SHRT_SELECT_ALL].key); + select_none_action->setShortcut(shortcuts[SHRT_SELECT_NONE].key); + select_invert_action->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); + select_iloop_action->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); + select_oloop_action->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); + + color_menu->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); + + func_quantize_action->setShortcut(shortcuts[SHRT_QUANTIZE].key); + func_notelen_action->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); + func_velocity_action->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); + func_transpose_action->setShortcut(shortcuts[SHRT_TRANSPOSE].key); + func_erase_action->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); + func_move_action->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); + func_fixed_len_action->setShortcut(shortcuts[SHRT_FIXED_LEN].key); + func_del_overlaps_action->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); +} + + void ScoreEdit::add_parts(PartList* pl, bool all_in_one) { score_canvas->add_staves(pl, all_in_one); @@ -482,6 +590,8 @@ void ScoreEdit::song_changed(int flags) if (velo>=0) velo_spinbox->setValue(velo); if (velo_off>=0) velo_off_spinbox->setValue(velo_off); } + + selection_changed(); } } @@ -568,16 +678,51 @@ void ScoreEdit::menu_command(int cmd) } break; + case CMD_SELECT_ALL: select_all(score_canvas->get_all_parts()); break; + case CMD_SELECT_NONE: select_none(score_canvas->get_all_parts()); break; + case CMD_SELECT_INVERT: select_invert(score_canvas->get_all_parts()); break; + case CMD_SELECT_ILOOP: select_in_loop(score_canvas->get_all_parts()); break; + case CMD_SELECT_OLOOP: select_not_in_loop(score_canvas->get_all_parts()); break; + + case CMD_CUT: + copy_notes(score_canvas->get_all_parts(), 1); + erase_notes(score_canvas->get_all_parts(), 1); + break; + case CMD_COPY: copy_notes(score_canvas->get_all_parts(), 1); break; + case CMD_PASTE: + menu_command(CMD_SELECT_NONE); + paste_notes(score_canvas->get_selected_part()); + break; case CMD_QUANTIZE: quantize_notes(score_canvas->get_all_parts()); break; case CMD_VELOCITY: modify_velocity(score_canvas->get_all_parts()); break; case CMD_CRESCENDO: crescendo(score_canvas->get_all_parts()); break; case CMD_NOTELEN: modify_notelen(score_canvas->get_all_parts()); break; - + case CMD_TRANSPOSE: transpose_notes(score_canvas->get_all_parts()); break; + case CMD_ERASE: erase_notes(score_canvas->get_all_parts()); break; + case CMD_DEL: erase_notes(score_canvas->get_all_parts(),1); break; + case CMD_MOVE: move_notes(score_canvas->get_all_parts()); break; + case CMD_FIXED_LEN: set_notelen(score_canvas->get_all_parts()); break; + case CMD_DELETE_OVERLAPS: delete_overlaps(score_canvas->get_all_parts()); break; + case CMD_LEGATO: legato(score_canvas->get_all_parts()); break; + default: score_canvas->menu_command(cmd); } } +void ScoreEdit::clipboard_changed() +{ + paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); +} + +void ScoreEdit::selection_changed() +{ + bool flag = !get_events(score_canvas->get_all_parts(),1).empty(); + cut_action->setEnabled(flag); + copy_action->setEnabled(flag); + del_action->setEnabled(flag); +} + //duplicated from songfile.cpp's MusE::readPart(); the only differences: //"none" is supported and tag_name is settable @@ -604,9 +749,11 @@ Part* read_part(Xml& xml, QString tag_name="part") else { sscanf(tag.toLatin1().constData(), "%d:%d", &trackIdx, &partIdx); + if (debugMsg) cout << "read_part: trackIdx="<<trackIdx<<", partIdx="<<partIdx; Track* track = song->tracks()->index(trackIdx); if (track) part = track->parts()->find(partIdx); + if (debugMsg) cout << ", track="<<track<<", part="<<part<<endl; } } break; @@ -656,12 +803,15 @@ void staff_t::read_status(Xml& xml) case Xml::TagEnd: if (tag == "staff") - return; + goto staff_readstatus_end; default: break; } } + + staff_readstatus_end: + update_part_indices(); } @@ -695,6 +845,7 @@ void ScoreEdit::writeStatus(int level, Xml& xml) const xml.strTag(level, "name", name); xml.intTag(level, "tool", edit_tools->curTool()); + xml.intTag(level, "steprec", srec->isChecked()); xml.intTag(level, "quantPower", score_canvas->quant_power2()); xml.intTag(level, "pxPerWhole", score_canvas->pixels_per_whole()); xml.intTag(level, "newNoteVelo", velo_spinbox->value()); @@ -787,6 +938,8 @@ void ScoreEdit::readStatus(Xml& xml) set_name(xml.parse1()); else if (tag == "tool") edit_tools->set(xml.parseInt()); + else if (tag == "steprec") + srec->setChecked(xml.parseInt()); else if (tag == "quantPower") quant_combobox->setCurrentIndex(xml.parseInt()-1); else if (tag == "pxPerWhole") @@ -911,55 +1064,101 @@ void ScoreEdit::write_configuration(int level, Xml& xml) void ScoreCanvas::add_staves(PartList* pl, bool all_in_one) { - staff_t staff(this); - - if (all_in_one) + if (!pl->empty()) { - staff.parts.clear(); - for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) - staff.parts.insert(part_it->second); - staff.cleanup_parts(); - - staff.type=GRAND_TOP; //FINDME_INITCLEF - staff.clef=VIOLIN; - staves.push_back(staff); - - staff.type=GRAND_BOTTOM; - staff.clef=BASS; - staves.push_back(staff); - } - else - { - set<Track*> tracks; - for (ciPart it=pl->begin(); it!=pl->end(); it++) - tracks.insert(it->second->track()); - - TrackList* tracklist = song->tracks(); - // this loop is used for inserting track-staves in the - // correct order. simply iterating through tracks's contents - // would sort after the pointer values, i.e. randomly - for (ciTrack track_it=tracklist->begin(); track_it!=tracklist->end(); track_it++) - if (tracks.find(*track_it)!=tracks.end()) + staff_t staff(this); + + if (all_in_one) + { + clefTypes clef=((MidiTrack*)pl->begin()->second->track())->getClef(); + + staff.parts.clear(); + for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) { - staff.parts.clear(); - for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) - if (part_it->second->track() == *track_it) - staff.parts.insert(part_it->second); - staff.cleanup_parts(); - - staff.type=GRAND_TOP; //FINDME_INITCLEF - staff.clef=VIOLIN; - staves.push_back(staff); + if (((MidiTrack*)part_it->second->track())->getClef() != clef) + clef=grandStaff; + + staff.parts.insert(part_it->second); + } + staff.cleanup_parts(); + staff.update_part_indices(); - staff.type=GRAND_BOTTOM; - staff.clef=BASS; - staves.push_back(staff); + switch (clef) + { + case trebleClef: + staff.type=NORMAL; + staff.clef=VIOLIN; + staves.push_back(staff); + break; + + case bassClef: + staff.type=NORMAL; + staff.clef=BASS; + staves.push_back(staff); + break; + + case grandStaff: + staff.type=GRAND_TOP; + staff.clef=VIOLIN; + staves.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staves.push_back(staff); + break; } + } + else + { + set<Track*> tracks; + for (ciPart it=pl->begin(); it!=pl->end(); it++) + tracks.insert(it->second->track()); + + TrackList* tracklist = song->tracks(); + // this loop is used for inserting track-staves in the + // correct order. simply iterating through tracks's contents + // would sort after the pointer values, i.e. randomly + for (ciTrack track_it=tracklist->begin(); track_it!=tracklist->end(); track_it++) + if (tracks.find(*track_it)!=tracks.end()) + { + staff.parts.clear(); + for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) + if (part_it->second->track() == *track_it) + staff.parts.insert(part_it->second); + staff.cleanup_parts(); + staff.update_part_indices(); + + switch (((MidiTrack*)(*track_it))->getClef()) + { + case trebleClef: + staff.type=NORMAL; + staff.clef=VIOLIN; + staves.push_back(staff); + break; + + case bassClef: + staff.type=NORMAL; + staff.clef=BASS; + staves.push_back(staff); + break; + + case grandStaff: + staff.type=GRAND_TOP; + staff.clef=VIOLIN; + staves.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staves.push_back(staff); + break; + } + } + } + + cleanup_staves(); + fully_recalculate(); + recalc_staff_pos(); } - - cleanup_staves(); - fully_recalculate(); - recalc_staff_pos(); } @@ -973,17 +1172,21 @@ ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget) : View(parent_wi init_pixmaps(); + srec=false; + for (int i=0;i<128;i++) held_notes[i]=false; + steprec=new StepRec(held_notes); + connect(song, SIGNAL(midiNote(int, int)), SLOT(midi_note(int,int))); + x_pos=0; x_left=0; y_pos=0; have_lasso=false; + inserting=false; dragging=false; drag_cursor_changed=false; mouse_erases_notes=false; mouse_inserts_notes=true; - undo_started=false; - undo_flags=0; selected_part=NULL; @@ -1155,6 +1358,8 @@ void ScoreCanvas::merge_staves(list<staff_t>::iterator dest, list<staff_t>::iter dest->parts.insert(src->parts.begin(), src->parts.end()); } + dest->update_part_indices(); + remove_staff(src); fully_recalculate(); @@ -1223,6 +1428,26 @@ void ScoreCanvas::fully_recalculate() void ScoreCanvas::song_changed(int flags) { + if (flags & (SC_PART_MODIFIED | SC_PART_REMOVED | SC_PART_INSERTED | SC_TRACK_REMOVED)) + { + update_parts(); + + if (flags & (SC_PART_REMOVED | SC_TRACK_REMOVED)) + { + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + it->cleanup_parts(); + + cleanup_staves(); + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + it->recalculate(); + + recalc_staff_pos(); + + redraw(); + } + } + if (flags & (SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | SC_SIG | SC_KEY) ) @@ -1238,26 +1463,6 @@ void ScoreCanvas::song_changed(int flags) emit canvas_width_changed(canvas_width()); } - if (flags & SC_PART_REMOVED) - { - bool something_changed=false; - - for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) - { - if (it->cleanup_parts()) - something_changed=true; - } - - cleanup_staves(); - - for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) - it->recalculate(); - - recalc_staff_pos(); - - redraw(); - } - if (flags & SC_SELECTION) { redraw(); @@ -3312,10 +3517,9 @@ int ScoreCanvas::y_to_pitch(int y, int t, clef_t clef) void ScoreCanvas::mousePressEvent (QMouseEvent* event) { - keystate=((QInputEvent*)event)->modifiers(); - + keystate=event->modifiers(); bool ctrl=keystate & Qt::ControlModifier; - + // den errechneten tick immer ABrunden! // denn der "bereich" eines schlags geht von schlag_begin bis nächsterschlag_begin-1 // noten werden aber genau in die mitte dieses bereiches gezeichnet @@ -3325,10 +3529,6 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) int y=event->y() + y_pos - staff_it->y_draw; int x=event->x()+x_pos-x_left; int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); - - if (event->button()==Qt::LeftButton) - if (!ctrl) - deselect_all(); if (staff_it!=staves.end()) { @@ -3389,7 +3589,7 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) int this_begin=tick; int this_end=this_begin+calc_len(set_it->len, set_it->dots); - selected_part=set_it->source_part; + set_selected_part(set_it->source_part); //that's the only note corresponding to the event? if (this_begin==total_begin && this_end==total_end) @@ -3419,9 +3619,10 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) + clicked_event_ptr=set_it->source_event; dragged_event=*set_it->source_event; + original_dragged_event=dragged_event.clone(); dragged_event_part=set_it->source_part; - dragged_event_original_pitch=dragged_event.pitch(); if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase? { @@ -3429,14 +3630,9 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) } else if (event->button()==Qt::LeftButton) //edit? { - set_it->source_event->setSelected(!set_it->source_event->selected()); - song_changed(SC_SELECTION); - setMouseTracking(true); dragging=true; drag_cursor_changed=false; - undo_started=false; - undo_flags=SC_EVENT_MODIFIED; } } else //we found nothing? @@ -3468,11 +3664,9 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) if (relative_tick<0) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl; song->startUndo(); - undo_started=true; - undo_flags=SC_EVENT_INSERTED | SC_EVENT_MODIFIED; - //stopping undo at the end of this function is unneccessary - //because we'll begin a drag right after it. finishing - //this drag will stop undo as well (in mouseReleaseEvent) + + if (!ctrl) + deselect_all(); Event newevent(Note); newevent.setPitch(y_to_pitch(y,tick, staff_it->clef)); @@ -3481,7 +3675,7 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) newevent.setTick(relative_tick); newevent.setLenTick((new_len>0)?new_len:last_len); newevent.setSelected(true); - + if (flo_quantize(newevent.lenTick(), quant_ticks()) <= 0) { newevent.setLenTick(quant_ticks()); @@ -3499,7 +3693,7 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) dragged_event_part=curr_part; dragged_event=newevent; - dragged_event_original_pitch=newevent.pitch(); + original_dragged_event=dragged_event.clone(); mouse_down_pos=event->pos(); mouse_operation=NO_OP; @@ -3509,9 +3703,12 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) setMouseTracking(true); dragging=true; + inserting=true; drag_cursor_changed=true; setCursor(Qt::SizeAllCursor); - //song->startUndo(); unneccessary because we have started it already above + + song->endUndo(SC_EVENT_INSERTED); + song->update(SC_SELECTION); } } else // !mouse_inserts_notes. open a lasso @@ -3525,16 +3722,16 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) } } } - - if (event->button()==Qt::LeftButton) - song->update(SC_SELECTION); } void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) { + keystate=event->modifiers(); + bool ctrl=keystate & Qt::ControlModifier; + if (dragging && event->button()==Qt::LeftButton) { - if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //also BEGIN can change the len by clipping + if (mouse_operation==LENGTH) { if (flo_quantize(dragged_event.lenTick(), quant_ticks()) <= 0) { @@ -3545,15 +3742,29 @@ void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) { last_len=flo_quantize(dragged_event.lenTick(), quant_ticks()); } + + if (undo_started) + song->endUndo(SC_EVENT_MODIFIED | SC_EVENT_REMOVED); } + - if (undo_started) - song->endUndo(undo_flags); + if (mouse_operation==NO_OP && !inserting) + { + if (event->button()==Qt::LeftButton) + if (!ctrl) + deselect_all(); + + clicked_event_ptr->setSelected(!clicked_event_ptr->selected()); + + song->update(SC_SELECTION); + } setMouseTracking(false); unsetCursor(); + inserting=false; dragging=false; drag_cursor_changed=false; + undo_started=false; x_scroll_speed=0; x_scroll_pos=0; } @@ -3583,6 +3794,9 @@ void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) if (have_lasso && event->button()==Qt::LeftButton) { + if (!ctrl) + deselect_all(); + set<Event*> already_processed; for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) @@ -3600,6 +3814,9 @@ void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) { + keystate=event->modifiers(); + bool ctrl=keystate & Qt::ControlModifier; + if (dragging) { int dx=event->x()-mouse_down_pos.x(); @@ -3630,6 +3847,22 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) mouse_operation=PITCH; setCursor(Qt::SizeVerCursor); } + + if (mouse_operation!=NO_OP) + { + if (!inserting && clicked_event_ptr->selected()==false) + { + if (!ctrl) + deselect_all(); + + clicked_event_ptr->setSelected(true); + + song->update(SC_SELECTION); + } + + old_pitch=-1; + old_dest_tick=MAXINT; + } } int new_pitch; @@ -3640,70 +3873,41 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) break; case PITCH: - if (debugMsg) cout << "changing pitch, delta="<<nearbyint((float)dy/PITCH_DELTA)<<endl; - new_pitch=dragged_event_original_pitch - nearbyint((float)dy/PITCH_DELTA); + if (heavyDebugMsg) cout << "trying to change pitch, delta="<<-nearbyint((float)dy/PITCH_DELTA)<<endl; + new_pitch=original_dragged_event.pitch() - nearbyint((float)dy/PITCH_DELTA); if (new_pitch < 0) new_pitch=0; if (new_pitch > 127) new_pitch=127; - if (dragged_event.pitch()!=new_pitch) + if (new_pitch != old_pitch) { - if (!undo_started) - { - song->startUndo(); - undo_started=true; - } - - Event tmp=dragged_event.clone(); - tmp.setPitch(new_pitch); - - audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); - dragged_event=tmp; - - fully_recalculate(); + if (debugMsg) cout << "changing pitch, delta="<<new_pitch-original_dragged_event.pitch()<<endl; + if (undo_started) song->undo(); + undo_started=transpose_notes(part_to_set(dragged_event_part),1, new_pitch-original_dragged_event.pitch()); + old_pitch=new_pitch; } break; case BEGIN: - if (dragged_event.tick()+dragged_event_part->tick() != unsigned(tick)) { - if (!undo_started) - { - song->startUndo(); - undo_started=true; - } - - Event tmp=dragged_event.clone(); signed relative_tick=tick-signed(dragged_event_part->tick()); + unsigned dest_tick; if (relative_tick >= 0) - tmp.setTick(relative_tick); + dest_tick=relative_tick; else { - tmp.setTick(0); + dest_tick=0; if (debugMsg) cout << "not moving note before begin of part; setting it directly to the begin" << endl; } - if (tmp.endTick() > dragged_event_part->lenTick()) + if (dest_tick != old_dest_tick) { - signed new_len=dragged_event_part->lenTick() - tmp.tick(); - if (new_len>=0) - { - tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); - if (debugMsg) cout << "moved note would exceed its part; clipping length to " << tmp.lenTick() << endl; - } - else - { - tmp.setLenTick(0); - if (debugMsg) cout << "moved note would exceed its part; clipping length to 0 (actually negative)" << endl; - } + if (undo_started) song->undo(); + undo_started=move_notes(part_to_set(dragged_event_part),1, (signed)dest_tick-original_dragged_event.tick()); + old_dest_tick=dest_tick; } - - audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); - dragged_event=tmp; - - fully_recalculate(); } break; @@ -4007,6 +4211,7 @@ void ScoreCanvas::menu_command(int cmd) case CMD_NOTELEN_16: new_len=TICKS_PER_WHOLE/16; break; case CMD_NOTELEN_32: new_len=TICKS_PER_WHOLE/32; break; case CMD_NOTELEN_LAST: new_len=-1; break; + default: cerr << "ERROR: ILLEGAL FUNCTION CALL: ScoreCanvas::menu_command called with unknown command ("<<cmd<<")"<<endl; } @@ -4123,14 +4328,6 @@ void ScoreCanvas::deselect_all() song->update(SC_SELECTION); } -void ScoreCanvas::keyPressEvent(QKeyEvent* event) -{ - if (event->key()==Qt::Key_Delete) - { - erase_notes(get_all_parts(), 1); // 1 means "all selected" - } -} - bool staff_t::cleanup_parts() { bool did_something=false; @@ -4162,6 +4359,7 @@ bool staff_t::cleanup_parts() it++; } + if (did_something) update_part_indices(); return did_something; } @@ -4191,6 +4389,48 @@ void staff_t::apply_lasso(QRect rect, set<Event*>& already_processed) } } +void ScoreCanvas::set_steprec(bool flag) +{ + srec=flag; +} + +void ScoreCanvas::midi_note(int pitch, int velo) +{ + if (velo) + held_notes[pitch]=true; + else + held_notes[pitch]=false; + + if ( srec && selected_part && !audio->isPlaying() && velo ) + steprec->record(selected_part,pitch,quant_ticks(),quant_ticks(),velo,globalKeyState&Qt::ControlModifier,globalKeyState&Qt::ShiftModifier); +} + + + +void ScoreCanvas::update_parts() +{ + if (selected_part!=NULL) //if it's null, let it be null + selected_part=partFromSerialNumber(selected_part_index); + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + it->update_parts(); +} + +void staff_t::update_parts() +{ + parts.clear(); + + for (set<int>::iterator it=part_indices.begin(); it!=part_indices.end(); it++) + parts.insert(partFromSerialNumber(*it)); +} + +void staff_t::update_part_indices() +{ + part_indices.clear(); + + for (set<Part*>::iterator it=parts.begin(); it!=parts.end(); it++) + part_indices.insert((*it)->sn()); +} //the following assertions are made: // pix_quarter.width() == pix_half.width() @@ -4221,29 +4461,29 @@ void staff_t::apply_lasso(QRect rect, set<Event*>& already_processed) * o rename stuff: UndoOp -> Operation, Undo -> OpList, * UndoType -> OpType, iUndoOp, riUndoOp -> iOperation, * undo.cpp/.h -> operations.cpp/.h + * o either remove these "hidden notes", or deal with them in the score editor + * o investigate with valgrind + * o controller view in score editor + * o deal with expanding parts + * o fix sigedit boxes + * o mid-click in pianoroll: change to "delete", or initiate drag and drop between windows? * * * o drum list: scroll while dragging * * IMPORTANT TODO - * o add a select-clef-toolbox for tracks - * o respect the track's clef (has to be implemented first in muse) * o do partial recalculating; recalculating can take pretty long * (0,5 sec) when displaying a whole song in scores * o transpose etc. must also transpose key-pressure events * o transpose: support in-key-transpose - * o legato: extend length to next note - * o delete: add velo and len threshold * o thin out: remove unneeded ctrl messages * * less important stuff - * o controller view in score editor * o quantize-templates (everything is forced into a specified * rhythm) * o part-templates (you specify some notes and a control-chord; * the notes are set according to the chord then) * o add functions like set velo, mod/set velo-off - * o deal with expanding parts * o use bars instead of flags over groups of 8ths / 16ths etc * o support different keys in different tracks at the same time * calc_pos_add_list and calc_item_pos will be affected by this @@ -4260,6 +4500,7 @@ void staff_t::apply_lasso(QRect rect, set<Event*>& already_processed) * o refuse to resize so that width gets smaller or equal than x_left * o draw a margin around notes which are in a bright color * o support drum tracks (x-note-heads etc.) + * o drum list: scroll while dragging: probably unneccessary with the "reorder list" function * * * stuff for the other muse developers @@ -4271,8 +4512,6 @@ void staff_t::apply_lasso(QRect rect, set<Event*>& already_processed) * ( (2+2+3)/4 or (3+2+2)/4 instead of 7/4 ) * o maybe do expanding parts inside the msgChangeEvent or * msgNewEvent functions (see my e-mail) - * - * o make quantize and other stuff faster (by assymetric communication) */ diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index 4004452f..e7302a46 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -22,6 +22,7 @@ #include <QActionGroup> #include <QGridLayout> #include <QByteArray> +#include <QToolButton> #include <values.h> #include "noteinfo.h" @@ -32,6 +33,9 @@ #include "part.h" #include "keyevent.h" #include "mtscale_flo.h" +#include "steprec.h" +#include "cleftypes.h" +#include "helper.h" #include <set> #include <map> @@ -58,7 +62,12 @@ enum {CMD_COLOR_BLACK, CMD_COLOR_VELO, CMD_COLOR_PART, CMD_NOTELEN_1, CMD_NOTELEN_2, CMD_NOTELEN_4, CMD_NOTELEN_8, CMD_NOTELEN_16, CMD_NOTELEN_32, CMD_NOTELEN_LAST, - CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN }; + CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN, CMD_TRANSPOSE, + CMD_ERASE, CMD_MOVE, CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO, + CMD_CUT, CMD_COPY, CMD_PASTE, CMD_DEL, + CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, + CMD_SELECT_ILOOP, CMD_SELECT_OLOOP}; + class ScoreCanvas; class EditToolBar; @@ -70,7 +79,6 @@ class EditToolBar; class ScoreEdit : public TopWin { Q_OBJECT - private: virtual void closeEvent(QCloseEvent*); virtual void resizeEvent(QResizeEvent*); @@ -106,6 +114,32 @@ class ScoreEdit : public TopWin QAction* color_black_action; QAction* color_velo_action; QAction* color_part_action; + + QMenu* color_menu; + + QAction* cut_action; + QAction* copy_action; + QAction* paste_action; + QAction* del_action; + + QAction* select_all_action; + QAction* select_none_action; + QAction* select_invert_action; + QAction* select_iloop_action; + QAction* select_oloop_action; + + QAction* func_quantize_action; + QAction* func_notelen_action; + QAction* func_velocity_action; + QAction* func_cresc_action; + QAction* func_transpose_action; + QAction* func_erase_action; + QAction* func_move_action; + QAction* func_fixed_len_action; + QAction* func_del_overlaps_action; + QAction* func_legato_action; + + QToolButton* srec; QScrollBar* xscroll; QScrollBar* yscroll; @@ -129,6 +163,9 @@ class ScoreEdit : public TopWin void menu_command(int); void velo_box_changed(); void velo_off_box_changed(); + void init_shortcuts(); + void selection_changed(); + void clipboard_changed(); signals: void deleted(unsigned long); @@ -481,6 +518,7 @@ enum staff_mode_t struct staff_t { set<Part*> parts; + set<int> part_indices; ScoreEventList eventlist; ScoreItemList itemlist; @@ -524,6 +562,7 @@ struct staff_t clef=clef_; parts=parts_; parent=parent_; + update_part_indices(); } bool cleanup_parts(); @@ -532,6 +571,9 @@ struct staff_t void read_status(Xml& xml); void write_status(int level, Xml& xml) const; + + void update_parts(); //re-populates the set<Part*> from the set<int> + void update_part_indices(); //re-populates the set<int> from the set<Part*> }; list<int> calc_accidentials(key_enum key, clef_t clef, key_enum next_key=KEY_C); @@ -612,6 +654,8 @@ class ScoreCanvas : public View list<staff_t> staves; + StepRec* steprec; + // the drawing area is split into a "preamble" containing clef, // key and time signature, and the "item's area" containing the // actual items (notes, bars, rests, etc.) @@ -633,6 +677,8 @@ class ScoreCanvas : public View float y_scroll_pos; Part* selected_part; + int selected_part_index; + int last_len; int new_len; //when zero or negative, last_len is used @@ -651,20 +697,26 @@ class ScoreCanvas : public View bool mouse_erases_notes; bool mouse_inserts_notes; + bool inserting; bool dragging; bool drag_cursor_changed; Part* dragged_event_part; Event dragged_event; - int dragged_event_original_pitch; + Event original_dragged_event; + Event* clicked_event_ptr; + + int old_pitch; + unsigned old_dest_tick; bool have_lasso; QPoint lasso_start; QRect lasso; bool undo_started; - int undo_flags; - + bool temp_undo; + bool srec; + bool held_notes[128]; enum {COLOR_MODE_BLACK, COLOR_MODE_PART, COLOR_MODE_VELO} coloring_mode; bool preamble_contains_keysig; @@ -692,6 +744,7 @@ class ScoreCanvas : public View void config_changed(); void deselect_all(); + void midi_note(int pitch, int velo); public slots: void x_scroll_event(int); @@ -711,7 +764,10 @@ class ScoreCanvas : public View void set_velo(int); void set_velo_off(int); - + + void set_steprec(bool); + + void update_parts(); //re-populates the set<Part*>s from the set<int>s signals: void xscroll_changed(int); void yscroll_changed(int); @@ -731,7 +787,6 @@ class ScoreCanvas : public View virtual void mouseMoveEvent (QMouseEvent* event); virtual void mouseReleaseEvent (QMouseEvent* event); virtual void resizeEvent(QResizeEvent*); - virtual void keyPressEvent(QKeyEvent* event); public: ScoreCanvas(ScoreEdit*, QWidget*); @@ -755,7 +810,7 @@ class ScoreCanvas : public View void set_last_len(int l) {last_len=l;} Part* get_selected_part() {return selected_part;} - void set_selected_part(Part* p) {selected_part=p;} + void set_selected_part(Part* p) {selected_part=p; if (selected_part) selected_part_index=selected_part->sn();} set<Part*> get_all_parts(); diff --git a/muse2/muse/mixer/amixer.h b/muse2/muse/mixer/amixer.h index f8e365c3..ca8a3f4c 100644 --- a/muse2/muse/mixer/amixer.h +++ b/muse2/muse/mixer/amixer.h @@ -62,6 +62,8 @@ class ScrollArea : public QScrollArea //--------------------------------------------------------- class AudioMixerApp : public QMainWindow { + Q_OBJECT + //QString name; MixerConfig* cfg; StripList stripList; @@ -84,7 +86,7 @@ class AudioMixerApp : public QMainWindow { QAction* showAuxTracksId; QAction* showSyntiTracksId; - Q_OBJECT + virtual void closeEvent(QCloseEvent*); void addStrip(Track*, int); diff --git a/muse2/muse/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index 265061ad..5644e6eb 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -48,33 +48,8 @@ #include "gconfig.h" #include "ttoolbutton.h" #include "menutitleitem.h" -#include "popupmenu.h" - -//--------------------------------------------------------- -// MenuTitleItem -//--------------------------------------------------------- - -MenuTitleItem::MenuTitleItem(const QString& ss, QWidget* parent) - : QWidgetAction(parent) - { - s = ss; - // Don't allow to click on it. - setEnabled(false); - // Just to be safe, set to -1 instead of default 0. - setData(-1); - } - -QWidget* MenuTitleItem::createWidget(QWidget *parent) -{ - QLabel* l = new QLabel(s, parent); - l->setAlignment(Qt::AlignCenter); - l->setAutoFillBackground(true); - //QPalette palette; - //palette.setColor(label->backgroundRole(), c); - //l->setPalette(palette); - l->setBackgroundRole(QPalette::Dark); - return l; -} +//#include "popupmenu.h" +#include "routepopup.h" /* //--------------------------------------------------------- @@ -145,14 +120,6 @@ void AudioStrip::songChanged(int val) if (val & SC_CHANNELS) updateChannels(); - // p3.3.47 - // Update the routing popup menu if anything relevant changed. - if (val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) - { - //updateRouteMenus(); - muse->updateRouteMenus(track, this); // p3.3.50 Use this handy shared routine. - } - // Catch when label font, or configuration min slider and meter values change. if (val & SC_CONFIG) { @@ -215,7 +182,6 @@ void AudioStrip::songChanged(int val) if (val & SC_TRACK_MODIFIED) { setLabelText(); - // Added by Tim. p3.3.9 setLabelFont(); } @@ -1007,898 +973,14 @@ AudioStrip::AudioStrip(QWidget* parent, AudioTrack* at) } //--------------------------------------------------------- -// addMenuItem -//--------------------------------------------------------- - -static int addMenuItem(AudioTrack* track, Track* route_track, PopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) -{ - // totalInChannels is only used by syntis. - int toch = ((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; - - RouteList* rl = isOutput ? track->outRoutes() : track->inRoutes(); - - QAction* act; - - QString s(route_track->name()); - - act = lb->addAction(s); - act->setData(id); - act->setCheckable(true); - - int ach = channel; - int bch = -1; - - Route r(route_track, isOutput ? ach : bch, channels); - - r.remoteChannel = isOutput ? bch : ach; - - mm.insert( pRouteMenuMap(id, r) ); - - for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == 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; - } - } - } - return ++id; -} - -//--------------------------------------------------------- -// addAuxPorts -//--------------------------------------------------------- - -static int addAuxPorts(AudioTrack* t, PopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) - { - AuxList* al = song->auxs(); - for (iAudioAux i = al->begin(); i != al->end(); ++i) { - Track* track = *i; - if (t == track) - continue; - id = addMenuItem(t, track, lb, id, mm, channel, channels, isOutput); - } - return id; - } - -//--------------------------------------------------------- -// addInPorts -//--------------------------------------------------------- - -static int addInPorts(AudioTrack* t, PopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) - { - InputList* al = song->inputs(); - for (iAudioInput i = al->begin(); i != al->end(); ++i) { - Track* track = *i; - if (t == track) - continue; - id = addMenuItem(t, track, lb, id, mm, channel, channels, isOutput); - } - return id; - } - -//--------------------------------------------------------- -// addOutPorts -//--------------------------------------------------------- - -static int addOutPorts(AudioTrack* t, PopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) - { - OutputList* al = song->outputs(); - for (iAudioOutput i = al->begin(); i != al->end(); ++i) { - Track* track = *i; - if (t == track) - continue; - id = addMenuItem(t, track, lb, id, mm, channel, channels, isOutput); - } - return id; - } - -//--------------------------------------------------------- -// addGroupPorts -//--------------------------------------------------------- - -static int addGroupPorts(AudioTrack* t, PopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) - { - GroupList* al = song->groups(); - for (iAudioGroup i = al->begin(); i != al->end(); ++i) { - Track* track = *i; - if (t == track) - continue; - id = addMenuItem(t, track, lb, id, mm, channel, channels, isOutput); - } - return id; - } - -//--------------------------------------------------------- -// addWavePorts -//--------------------------------------------------------- - -static int addWavePorts(AudioTrack* t, PopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) - { - WaveTrackList* al = song->waves(); - for (iWaveTrack i = al->begin(); i != al->end(); ++i) { - Track* track = *i; - if (t == track) - continue; - id = addMenuItem(t, track, lb, id, mm, channel, channels, isOutput); - } - return id; - } - -//--------------------------------------------------------- -// addSyntiPorts -//--------------------------------------------------------- - -static int addSyntiPorts(AudioTrack* t, PopupMenu* lb, int id, - RouteMenuMap& mm, int channel, int channels, bool isOutput) -{ - RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); - - QAction* act; - - SynthIList* al = song->syntis(); - for (iSynthI i = al->begin(); i != al->end(); ++i) - { - Track* track = *i; - if (t == track) - continue; - int toch = ((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() != Track::AUDIO_SOFTSYNTH) ? toch : ((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); - chpup->setTitle(track->name()); - for(int ch = 0; ch < chans; ++ch) - { - char buffer[128]; - if(tchans == 2) - snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").toLatin1().constData(), ch+1, ch+2); - else - snprintf(buffer, 128, "%s %d", chpup->tr("Channel").toLatin1().constData(), ch+1); - act = chpup->addAction(QString(buffer)); - act->setData(id); - act->setCheckable(true); - - int ach = (channel == -1) ? ch : channel; - int bch = (channel == -1) ? -1 : ch; - - Route rt(track, (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans); - //Route rt(track, ch); - //rt.remoteChannel = -1; - rt.remoteChannel = (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach; - - mm.insert( pRouteMenuMap(id, rt) ); - - for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == 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; - } - } - } - ++id; - } - - lb->addMenu(chpup); - } - } - return id; -} - -//--------------------------------------------------------- -// addMultiChannelOutPorts -//--------------------------------------------------------- - -static int addMultiChannelPorts(AudioTrack* t, PopupMenu* pup, int id, RouteMenuMap& mm, 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() != Track::AUDIO_SOFTSYNTH) ? toch : t->totalInChannels(); - - if(chans > 1) - pup->addAction(new MenuTitleItem("<Mono>", 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); - - if(isOutput) - { - switch(t->type()) - { - - case Track::AUDIO_INPUT: - //id = addWavePorts(t, chpup, id, mm, ch, 1, isOutput); // Rem p4.0.20 - case Track::WAVE: - case Track::AUDIO_GROUP: - case Track::AUDIO_SOFTSYNTH: - case Track::AUDIO_AUX: // p4.0.20 - id = addWavePorts(t, chpup, id, mm, ch, 1, isOutput); - id = addOutPorts(t, chpup, id, mm, ch, 1, isOutput); - id = addGroupPorts(t, chpup, id, mm, ch, 1, isOutput); - id = addSyntiPorts(t, chpup, id, mm, ch, 1, isOutput); - //break; // Rem p4.0.20 - //case Track::AUDIO_AUX: // - //id = addOutPorts(t, chpup, id, mm, ch, 1, isOutput); // - break; - default: - break; - } - } - else - { - switch(t->type()) - { - - case Track::AUDIO_OUTPUT: - id = addWavePorts(t, chpup, id, mm, ch, 1, isOutput); - id = addInPorts(t, chpup, id, mm, ch, 1, isOutput); - id = addGroupPorts(t, chpup, id, mm, ch, 1, isOutput); - id = addAuxPorts(t, chpup, id, mm, ch, 1, isOutput); - id = addSyntiPorts(t, chpup, id, mm, ch, 1, isOutput); - break; - case Track::WAVE: - //id = addInPorts(t, chpup, id, mm, ch, 1, isOutput); // Rem p4.0.20 - //break; - case Track::AUDIO_SOFTSYNTH: - case Track::AUDIO_GROUP: - id = addWavePorts(t, chpup, id, mm, ch, 1, isOutput); - id = addInPorts(t, chpup, id, mm, ch, 1, isOutput); - id = addGroupPorts(t, chpup, id, mm, ch, 1, isOutput); - id = addAuxPorts(t, chpup, id, mm, ch, 1, isOutput); // p4.0.20 - id = addSyntiPorts(t, chpup, id, mm, 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", pup->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("<Stereo>", 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); - - if(isOutput) - { - switch(t->type()) - { - case Track::AUDIO_INPUT: - //id = addWavePorts(t, chpup, id, mm, ch, 2, isOutput); // Rem p4.0.20 - case Track::WAVE: - case Track::AUDIO_GROUP: - case Track::AUDIO_SOFTSYNTH: - case Track::AUDIO_AUX: // p4.0.20 - id = addWavePorts(t, chpup, id, mm, ch, 2, isOutput); // p4.0.20 - id = addOutPorts(t, chpup, id, mm, ch, 2, isOutput); - id = addGroupPorts(t, chpup, id, mm, ch, 2, isOutput); - id = addSyntiPorts(t, chpup, id, mm, ch, 2, isOutput); - break; - //case Track::AUDIO_AUX: // Rem p4.0.20 - // id = addOutPorts(t, chpup, id, mm, ch, 2, isOutput); - // break; - default: - break; - } - } - else - { - switch(t->type()) - { - case Track::AUDIO_OUTPUT: - id = addWavePorts(t, chpup, id, mm, ch, 2, isOutput); - id = addInPorts(t, chpup, id, mm, ch, 2, isOutput); - id = addGroupPorts(t, chpup, id, mm, ch, 2, isOutput); - id = addAuxPorts(t, chpup, id, mm, ch, 2, isOutput); - id = addSyntiPorts(t, chpup, id, mm, ch, 2, isOutput); - break; - case Track::WAVE: - //id = addInPorts(t, chpup, id, mm, ch, 2, isOutput); // Rem p4.0.20 - //break; - case Track::AUDIO_SOFTSYNTH: - case Track::AUDIO_GROUP: - id = addWavePorts(t, chpup, id, mm, ch, 2, isOutput); - id = addInPorts(t, chpup, id, mm, ch, 2, isOutput); - id = addGroupPorts(t, chpup, id, mm, ch, 2, isOutput); - id = addAuxPorts(t, chpup, id, mm, ch, 2, isOutput); // p4.0.20 - id = addSyntiPorts(t, chpup, id, mm, 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", pup->tr("Channel").toLatin1().constData(), ch+1, ch+2); - chpup->setTitle(QString(buffer)); - pup->addMenu(chpup); - } - } - } - - return id; -} - -//--------------------------------------------------------- -// nonSyntiTrackAddSyntis -//--------------------------------------------------------- - -static int nonSyntiTrackAddSyntis(AudioTrack* t, PopupMenu* lb, int id, RouteMenuMap& mm, bool isOutput) -{ - RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); - - QAction* act; - SynthIList* al = song->syntis(); - for (iSynthI i = al->begin(); i != al->end(); ++i) - { - Track* track = *i; - if (t == track) - continue; - - int toch = ((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() != Track::AUDIO_SOFTSYNTH) ? toch : ((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); - chpup->setTitle(track->name()); - if(chans > 1) - chpup->addAction(new MenuTitleItem("<Mono>", chpup)); - - for(int ch = 0; ch < chans; ++ch) - { - char buffer[128]; - snprintf(buffer, 128, "%s %d", chpup->tr("Channel").toLatin1().constData(), ch+1); - act = chpup->addAction(QString(buffer)); - act->setData(id); - act->setCheckable(true); - - int ach = ch; - int bch = -1; - - Route rt(track, isOutput ? bch : ach, 1); - - rt.remoteChannel = isOutput ? ach : bch; - - mm.insert( pRouteMenuMap(id, rt) ); - - for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == 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; - } - } - } - ++id; - } - - chans -= 1; - if(chans > 0) - { - // Ignore odd numbered left-over channels. - //int schans = (chans & ~1) - 1; - - chpup->addSeparator(); - chpup->addAction(new MenuTitleItem("<Stereo>", chpup)); - - for(int ch = 0; ch < chans; ++ch) - { - char buffer[128]; - snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").toLatin1().constData(), ch+1, ch+2); - act = chpup->addAction(QString(buffer)); - act->setData(id); - act->setCheckable(true); - - int ach = ch; - int bch = -1; - - Route rt(track, isOutput ? bch : ach, 2); - - rt.remoteChannel = isOutput ? ach : bch; - - mm.insert( pRouteMenuMap(id, rt) ); - - for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == 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; - } - } - } - ++id; - } - } - - lb->addMenu(chpup); - } - } - return id; -} - -//--------------------------------------------------------- -// addMidiPorts -//--------------------------------------------------------- - -static int addMidiPorts(AudioTrack* t, PopupMenu* pup, int id, RouteMenuMap& mm, bool isOutput) -{ - QAction* act; - for(int i = 0; i < MIDI_PORTS; ++i) - { - MidiPort* mp = &midiPorts[i]; - 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->rwFlags() & (isOutput ? 1 : 2))) - // continue; - - // p4.0.17 Do not list synth devices! - if(md->isSynti()) - continue; - - RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); - - PopupMenu* subp = new PopupMenu(pup); - subp->setTitle(md->name()); - - 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(iRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i) - { - // We have a route to the midi port. Grab the channel mask. - chanmask = ir->channel; - break; - } - } - - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - { - act = subp->addAction(QString("Channel %1").arg(ch+1)); - act->setCheckable(true); - act->setData(id); - - int chbit = 1 << ch; - Route srcRoute(i, chbit); // In accordance with new channel mask, use the bit position. - - mm.insert( pRouteMenuMap(id, 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); - act->setData(id); - Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. - mm.insert( pRouteMenuMap(id, togRoute) ); - ++id; - - pup->addMenu(subp); - } - return id; -} - -//--------------------------------------------------------- -// routingPopupMenuActivated -//--------------------------------------------------------- - -void AudioStrip::routingPopupMenuActivated(QAction* act) -{ - if(!track || gRoutingPopupMenuMaster != this || track->isMidiTrack()) - return; - - PopupMenu* pup = muse->getRoutingPopupMenu(); - - if(pup->actions().isEmpty()) - return; - - AudioTrack* t = (AudioTrack*)track; - RouteList* rl = gIsOutRoutingPopupMenu ? t->outRoutes() : t->inRoutes(); - - int n = act->data().toInt(); - if (n == -1) - return; - - iRouteMenuMap imm = gRoutingMenuMap.find(n); - if(imm == gRoutingMenuMap.end()) - return; - - if(gIsOutRoutingPopupMenu) - { - Route srcRoute(t, imm->second.channel, imm->second.channels); - srcRoute.remoteChannel = imm->second.remoteChannel; - - Route &dstRoute = imm->second; - - // check if route src->dst exists: - iRoute irl = rl->begin(); - for (; irl != rl->end(); ++irl) { - if (*irl == dstRoute) - break; - } - if (irl != rl->end()) { - // disconnect if route exists - audio->msgRemoveRoute(srcRoute, dstRoute); - } - else { - // connect if route does not exist - audio->msgAddRoute(srcRoute, dstRoute); - } - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - } - else - { - Route &srcRoute = imm->second; - - // Support Midi Port to Audio Input routes. p4.0.14 Tim. - if(track->type() == Track::AUDIO_INPUT && srcRoute.type == Route::MIDI_PORT_ROUTE) - { - int chbit = srcRoute.channel; - Route dstRoute(t, chbit); - int mdidx = srcRoute.midiPort; - int chmask = 0; - iRoute iir = rl->begin(); - for (; iir != rl->end(); ++iir) - { - if(iir->type == 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("astrip: removing src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel); - audio->msgRemoveRoute(srcRoute, dstRoute); - } - else - { - //printf("astrip: adding src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel); - audio->msgAddRoute(srcRoute, dstRoute); - } - - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - return; - } - - Route dstRoute(t, imm->second.channel, imm->second.channels); - dstRoute.remoteChannel = imm->second.remoteChannel; - - iRoute irl = rl->begin(); - for (; irl != rl->end(); ++irl) { - if (*irl == srcRoute) - break; - } - if (irl != rl->end()) { - // disconnect - audio->msgRemoveRoute(srcRoute, dstRoute); - } - else { - // connect - audio->msgAddRoute(srcRoute, dstRoute); - } - audio->msgUpdateSoloStates(); - song->update(SC_ROUTE); - } -} - -//--------------------------------------------------------- // iRoutePressed //--------------------------------------------------------- void AudioStrip::iRoutePressed() { - //if(track->isMidiTrack() || (track->type() == Track::AUDIO_AUX) || (track->type() == Track::AUDIO_SOFTSYNTH)) - if(!track || track->isMidiTrack() || track->type() == Track::AUDIO_AUX) - { - gRoutingPopupMenuMaster = 0; - return; - } - - QPoint ppt = QCursor::pos(); - - PopupMenu* pup = muse->getRoutingPopupMenu(); - pup->disconnect(); - - AudioTrack* t = (AudioTrack*)track; - RouteList* irl = t->inRoutes(); - - QAction* act = 0; - int gid = 0; - //int id = 0; - - pup->clear(); - gRoutingMenuMap.clear(); - gid = 0; - - switch(track->type()) - { - case 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), pup); - pup->addAction(titel); - - if(!checkAudioDevice()) - { - gRoutingPopupMenuMaster = 0; - pup->clear(); - gRoutingMenuMap.clear(); - iR->setDown(false); - return; - } - std::list<QString> ol = audioDevice->outputPorts(); - for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) - { - //id = gid * 16 + i; // IDs removed p4.0.14 Tim. - act = pup->addAction(*ip); - //act->setData(id); - act->setData(gid); - act->setCheckable(true); - - Route dst(*ip, true, i, Route::JACK_ROUTE); - //gRoutingMenuMap.insert( pRouteMenuMap(id, dst) ); - gRoutingMenuMap.insert( pRouteMenuMap(gid, dst) ); - ++gid; - for(iRoute ir = irl->begin(); ir != irl->end(); ++ir) - { - if(*ir == dst) - { - act->setChecked(true); - break; - } - } - } - if(i+1 != channel) - pup->addSeparator(); - } - - // p4.0.14 - // - // Display using separate menus for midi ports and audio outputs: - // - pup->addSeparator(); - pup->addAction(new MenuTitleItem(tr("Soloing chain"), pup)); - PopupMenu* subp = new PopupMenu(pup); - subp->setTitle(tr("Audio sends")); - pup->addMenu(subp); - gid = addOutPorts(t, subp, gid, gRoutingMenuMap, -1, -1, false); - subp = new PopupMenu(pup); - subp->setTitle(tr("Midi port sends")); - pup->addMenu(subp); - addMidiPorts(t, subp, gid, gRoutingMenuMap, false); - // - // Display all in the same menu: - // - //pup->addAction(new MenuTitleItem(tr("Audio sends"), pup)); - //gid = addOutPorts(t, pup, gid, gRoutingMenuMap, -1, -1, false); - //pup->addSeparator(); - //pup->addAction(new MenuTitleItem(tr("Midi sends"), pup)); - //addMidiPorts(t, pup, gid, gRoutingMenuMap, false); - } - break; - //case Track::AUDIO_OUTPUT: - //case Track::WAVE: - //case Track::AUDIO_GROUP: - - case Track::AUDIO_OUTPUT: - gid = addWavePorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = addInPorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = addGroupPorts(t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = addAuxPorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = nonSyntiTrackAddSyntis(t, pup, gid, gRoutingMenuMap, false); - break; - case Track::WAVE: - gid = addWavePorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); // p4.0.20 - gid = addInPorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = addGroupPorts(t, pup, gid, gRoutingMenuMap, -1, -1, false); // - gid = addAuxPorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); // - gid = nonSyntiTrackAddSyntis(t, pup, gid, gRoutingMenuMap, false); // - break; - case Track::AUDIO_GROUP: - gid = addWavePorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = addInPorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = addGroupPorts(t, pup, gid, gRoutingMenuMap, -1, -1, false); - gid = addAuxPorts( t, pup, gid, gRoutingMenuMap, -1, -1, false); // p4.0.20 - gid = nonSyntiTrackAddSyntis(t, pup, gid, gRoutingMenuMap, false); - break; - - case Track::AUDIO_SOFTSYNTH: - gid = addMultiChannelPorts(t, pup, gid, gRoutingMenuMap, false); - break; - default: - gRoutingPopupMenuMaster = 0; - pup->clear(); - gRoutingMenuMap.clear(); - iR->setDown(false); - return; - } - - if(pup->actions().isEmpty()) - { - gRoutingPopupMenuMaster = 0; - gRoutingMenuMap.clear(); - iR->setDown(false); - return; - } - - gIsOutRoutingPopupMenu = false; - gRoutingPopupMenuMaster = this; - connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); - connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); - pup->popup(ppt); + RoutePopupMenu* pup = muse->getRoutingPopupMenu(); iR->setDown(false); + pup->exec(QCursor::pos(), track, false); } //--------------------------------------------------------- @@ -1907,136 +989,8 @@ void AudioStrip::iRoutePressed() void AudioStrip::oRoutePressed() { - if(!track || track->isMidiTrack()) - { - gRoutingPopupMenuMaster = 0; - return; - } - - QPoint ppt = QCursor::pos(); - - PopupMenu* pup = muse->getRoutingPopupMenu(); - pup->disconnect(); - - AudioTrack* t = (AudioTrack*)track; - RouteList* orl = t->outRoutes(); - - QAction* act = 0; - int gid = 0; - //int id = 0; - - pup->clear(); - gRoutingMenuMap.clear(); - gid = 0; - - switch(track->type()) - { - case 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), pup); - pup->addAction(titel); - - if(!checkAudioDevice()) - { - gRoutingPopupMenuMaster = 0; - pup->clear(); - gRoutingMenuMap.clear(); - oR->setDown(false); - return; - } - std::list<QString> ol = audioDevice->inputPorts(); - for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) - { - //id = gid * 16 + i; // IDs removed p4.0.14 Tim. - act = pup->addAction(*ip); - //act->setData(id); - act->setData(gid); - act->setCheckable(true); - - Route dst(*ip, true, i, Route::JACK_ROUTE); - //gRoutingMenuMap.insert( pRouteMenuMap(id, dst) ); - gRoutingMenuMap.insert( pRouteMenuMap(gid, dst) ); - ++gid; - for(iRoute ir = orl->begin(); ir != orl->end(); ++ir) - { - if(*ir == dst) - { - act->setChecked(true); - break; - } - } - } - if(i+1 != channel) - pup->addSeparator(); - } - - // p4.0.14 - // - // Display using separate menu for audio inputs: - // - pup->addSeparator(); - pup->addAction(new MenuTitleItem(tr("Soloing chain"), pup)); - PopupMenu* subp = new PopupMenu(pup); - subp->setTitle(tr("Audio returns")); - pup->addMenu(subp); - gid = addInPorts(t, subp, gid, gRoutingMenuMap, -1, -1, true); - // - // Display all in the same menu: - // - //pup->addSeparator(); - //MenuTitleItem* title = new MenuTitleItem(tr("Audio returns"), pup); - //pup->addAction(title); - //gid = addInPorts(t, pup, gid, gRoutingMenuMap, -1, -1, true); - } - break; - //case Track::AUDIO_INPUT: - //case Track::WAVE: - //case Track::AUDIO_GROUP: - - case Track::AUDIO_SOFTSYNTH: - gid = addMultiChannelPorts(t, pup, gid, gRoutingMenuMap, true); - break; - - case Track::AUDIO_INPUT: - //gid = addWavePorts( t, pup, gid, gRoutingMenuMap, -1, -1, true); // Rem p4.0.20 - case Track::WAVE: - case Track::AUDIO_GROUP: - case Track::AUDIO_AUX: - //case Track::AUDIO_SOFTSYNTH: - gid = addWavePorts( t, pup, gid, gRoutingMenuMap, -1, -1, true); // p4.0.20 - gid = addOutPorts( t, pup, gid, gRoutingMenuMap, -1, -1, true); - gid = addGroupPorts( t, pup, gid, gRoutingMenuMap, -1, -1, true); - gid = nonSyntiTrackAddSyntis(t, pup, gid, gRoutingMenuMap, true); - break; - //case Track::AUDIO_AUX: - // gid = addOutPorts( t, pup, gid, gRoutingMenuMap, -1, -1, true); - //break; - - default: - gRoutingPopupMenuMaster = 0; - pup->clear(); - gRoutingMenuMap.clear(); - oR->setDown(false); - return; - } - - if(pup->actions().isEmpty()) - { - gRoutingPopupMenuMaster = 0; - gRoutingMenuMap.clear(); - oR->setDown(false); - return; - } - - gIsOutRoutingPopupMenu = true; - gRoutingPopupMenuMaster = this; - connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); - connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); - pup->popup(ppt); + RoutePopupMenu* pup = muse->getRoutingPopupMenu(); oR->setDown(false); + pup->exec(QCursor::pos(), track, true); } diff --git a/muse2/muse/mixer/astrip.h b/muse2/muse/mixer/astrip.h index 10d75305..92867033 100644 --- a/muse2/muse/mixer/astrip.h +++ b/muse2/muse/mixer/astrip.h @@ -12,7 +12,7 @@ #include <vector> #include "strip.h" -#include "route.h" +//#include "route.h" class Slider; class Knob; @@ -20,7 +20,7 @@ class Knob; class QToolButton; //class QAction; //class QPopupMenu; -class PopupMenu; +//class PopupMenu; class QButton; class TransparentToolButton; class AudioTrack; @@ -61,7 +61,6 @@ class AudioStrip : public Strip { void updateVolume(); void updatePan(); void updateChannels(); - //void updateRouteMenus(); private slots: void stereoToggled(bool); @@ -69,7 +68,6 @@ class AudioStrip : public Strip { void offToggled(bool); void iRoutePressed(); void oRoutePressed(); - void routingPopupMenuActivated(QAction*); void auxChanged(double, int); void volumeChanged(double); void volumePressed(); diff --git a/muse2/muse/mixer/mstrip.cpp b/muse2/muse/mixer/mstrip.cpp index 427f9ed6..d773708a 100644 --- a/muse2/muse/mixer/mstrip.cpp +++ b/muse2/muse/mixer/mstrip.cpp @@ -43,7 +43,8 @@ #include "gconfig.h" #include "ttoolbutton.h" //#include "utils.h" -#include "popupmenu.h" +//#include "popupmenu.h" +#include "routepopup.h" enum { KNOB_PAN, KNOB_VAR_SEND, KNOB_REV_SEND, KNOB_CHO_SEND }; @@ -503,26 +504,17 @@ void MidiStrip::songChanged(int val) if (val & SC_TRACK_MODIFIED) { setLabelText(); - // Added by Tim. p3.3.9 setLabelFont(); } - // Added by Tim. p3.3.9 - // Catch when label font changes. + // Catch when label font changes. Tim. p3.3.9 if (val & SC_CONFIG) { // Set the strip label's font. //label->setFont(config.fonts[1]); setLabelFont(); } - - // p3.3.47 Update the routing popup menu if anything relevant changes. - //if(gRoutingPopupMenuMaster == this && track && (val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG))) - if(val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) // p3.3.50 - // Use this handy shared routine. - //muse->updateRouteMenus(track); - muse->updateRouteMenus(track, this); // p3.3.50 } //--------------------------------------------------------- @@ -1007,35 +999,14 @@ void MidiStrip::setReverbSend(double val) } //--------------------------------------------------------- -// routingPopupMenuActivated -//--------------------------------------------------------- - -void MidiStrip::routingPopupMenuActivated(QAction* act) -{ - if(gRoutingPopupMenuMaster != this || !track || !track->isMidiTrack()) - return; - - muse->routingPopupMenuActivated(track, act->data().toInt()); -} - -//--------------------------------------------------------- // iRoutePressed //--------------------------------------------------------- void MidiStrip::iRoutePressed() { - if(!track || !track->isMidiTrack()) - return; - - PopupMenu* pup = muse->prepareRoutingPopupMenu(track, false); - if(!pup) - return; - - gRoutingPopupMenuMaster = this; - connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); - connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); - pup->popup(QCursor::pos()); + RoutePopupMenu* pup = muse->getRoutingPopupMenu(); iR->setDown(false); + pup->exec(QCursor::pos(), track, false); } //--------------------------------------------------------- @@ -1044,18 +1015,9 @@ void MidiStrip::iRoutePressed() void MidiStrip::oRoutePressed() { - if(!track || !track->isMidiTrack()) - return; - - PopupMenu* pup = muse->prepareRoutingPopupMenu(track, true); - if(!pup) - return; - - gRoutingPopupMenuMaster = this; - connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); - connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); - pup->popup(QCursor::pos()); + RoutePopupMenu* pup = muse->getRoutingPopupMenu(); oR->setDown(false); + pup->exec(QCursor::pos(), track, true); } diff --git a/muse2/muse/mixer/mstrip.h b/muse2/muse/mixer/mstrip.h index 920cca99..39b55d21 100644 --- a/muse2/muse/mixer/mstrip.h +++ b/muse2/muse/mixer/mstrip.h @@ -32,9 +32,6 @@ class MidiStrip : public Strip { Slider* slider; DoubleLabel* sl; TransparentToolButton* off; - //QToolButton* route; - //QToolButton* iR; - //QToolButton* oR; struct KNOB { Knob* knob; @@ -55,11 +52,9 @@ class MidiStrip : public Strip { void updateOffState(); private slots: - //void routeClicked(); void offToggled(bool); void iRoutePressed(); void oRoutePressed(); - void routingPopupMenuActivated(QAction*); void setVolume(double); void setPan(double); void setChorusSend(double); diff --git a/muse2/muse/mixer/rack.h b/muse2/muse/mixer/rack.h index 33c846bd..2b1bbb66 100644 --- a/muse2/muse/mixer/rack.h +++ b/muse2/muse/mixer/rack.h @@ -24,8 +24,11 @@ class Xml; //--------------------------------------------------------- class EffectRack : public QListWidget { - AudioTrack* track; Q_OBJECT + + + AudioTrack* track; + virtual QSize minimumSizeHint() const; virtual QSize sizeHint() const; diff --git a/muse2/muse/mplugins/mrconfig.cpp b/muse2/muse/mplugins/mrconfig.cpp index f64384af..1a55c826 100644 --- a/muse2/muse/mplugins/mrconfig.cpp +++ b/muse2/muse/mplugins/mrconfig.cpp @@ -26,12 +26,14 @@ MRConfig::MRConfig(QWidget* parent, Qt::WFlags fl) sb2->setValue(rcRecordNote); sb3->setValue(rcGotoLeftMarkNote); sb4->setValue(rcPlayNote); + steprec_box->setValue(rcSteprecNote); connect(b1, SIGNAL(toggled(bool)), SLOT(setRcEnable(bool))); connect(sb1, SIGNAL(valueChanged(int)), SLOT(setRcStopNote(int))); connect(sb2, SIGNAL(valueChanged(int)), SLOT(setRcRecordNote(int))); connect(sb3, SIGNAL(valueChanged(int)), SLOT(setRcGotoLeftMarkNote(int))); connect(sb4, SIGNAL(valueChanged(int)), SLOT(setRcPlayNote(int))); + connect(steprec_box, SIGNAL(valueChanged(int)), SLOT(setRcSteprecNote(int))); } //--------------------------------------------------------- @@ -69,3 +71,8 @@ void MRConfig::setRcPlayNote(int val) rcPlayNote = val; } +void MRConfig::setRcSteprecNote(int val) + { + rcSteprecNote = val; + } + diff --git a/muse2/muse/mplugins/mrconfig.h b/muse2/muse/mplugins/mrconfig.h index e829c15c..5e4b38af 100644 --- a/muse2/muse/mplugins/mrconfig.h +++ b/muse2/muse/mplugins/mrconfig.h @@ -32,6 +32,7 @@ class MRConfig : public QWidget, public Ui::MRConfigBase { void setRcRecordNote(int); void setRcGotoLeftMarkNote(int); void setRcPlayNote(int); + void setRcSteprecNote(int); public: MRConfig(QWidget* parent=0, Qt::WFlags fl = 0); diff --git a/muse2/muse/mplugins/mrconfigbase.ui b/muse2/muse/mplugins/mrconfigbase.ui index ed04c334..18050c78 100644 --- a/muse2/muse/mplugins/mrconfigbase.ui +++ b/muse2/muse/mplugins/mrconfigbase.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>298</width> - <height>249</height> + <height>267</height> </rect> </property> <property name="windowTitle"> @@ -95,19 +95,29 @@ </widget> </item> <item row="0" column="1"> - <widget class="PitchEdit" name="sb1" native="true"/> + <widget class="Awl::PitchEdit" name="sb1"/> </item> <item row="1" column="1"> - <widget class="PitchEdit" name="sb2" native="true"/> + <widget class="Awl::PitchEdit" name="sb2"/> </item> <item row="2" column="1"> - <widget class="PitchEdit" name="sb3" native="true"/> + <widget class="Awl::PitchEdit" name="sb3"/> </item> <item row="3" column="1"> - <widget class="PitchEdit" name="sb4" native="true"/> + <widget class="Awl::PitchEdit" name="sb4"/> </item> - <item row="0" column="2"> - <spacer name="Spacer1"> + <item row="4" column="1"> + <widget class="Awl::PitchEdit" name="steprec_box"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Insert rest (step rec) </string> + </property> + </widget> + </item> + <item row="3" column="2"> + <spacer name="Spacer4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -122,8 +132,8 @@ </property> </spacer> </item> - <item row="1" column="2"> - <spacer name="Spacer2"> + <item row="2" column="2"> + <spacer name="Spacer3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -138,8 +148,8 @@ </property> </spacer> </item> - <item row="2" column="2"> - <spacer name="Spacer3"> + <item row="1" column="2"> + <spacer name="Spacer2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -154,8 +164,8 @@ </property> </spacer> </item> - <item row="3" column="2"> - <spacer name="Spacer4"> + <item row="0" column="2"> + <spacer name="Spacer1"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -178,9 +188,9 @@ <layoutdefault spacing="6" margin="11"/> <customwidgets> <customwidget> - <class>PitchEdit</class> - <extends>QWidget</extends> - <header>pitchedit.h</header> + <class>Awl::PitchEdit</class> + <extends>QSpinBox</extends> + <header>awl/pitchedit.h</header> </customwidget> </customwidgets> <includes> diff --git a/muse2/muse/node.cpp b/muse2/muse/node.cpp index 114b03d3..06dbbc8d 100644 --- a/muse2/muse/node.cpp +++ b/muse2/muse/node.cpp @@ -1264,7 +1264,7 @@ bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** b printf("AudioTrack::getData name:%s inRoutes:%d\n", name().toLatin1().constData(), rl->size()); #endif - iRoute ir = rl->begin(); + ciRoute ir = rl->begin(); if (ir == rl->end()) return false; diff --git a/muse2/muse/route.cpp b/muse2/muse/route.cpp index 6f42c1f2..9425f056 100644 --- a/muse2/muse/route.cpp +++ b/muse2/muse/route.cpp @@ -187,7 +187,7 @@ void addRoute(Route src, Route dst) src.channel = dst.channel; //src.channels = dst.channels = 1; RouteList* inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) + for (ciRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { if (*i == src) // route already there { @@ -226,7 +226,7 @@ void addRoute(Route src, Route dst) //dst.channel = -1; RouteList* routes = dst.device->inRoutes(); - for (iRoute i = routes->begin(); i != routes->end(); ++i) + for (ciRoute i = routes->begin(); i != routes->end(); ++i) { if (*i == src) // route already there { @@ -284,7 +284,7 @@ void addRoute(Route src, Route dst) dst.channel = src.channel; //dst.channels = src.channels = 1; - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) // route already there { @@ -312,7 +312,7 @@ void addRoute(Route src, Route dst) //dst.channels = src.channels = 1; RouteList* routes = src.device->outRoutes(); - for (iRoute i = routes->begin(); i != routes->end(); ++i) + for (ciRoute i = routes->begin(); i != routes->end(); ++i) { if (*i == dst) // route already there { @@ -616,7 +616,7 @@ void addRoute(Route src, Route dst) // dst.channels = src.track->channels(); //} - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) // route already there // TODO: @@ -1248,7 +1248,7 @@ bool checkRoute(const QString& s, const QString& d) } src.channel = dst.channel; RouteList* inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) + for (ciRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { if (*i == src) { // route already there return false; @@ -1263,7 +1263,7 @@ bool checkRoute(const QString& s, const QString& d) src.channel = -1; //dst.channel = -1; RouteList* routes = dst.device->inRoutes(); - for (iRoute i = routes->begin(); i != routes->end(); ++i) + for (ciRoute i = routes->begin(); i != routes->end(); ++i) { if (*i == src) { // route already there return false; @@ -1286,7 +1286,7 @@ bool checkRoute(const QString& s, const QString& d) } RouteList* outRoutes = src.track->outRoutes(); dst.channel = src.channel; - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) { // route already there return false; @@ -1301,7 +1301,7 @@ bool checkRoute(const QString& s, const QString& d) //dst.channel = src.channel; dst.channel = -1; //src.channel = -1; - for (iRoute i = routes->begin(); i != routes->end(); ++i) + for (ciRoute i = routes->begin(); i != routes->end(); ++i) { if (*i == dst) { // route already there return false; @@ -1314,7 +1314,7 @@ bool checkRoute(const QString& s, const QString& d) else if (src.type == Route::MIDI_PORT_ROUTE) // p3.3.49 { RouteList* outRoutes = midiPorts[src.midiPort].outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) { // route already there return false; @@ -1330,7 +1330,7 @@ bool checkRoute(const QString& s, const QString& d) // src.device->outRoutes() : src.track->outRoutes(); RouteList* outRoutes = (src.type == Route::MIDI_DEVICE_ROUTE) ? src.device->outRoutes() : src.track->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) { // route already there return false; diff --git a/muse2/muse/route.h b/muse2/muse/route.h index 2f29bcf8..9809352c 100644 --- a/muse2/muse/route.h +++ b/muse2/muse/route.h @@ -9,9 +9,10 @@ #ifndef __ROUTE_H__ #define __ROUTE_H__ +#include <QMetaType> + #include <vector> #include <map> - #include "globaldefs.h" class QString; @@ -71,6 +72,8 @@ struct Route { void dump() const; }; +// Allow Routes to be a QVariant +Q_DECLARE_METATYPE(Route) ; //--------------------------------------------------------- // RouteList @@ -93,11 +96,11 @@ extern bool checkRoute(const QString&, const QString&); // RouteMenuMap //--------------------------------------------------------- -typedef std::map<int, Route, std::less<int> >::iterator iRouteMenuMap; -typedef std::map<int, Route, std::less<int> >::const_iterator ciRouteMenuMap; -typedef std::map<int, Route, std::less<int> > RouteMenuMap; -typedef std::pair<int, Route> pRouteMenuMap; -typedef std::pair<iRouteMenuMap, bool > rpRouteMenuMap; +//typedef std::map<int, Route, std::less<int> >::iterator iRouteMenuMap; +//typedef std::map<int, Route, std::less<int> >::const_iterator ciRouteMenuMap; +//typedef std::map<int, Route, std::less<int> > RouteMenuMap; +//typedef std::pair<int, Route> pRouteMenuMap; +//typedef std::pair<iRouteMenuMap, bool > rpRouteMenuMap; #endif diff --git a/muse2/muse/seqmsg.cpp b/muse2/muse/seqmsg.cpp index 3fb91293..0aa74aaa 100644 --- a/muse2/muse/seqmsg.cpp +++ b/muse2/muse/seqmsg.cpp @@ -396,7 +396,7 @@ void Audio::msgSetChannels(AudioTrack* node, int n) else if ((i >= n) && ai->jackPort(i)) { RouteList* ir = node->inRoutes(); - for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; if ((r.type == Route::JACK_ROUTE) && (r.channel == i)) @@ -427,7 +427,7 @@ void Audio::msgSetChannels(AudioTrack* node, int n) else if (i >= n && jp) { RouteList* ir = node->outRoutes(); - for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; if ((r.type == Route::JACK_ROUTE) && (r.channel == i)) diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index f781b8f0..6d0541a3 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -2926,7 +2926,7 @@ void Song::connectJackRoutes(AudioTrack* track, bool disconnect) for(int ch = 0; ch < ao->channels(); ++ch) { RouteList* ir = ao->outRoutes(); - for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) @@ -2959,7 +2959,7 @@ void Song::connectJackRoutes(AudioTrack* track, bool disconnect) for(int ch = 0; ch < ai->channels(); ++ch) { RouteList* ir = ai->inRoutes(); - for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) @@ -3685,8 +3685,8 @@ void Song::executeScript(const char* scriptfile, PartList* parts, int quant, boo //const char* tmp = tmpnam(NULL); char tmp[16] = "muse-tmp-XXXXXX"; int fd = mkstemp(tmp); - printf("script input filename=%s\n",tmp); - //FILE *fp = fopen(tmp, "w"); + if (debugMsg) + printf("executeScript: script input filename=%s\n",tmp); FILE *fp = fdopen(fd , "w"); MidiPart *part = (MidiPart*)(i->second); int partStart = part->endTick()-part->lenTick(); @@ -3717,7 +3717,6 @@ void Song::executeScript(const char* scriptfile, PartList* parts, int quant, boo } fclose(fp); -// QString program(scriptfile); QStringList arguments; arguments << tmp; @@ -3725,64 +3724,58 @@ void Song::executeScript(const char* scriptfile, PartList* parts, int quant, boo myProcess->start(scriptfile, arguments); myProcess->waitForFinished(); QByteArray errStr = myProcess->readAllStandardError(); - if (errStr.size()) { - QMessageBox::warning(muse, tr("MusE - external script failed"), - "Script returned the following error\n"+ QString(errStr)); - endUndo(SC_EVENT_REMOVED); - return; - } else if (myProcess->exitCode()) { + if (myProcess->exitCode()) { QMessageBox::warning(muse, tr("MusE - external script failed"), - tr("MusE was unable to launch the script\n") + tr("MusE was unable to launch the script, error message:\n ")+ QString(errStr) ); endUndo(SC_EVENT_REMOVED); return; } - else { // d0 the fun55or5! - // TODO: Create a new part, update the entire editor from it, hehh.... + if (errStr.size()> 0) { + printf("script execution produced the following error:\n%s\n", QString(errStr).toLatin1().data()); + } + QFile file(tmp); + if ( file.open( QIODevice::ReadOnly ) ) { + QTextStream stream( &file ); + QString line; + while ( !stream.atEnd() ) { + line = stream.readLine(); // line of text excluding '\n' + if (line.startsWith("NOTE")) + { + QStringList sl = line.split(" "); + + Event e(Note); + int tick = sl[1].toInt(); + int pitch = sl[2].toInt(); + int len = sl[3].toInt(); + int velo = sl[4].toInt(); + e.setTick(tick); + e.setPitch(pitch); + e.setVelo(velo); + e.setLenTick(len); + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgAddEvent(e, part, false, false, false); + } + if (line.startsWith("CONTROLLER")) + { + QStringList sl = line.split(" "); + + Event e(Controller); + //int tick = sl[1].toInt(); + int a = sl[2].toInt(); + int b = sl[3].toInt(); + int c = sl[4].toInt(); + e.setA(a); + e.setB(b); + e.setB(c); + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgAddEvent(e, part, false, false, false); + } + } + file.close(); + } - QFile file(tmp); - if ( file.open( QIODevice::ReadOnly ) ) { - QTextStream stream( &file ); - QString line; - while ( !stream.atEnd() ) { - line = stream.readLine(); // line of text excluding '\n' - if (line.startsWith("NOTE")) - { - QStringList sl = line.split(" "); - - Event e(Note); - int tick = sl[1].toInt(); - int pitch = sl[2].toInt(); - int len = sl[3].toInt(); - int velo = sl[4].toInt(); - //printf ("tick=%d pitch=%d velo=%d len=%d\n", tick,pitch,velo,len); - e.setTick(tick); - e.setPitch(pitch); - e.setVelo(velo); - e.setLenTick(len); - // Indicate no undo, and do not do port controller values and clone parts. - audio->msgAddEvent(e, part, false, false, false); - } - if (line.startsWith("CONTROLLER")) - { - QStringList sl = line.split(" "); - - Event e(Controller); - int a = sl[2].toInt(); - int b = sl[3].toInt(); - int c = sl[4].toInt(); - //printf ("tick=%d a=%d b=%d c=%d\n", tick,a,b,c); - e.setA(a); - e.setB(b); - e.setB(c); - // Indicate no undo, and do not do port controller values and clone parts. - audio->msgAddEvent(e, part, false, false, false); - } - } - file.close(); - } - } remove(tmp); } diff --git a/muse2/muse/song.h b/muse2/muse/song.h index 17d70833..fd88b278 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -147,7 +147,7 @@ class Song : public QObject { Song(const char* name = 0); ~Song(); - void applyOperationGroup(Undo& group, bool doUndo=true); + bool applyOperationGroup(Undo& group, bool doUndo=true); void putEvent(int pv); void endMsgCmd(); @@ -164,7 +164,7 @@ class Song : public QObject { AudioOutput* bounceOutput; void updatePos(); - void read(Xml&); + void read(Xml&, bool isTemplate=false); void write(int, Xml&) const; void writeFont(int level, Xml& xml, const char* name, const QFont& font) const; diff --git a/muse2/muse/songfile.cpp b/muse2/muse/songfile.cpp index a8134b1d..72cacfaa 100644 --- a/muse2/muse/songfile.cpp +++ b/muse2/muse/songfile.cpp @@ -1213,7 +1213,7 @@ void Song::readMarker(Xml& xml) // read //--------------------------------------------------------- -void Song::read(Xml& xml) +void Song::read(Xml& xml, bool isTemplate) { cloneList.clear(); for (;;) { @@ -1260,7 +1260,7 @@ void Song::read(Xml& xml) _follow = FollowMode(xml.parseInt()); else if (tag == "sampleRate") { int sRate = xml.parseInt(); - if (audioDevice->deviceType() != AudioDevice::DUMMY_AUDIO && sRate != sampleRate) + if (!isTemplate && audioDevice->deviceType() != AudioDevice::DUMMY_AUDIO && sRate != sampleRate) QMessageBox::warning(muse,"Wrong sample rate", "The sample rate in this project and the current system setting differs, the project may not work as intended!"); } else if (tag == "tempolist") { @@ -1373,7 +1373,7 @@ void Song::read(Xml& xml) // read song //--------------------------------------------------------- -void MusE::read(Xml& xml, bool skipConfig) +void MusE::read(Xml& xml, bool skipConfig, bool isTemplate) { bool skipmode = true; for (;;) { @@ -1398,7 +1398,7 @@ void MusE::read(Xml& xml, bool skipConfig) readConfiguration(xml, false); else if (tag == "song") { - song->read(xml); + song->read(xml, isTemplate); audio->msgUpdateSoloStates(); } else if (tag == "midiport") diff --git a/muse2/muse/steprec.cpp b/muse2/muse/steprec.cpp new file mode 100644 index 00000000..29cb9540 --- /dev/null +++ b/muse2/muse/steprec.cpp @@ -0,0 +1,159 @@ +//========================================================= +// MusE +// Linux Music Editor +// steprec.cpp +// (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) +//========================================================= + +#include "steprec.h" +#include "part.h" +#include "event.h" +#include "globals.h" + +#include "song.h" +#include "audio.h" + +#include <set> + +#define CHORD_TIMEOUT 75 + +StepRec::StepRec(bool* note_held_down_array) +{ + note_held_down=note_held_down_array; + + chord_timer=new QTimer(this); + chord_timer->setSingleShot(true); + chord_timer->setInterval(CHORD_TIMEOUT); + chord_timer->stop(); + connect(chord_timer, SIGNAL(timeout()), SLOT(timeout())); +} + +void StepRec::timeout() +{ + if (chord_timer_set_to_tick != song->cpos()) + { + Pos p(chord_timer_set_to_tick, true); + song->setPos(0, p, true, false, true); + } +} + +void StepRec::record(Part* part, int pitch, int len, int step, int velo, bool ctrl, bool shift) +{ + unsigned tick = song->cpos(); + + if (pitch!=rcSteprecNote) { + chord_timer->stop(); + + + // + // extend len of last note? + // + EventList* events = part->events(); + if (ctrl) { + for (iEvent i = events->begin(); i != events->end(); ++i) { + Event ev = i->second; + if (!ev.isNote()) + continue; + if (ev.pitch() == pitch && ((ev.tick() + ev.lenTick()) == tick)) { + Event e = ev.clone(); + e.setLenTick(ev.lenTick() + len); + // Indicate do undo, and do not do port controller values and clone parts. + audio->msgChangeEvent(ev, e, part, true, false, false); + + if (!shift) { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + return; + } + } + } + + // + // if we already entered the note, delete it + // + EventRange range = events->equal_range(tick); + for (iEvent i = range.first; i != range.second; ++i) { + Event ev = i->second; + if (ev.isNote() && ev.pitch() == pitch) { + // Indicate do undo, and do not do port controller values and clone parts. + //audio->msgDeleteEvent(ev, part); + audio->msgDeleteEvent(ev, part, true, false, false); + + if (!shift) { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + + return; + } + } + + Event e(Note); + e.setTick(tick - part->tick()); + e.setPitch(pitch); + e.setVelo(velo); + e.setLenTick(len); + // Indicate do undo, and do not do port controller values and clone parts. + //audio->msgAddEvent(e, part); + audio->msgAddEvent(e, part, true, false, false); + + if (! (globalKeyState & Qt::ShiftModifier)) { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + } + else { // equals if (pitch==rcSteprecNote) + bool held_notes=false; + if (note_held_down!=NULL) + { + for (int i=0;i<128;i++) + if (note_held_down[i]) { held_notes=true; break; } + } + else + held_notes=false; + + + if (held_notes) + { + chord_timer->stop(); + + // extend len of last note(s) + using std::set; + + set<Event*> extend_set; + EventList* events = part->events(); + for (iEvent i = events->begin(); i != events->end(); ++i) { + Event& ev = i->second; + if (!ev.isNote()) + continue; + + if (note_held_down[ev.pitch()] && ((ev.tick() + ev.lenTick()) == tick)) + extend_set.insert(&ev); + } + for (set<Event*>::iterator it=extend_set.begin(); it!=extend_set.end(); it++) + { + Event& ev=**it; + Event e = ev.clone(); + e.setLenTick(ev.lenTick() + len); + // Indicate do undo, and do not do port controller values and clone parts. + audio->msgChangeEvent(ev, e, part, true, false, false); + } + + if (!shift) { + chord_timer_set_to_tick = tick + step; + chord_timer->start(); + } + return; + + } + else // equals if (!held_notes) + { + chord_timer->stop(); + + //simply proceed, inserting a rest + Pos p(song->cpos() + step, true); + song->setPos(0, p, true, false, true); + } + } +} diff --git a/muse2/muse/steprec.h b/muse2/muse/steprec.h new file mode 100644 index 00000000..02eab46c --- /dev/null +++ b/muse2/muse/steprec.h @@ -0,0 +1,35 @@ +//========================================================= +// MusE +// Linux Music Editor +// steprec.h +// (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) +//========================================================= + +#ifndef __STEPREC_H__ +#define __STEPREC_H__ + +#include <QObject> +#include <QTimer> + +#include "part.h" + + +class StepRec : public QObject +{ + Q_OBJECT + + public: + StepRec(bool* note_held_down_array); + + void record(Part* part, int pitch, int len, int step, int velo=80, bool ctrl=false, bool shift=false); + + private slots: + void timeout(); + + private: + QTimer* chord_timer; + unsigned int chord_timer_set_to_tick; + bool* note_held_down; +}; + +#endif diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index 521c7d63..4f43a02a 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -17,7 +17,7 @@ #include <dlfcn.h> #include <QDir> -#include <QMenu> +//#include <QMenu> #include "app.h" #include "synth.h" @@ -35,6 +35,7 @@ #include "midiseq.h" #include "midictrl.h" //#include "stringparam.h" +#include "popupmenu.h" std::vector<Synth*> synthis; // array of available synthis @@ -864,7 +865,7 @@ const char* MessSynthIF::getPatchName(int channel, int prog, MType type, bool dr // populatePatchPopup //--------------------------------------------------------- -void MessSynthIF::populatePatchPopup(QMenu* menu, int ch, MType, bool) +void MessSynthIF::populatePatchPopup(PopupMenu* menu, int ch, MType, bool) { menu->clear(); const MidiPatch* mp = _mess->getPatchInfo(ch, 0); diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h index b11ea2d9..88fa70b8 100644 --- a/muse2/muse/synth.h +++ b/muse2/muse/synth.h @@ -22,7 +22,8 @@ #include <QFileInfo> -class QMenu; +//class QMenu; +class PopupMenu; //class MidiEvent; class MidiPlayEvent; @@ -147,7 +148,8 @@ class SynthIF { virtual void deactivate3() = 0; virtual const char* getPatchName(int, int, int, bool) const = 0; virtual const char* getPatchName(int, int, MType, bool) = 0; - virtual void populatePatchPopup(QMenu*, int, MType, bool) = 0; + //virtual void populatePatchPopup(QMenu*, int, MType, bool) = 0; + virtual void populatePatchPopup(PopupMenu*, int, MType, bool) = 0; virtual void write(int level, Xml& xml) const = 0; virtual float getParameter(unsigned long idx) const = 0; virtual void setParameter(unsigned long idx, float value) = 0; @@ -231,7 +233,8 @@ class SynthI : public AudioTrack, public MidiDevice, return _sif->getPatchName(ch, prog, t, dr); } - virtual void populatePatchPopup(QMenu* m, int i, MType t, bool d) { + //virtual void populatePatchPopup(QMenu* m, int i, MType t, bool d) { + virtual void populatePatchPopup(PopupMenu* m, int i, MType t, bool d) { _sif->populatePatchPopup(m, i, t, d); } @@ -313,7 +316,8 @@ class MessSynthIF : public SynthIF { virtual void deactivate3(); virtual const char* getPatchName(int, int, int, bool) const { return ""; } virtual const char* getPatchName(int, int, MType, bool); - virtual void populatePatchPopup(QMenu*, int, MType, bool); + //virtual void populatePatchPopup(QMenu*, int, MType, bool); + virtual void populatePatchPopup(PopupMenu*, int, MType, bool); virtual void write(int level, Xml& xml) const; virtual float getParameter(unsigned long) const { return 0.0; } virtual void setParameter(unsigned long, float) {} diff --git a/muse2/muse/ticksynth.cpp b/muse2/muse/ticksynth.cpp index c5d3a1e7..7456b856 100644 --- a/muse2/muse/ticksynth.cpp +++ b/muse2/muse/ticksynth.cpp @@ -9,7 +9,8 @@ #include "ticksynth.h" #include "default_click.h" -#include <QMenu> +//#include <QMenu> +#include "popupmenu.h" // Added by Tim. p3.3.18 //#define METRONOME_DEBUG @@ -90,7 +91,8 @@ class MetronomeSynthIF : public SynthIF virtual void deactivate3() {} virtual const char* getPatchName(int, int, int, bool) const { return ""; } virtual const char* getPatchName(int, int, MType, bool) { return ""; } - virtual void populatePatchPopup(QMenu*, int, MType, bool) {}; + //virtual void populatePatchPopup(QMenu*, int, MType, bool) {}; + virtual void populatePatchPopup(PopupMenu*, int, MType, bool) {}; virtual void write(int, Xml&) const {} virtual float getParameter(unsigned long) const { return 0.0; } virtual void setParameter(unsigned long, float) {} diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp index 2a93968d..5f358375 100644 --- a/muse2/muse/track.cpp +++ b/muse2/muse/track.cpp @@ -369,6 +369,7 @@ MidiTrack::MidiTrack() init(); _events = new EventList; _mpevents = new MPEventList; + clefType=trebleClef; } //MidiTrack::MidiTrack(const MidiTrack& mt) @@ -388,6 +389,7 @@ MidiTrack::MidiTrack(const MidiTrack& mt, bool cloneParts) len = mt.len; compression = mt.compression; _recEcho = mt.recEcho(); + clefType=trebleClef; } MidiTrack::~MidiTrack() @@ -894,6 +896,7 @@ void MidiTrack::write(int level, Xml& xml) const xml.intTag(level, "len", len); xml.intTag(level, "compression", compression); xml.intTag(level, "automation", int(automationType())); + xml.intTag(level, "clef", int(clefType)); const PartList* pl = cparts(); for (ciPart p = pl->begin(); p != pl->end(); ++p) @@ -955,6 +958,8 @@ void MidiTrack::read(Xml& xml) _recEcho = xml.parseInt(); else if (tag == "automation") setAutomationType(AutomationType(xml.parseInt())); + else if (tag == "clef") + clefType = (clefTypes)xml.parseInt(); else if (Track::readProperties(xml, tag)) { // version 1.0 compatibility: if (tag == "track" && xml.majorVersion() == 1 && xml.minorVersion() == 0) diff --git a/muse2/muse/track.h b/muse2/muse/track.h index aec765da..50870166 100644 --- a/muse2/muse/track.h +++ b/muse2/muse/track.h @@ -20,6 +20,7 @@ #include "route.h" #include "ctrl.h" #include "globaldefs.h" +#include "cleftypes.h" class Pipeline; class Xml; @@ -208,6 +209,7 @@ class MidiTrack : public Track { EventList* _events; // tmp Events during midi import MPEventList* _mpevents; // tmp Events druring recording static bool _isVisible; + clefTypes clefType; public: MidiTrack(); @@ -273,6 +275,9 @@ class MidiTrack : public Track { virtual bool canRecord() const { return true; } static void setVisible(bool t) { _isVisible = t; } static bool visible() { return _isVisible; } + + void setClef(clefTypes i) { clefType = i; } + clefTypes getClef() { return clefType; } }; //--------------------------------------------------------- diff --git a/muse2/muse/transport.h b/muse2/muse/transport.h index b2d3facf..ce8dcf1b 100644 --- a/muse2/muse/transport.h +++ b/muse2/muse/transport.h @@ -34,10 +34,12 @@ class Pos; //--------------------------------------------------------- class TempoSig : public QWidget { + Q_OBJECT + DoubleLabel* l1; SigLabel* l2; QLabel* l3; - Q_OBJECT + private slots: void configChanged(); @@ -76,6 +78,8 @@ class TimeLLabel; class Transport : public QWidget { + Q_OBJECT + PosEdit* tl1; // left mark PosEdit* tl2; // right mark PosEdit* time1; // tick time @@ -99,9 +103,7 @@ class Transport : public QWidget Handle *lefthandle, *righthandle; - Q_OBJECT - - private slots: + private slots: void cposChanged(const Pos&); void cposChanged(int); void lposChanged(const Pos&); diff --git a/muse2/muse/undo.cpp b/muse2/muse/undo.cpp index 2a11c3dc..c57bb5c1 100644 --- a/muse2/muse/undo.cpp +++ b/muse2/muse/undo.cpp @@ -186,6 +186,9 @@ void UndoList::clearDelete() void Song::startUndo() { + redoList->clear(); // added by flo93: redo must be invalidated when + redoAction->setEnabled(false); // a new undo is started + undoList->push_back(Undo()); updateFlags = 0; undoMode = true; @@ -203,7 +206,7 @@ void Song::endUndo(int flags) } -void Song::applyOperationGroup(Undo& group, bool doUndo) +bool Song::applyOperationGroup(Undo& group, bool doUndo) { if (!group.empty()) { @@ -216,7 +219,16 @@ void Song::applyOperationGroup(Undo& group, bool doUndo) undoList->pop_back(); undoAction->setEnabled(!undoList->empty()); } + else + { + redoList->clear(); // added by flo93: redo must be invalidated when + redoAction->setEnabled(false); // a new undo is started + } + + return doUndo; } + else + return false; } diff --git a/muse2/muse/value.h b/muse2/muse/value.h index 22aa9b5a..e5c74b20 100644 --- a/muse2/muse/value.h +++ b/muse2/muse/value.h @@ -18,9 +18,9 @@ class Xml; //--------------------------------------------------------- class IValue : public QObject { - int val; - Q_OBJECT + + int val; signals: void valueChanged(int); @@ -39,9 +39,11 @@ class IValue : public QObject { //--------------------------------------------------------- class BValue : public QObject { + Q_OBJECT + bool val; - Q_OBJECT + signals: void valueChanged(bool); diff --git a/muse2/muse/vst.h b/muse2/muse/vst.h index d41502e5..bb675c22 100644 --- a/muse2/muse/vst.h +++ b/muse2/muse/vst.h @@ -10,7 +10,8 @@ #include "synth.h" -class QMenu; +//class QMenu; +class PopupMenu; struct _FSTHandle; struct _FST; @@ -76,7 +77,8 @@ class VstSynthIF : public SynthIF virtual void deactivate3(); virtual const char* getPatchName(int, int, int, bool) const { return ""; } virtual const char* getPatchName(int, int, MType, bool) { return ""; } - virtual void populatePatchPopup(QMenu*, int, MType, bool) {}; + //virtual void populatePatchPopup(QMenu*, int, MType, bool) {}; + virtual void populatePatchPopup(PopupMenu*, int, MType, bool) {}; virtual void write(int level, Xml& xml) const; virtual float getParameter(unsigned long idx) const; virtual void setParameter(unsigned long idx, float value); diff --git a/muse2/muse/waveedit/waveedit.h b/muse2/muse/waveedit/waveedit.h index e966a635..1ff8a65b 100644 --- a/muse2/muse/waveedit/waveedit.h +++ b/muse2/muse/waveedit/waveedit.h @@ -33,6 +33,8 @@ class QAction; //--------------------------------------------------------- class WaveEdit : public MidiEditor { + Q_OBJECT + WaveView* view; QSlider* ymag; QToolBar* tools; @@ -49,7 +51,7 @@ class WaveEdit : public MidiEditor { static int _widthInit, _heightInit; static QByteArray _toolbarInit; - Q_OBJECT + virtual void closeEvent(QCloseEvent*); virtual void keyPressEvent(QKeyEvent*); virtual void resizeEvent(QResizeEvent* ev); diff --git a/muse2/muse/waveedit/waveview.h b/muse2/muse/waveedit/waveview.h index c7992952..1a646af9 100644 --- a/muse2/muse/waveedit/waveview.h +++ b/muse2/muse/waveedit/waveview.h @@ -34,6 +34,8 @@ typedef std::list<WaveEventSelection>::iterator iWaveSelection; //--------------------------------------------------------- class WaveView : public View { + Q_OBJECT + MidiEditor* editor; unsigned pos[3]; int yScale; @@ -50,7 +52,6 @@ class WaveView : public View { unsigned selectionStart, selectionStop, dragstartx; - Q_OBJECT virtual void pdraw(QPainter&, const QRect&); virtual void draw(QPainter&, const QRect&); virtual void viewMousePressEvent(QMouseEvent*); diff --git a/muse2/muse/waveevent.cpp b/muse2/muse/waveevent.cpp index 867ce5c8..9f81b7e3 100644 --- a/muse2/muse/waveevent.cpp +++ b/muse2/muse/waveevent.cpp @@ -151,7 +151,7 @@ void WaveEventBase::write(int level, Xml& xml, const Pos& offset, bool forcePath //off_t WaveEventBase::readAudio(SRC_STATE* src_state, off_t sfCurFrame, unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) //off_t WaveEventBase::readAudio(AudioConverter* audConv, off_t sfCurFrame, unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) // p3.3.33 -void WaveEventBase::readAudio(WavePart* part, unsigned offset, float** buffer, int channel, int n, bool doSeek, bool overwrite) +void WaveEventBase::readAudio(WavePart* /*part*/, unsigned offset, float** buffer, int channel, int n, bool /*doSeek*/, bool overwrite) { // Added by Tim. p3.3.17 #ifdef WAVEEVENT_DEBUG_PRC diff --git a/muse2/muse/wavetrack.cpp b/muse2/muse/wavetrack.cpp index fdebc8b8..ad02084c 100644 --- a/muse2/muse/wavetrack.cpp +++ b/muse2/muse/wavetrack.cpp @@ -209,7 +209,7 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float* if ((song->bounceTrack != this) && !noInRoute()) { RouteList* irl = inRoutes(); - iRoute i = irl->begin(); + ciRoute i = irl->begin(); if(i->track->isMidiTrack()) { if(debugMsg) diff --git a/muse2/muse/widgets/CMakeLists.txt b/muse2/muse/widgets/CMakeLists.txt index 7589ddf0..a4da398f 100644 --- a/muse2/muse/widgets/CMakeLists.txt +++ b/muse2/muse/widgets/CMakeLists.txt @@ -47,6 +47,7 @@ QT4_WRAP_CPP (widget_mocs intlabel.h knob.h lcombo.h + menutitleitem.h meter.h metronome.h midisyncimpl.h @@ -63,6 +64,7 @@ QT4_WRAP_CPP (widget_mocs # posedit.h poslabel.h projectcreateimpl.h + routepopup.h scrollscale.h shortcutcapturedialog.h shortcutconfig.h @@ -142,6 +144,7 @@ file (GLOB widgets_source_files intlabel.cpp knob.cpp lcombo.cpp + menutitleitem.cpp meter.cpp metronome.cpp midisyncimpl.cpp @@ -159,6 +162,7 @@ file (GLOB widgets_source_files # posedit.cpp poslabel.cpp projectcreateimpl.cpp + routepopup.cpp scldiv.cpp scldraw.cpp sclif.cpp diff --git a/muse2/muse/widgets/bigtime.h b/muse2/muse/widgets/bigtime.h index bb32cedc..eff0ef92 100644 --- a/muse2/muse/widgets/bigtime.h +++ b/muse2/muse/widgets/bigtime.h @@ -13,9 +13,11 @@ class MusE; //--------------------------------------------------------- class BigTime : public QWidget { + Q_OBJECT + bool tickmode; MusE* seq; - Q_OBJECT + bool setString(unsigned); diff --git a/muse2/muse/widgets/comment.h b/muse2/muse/widgets/comment.h index 688d7b2f..0dbd953b 100644 --- a/muse2/muse/widgets/comment.h +++ b/muse2/muse/widgets/comment.h @@ -36,8 +36,10 @@ class Comment : public QWidget, public Ui::CommentBase { //--------------------------------------------------------- class TrackComment : public Comment { - Track* track; Q_OBJECT + + Track* track; + private: virtual void setText(const QString& s); diff --git a/muse2/muse/widgets/function_dialogs/CMakeLists.txt b/muse2/muse/widgets/function_dialogs/CMakeLists.txt index 7ddc6bee..db1f3229 100644 --- a/muse2/muse/widgets/function_dialogs/CMakeLists.txt +++ b/muse2/muse/widgets/function_dialogs/CMakeLists.txt @@ -30,6 +30,7 @@ QT4_WRAP_CPP (widgets_functiondialogs_mocs remove.h setlen.h transpose.h + legato.h velocity.h ) @@ -45,6 +46,7 @@ file (GLOB widgets_functiondialogs_ui_files removebase.ui setlenbase.ui transposebase.ui + legatobase.ui velocitybase.ui ) @@ -62,6 +64,7 @@ file (GLOB widgets_functiondialogs_source_files remove.cpp setlen.cpp transpose.cpp + legato.cpp velocity.cpp ) diff --git a/muse2/muse/widgets/function_dialogs/crescendo.h b/muse2/muse/widgets/function_dialogs/crescendo.h index eb00e94f..73a7e088 100644 --- a/muse2/muse/widgets/function_dialogs/crescendo.h +++ b/muse2/muse/widgets/function_dialogs/crescendo.h @@ -15,8 +15,9 @@ class Xml; class Crescendo : public QDialog, public Ui::CrescendoBase { + Q_OBJECT private: - Q_OBJECT + QButtonGroup* range_group; protected slots: diff --git a/muse2/muse/widgets/function_dialogs/deloverlaps.h b/muse2/muse/widgets/function_dialogs/deloverlaps.h index 813192a6..d151d5a5 100644 --- a/muse2/muse/widgets/function_dialogs/deloverlaps.h +++ b/muse2/muse/widgets/function_dialogs/deloverlaps.h @@ -15,8 +15,9 @@ class Xml; class DelOverlaps : public QDialog, public Ui::DelOverlapsBase { + Q_OBJECT private: - Q_OBJECT + QButtonGroup* range_group; protected slots: diff --git a/muse2/muse/widgets/function_dialogs/gatetime.h b/muse2/muse/widgets/function_dialogs/gatetime.h index d2555872..baa5a97b 100644 --- a/muse2/muse/widgets/function_dialogs/gatetime.h +++ b/muse2/muse/widgets/function_dialogs/gatetime.h @@ -11,7 +11,6 @@ #include "ui_gatetimebase.h" class QButtonGroup; -class QDialog; class Xml; //--------------------------------------------------------- @@ -19,9 +18,9 @@ class Xml; //--------------------------------------------------------- class GateTime : public QDialog, public Ui::GateTimeBase { + Q_OBJECT private: - Q_OBJECT - + QButtonGroup *rangeGroup; protected slots: diff --git a/muse2/muse/widgets/function_dialogs/legato.cpp b/muse2/muse/widgets/function_dialogs/legato.cpp new file mode 100644 index 00000000..0a181106 --- /dev/null +++ b/muse2/muse/widgets/function_dialogs/legato.cpp @@ -0,0 +1,88 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: legato.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ +// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) +//========================================================= + +#include <QButtonGroup> +#include "legato.h" +#include "xml.h" + +Legato::Legato(QWidget* parent) + : QDialog(parent) +{ + setupUi(this); + range_group = new QButtonGroup; + range_group->addButton(all_events_button,0); + range_group->addButton(selected_events_button,1); + range_group->addButton(looped_events_button,2); + range_group->addButton(selected_looped_button,3); + + pull_values(); +} + +void Legato::pull_values() +{ + range = range_group->checkedId(); + min_len = len_spinbox->value(); + allow_shortening = allow_shorten_checkbox->isChecked(); +} + +void Legato::accept() +{ + pull_values(); + QDialog::accept(); +} + +int Legato::exec() +{ + if ((range < 0) || (range > 3)) range=0; + + range_group->button(range)->setChecked(true); + len_spinbox->setValue(min_len); + allow_shorten_checkbox->setChecked(allow_shortening); + + return QDialog::exec(); +} + +void Legato::read_configuration(Xml& xml) +{ + for (;;) + { + Xml::Token token = xml.parse(); + if (token == Xml::Error || token == Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case Xml::TagStart: + if (tag == "range") + range=xml.parseInt(); + else if (tag == "min_len") + min_len=xml.parseInt(); + else if (tag == "allow_shortening") + allow_shortening=xml.parseInt(); + else + xml.unknown("Legato"); + break; + + case Xml::TagEnd: + if (tag == "legato") + return; + + default: + break; + } + } +} + +void Legato::write_configuration(int level, Xml& xml) +{ + xml.tag(level++, "legato"); + xml.intTag(level, "range", range); + xml.intTag(level, "min_len", min_len); + xml.intTag(level, "allow_shortening", allow_shortening); + xml.tag(level, "/legato"); +} diff --git a/muse2/muse/widgets/function_dialogs/legato.h b/muse2/muse/widgets/function_dialogs/legato.h new file mode 100644 index 00000000..00831830 --- /dev/null +++ b/muse2/muse/widgets/function_dialogs/legato.h @@ -0,0 +1,43 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: legato.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ +// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) +//========================================================= + +#ifndef __LEGATO_H__ +#define __LEGATO_H__ + +#include "ui_legatobase.h" + +class QButtonGroup; +class Xml; + +class Legato : public QDialog, public Ui::LegatoBase +{ + Q_OBJECT + private: + + QButtonGroup* range_group; + + protected slots: + void accept(); + void pull_values(); + + public: + Legato(QWidget* parent = 0); + + int range; + int min_len; + bool allow_shortening; + + void read_configuration(Xml& xml); + void write_configuration(int level, Xml& xml); + + + public slots: + int exec(); +}; + +#endif + diff --git a/muse2/muse/widgets/function_dialogs/legatobase.ui b/muse2/muse/widgets/function_dialogs/legatobase.ui new file mode 100644 index 00000000..7bc406df --- /dev/null +++ b/muse2/muse/widgets/function_dialogs/legatobase.ui @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LegatoBase</class> + <widget class="QDialog" name="LegatoBase"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>275</width> + <height>289</height> + </rect> + </property> + <property name="windowTitle"> + <string>MusE: Legato</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>11</number> + </property> + <item> + <widget class="QGroupBox" name="rangeBox"> + <property name="title"> + <string>Range</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>11</number> + </property> + <item> + <widget class="QRadioButton" name="all_events_button"> + <property name="text"> + <string>All Events</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="selected_events_button"> + <property name="text"> + <string>Selected Events</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="looped_events_button"> + <property name="text"> + <string>Looped Events</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="selected_looped_button"> + <property name="text"> + <string>Selected Looped</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Settings</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="1"> + <widget class="QSpinBox" name="len_spinbox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="suffix"> + <string> ticks</string> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>10000</number> + </property> + <property name="singleStep"> + <number>1</number> + </property> + <property name="value"> + <number>0</number> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Minimum Length</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Allow shortening notes</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="allow_shorten_checkbox"> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>6</number> + </property> + <item> + <spacer name="Spacer1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton"> + <property name="text"> + <string>OK</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>LegatoBase</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>LegatoBase</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/muse2/muse/widgets/function_dialogs/move.h b/muse2/muse/widgets/function_dialogs/move.h index 4c90a922..5049c567 100644 --- a/muse2/muse/widgets/function_dialogs/move.h +++ b/muse2/muse/widgets/function_dialogs/move.h @@ -15,8 +15,9 @@ class Xml; class Move : public QDialog, public Ui::MoveBase { + Q_OBJECT private: - Q_OBJECT + QButtonGroup* range_group; protected slots: diff --git a/muse2/muse/widgets/function_dialogs/quantize.h b/muse2/muse/widgets/function_dialogs/quantize.h index 399e2545..a857e667 100644 --- a/muse2/muse/widgets/function_dialogs/quantize.h +++ b/muse2/muse/widgets/function_dialogs/quantize.h @@ -15,8 +15,9 @@ class Xml; class Quantize : public QDialog, public Ui::QuantBase { + Q_OBJECT private: - Q_OBJECT + QButtonGroup* range_group; protected slots: diff --git a/muse2/muse/widgets/function_dialogs/remove.cpp b/muse2/muse/widgets/function_dialogs/remove.cpp index 5ad272ab..4a875135 100644 --- a/muse2/muse/widgets/function_dialogs/remove.cpp +++ b/muse2/muse/widgets/function_dialogs/remove.cpp @@ -25,6 +25,10 @@ Remove::Remove(QWidget* parent) void Remove::pull_values() { range = range_group->checkedId(); + len_thres_used=len_checkbox->isChecked(); + len_threshold=len_spinbox->value(); + velo_thres_used=velo_checkbox->isChecked(); + velo_threshold=velo_spinbox->value(); } void Remove::accept() @@ -38,6 +42,10 @@ int Remove::exec() if ((range < 0) || (range > 3)) range=0; range_group->button(range)->setChecked(true); + len_checkbox->setChecked(len_thres_used); + len_spinbox->setValue(len_threshold); + velo_checkbox->setChecked(velo_thres_used); + velo_spinbox->setValue(velo_threshold); return QDialog::exec(); } @@ -56,6 +64,14 @@ void Remove::read_configuration(Xml& xml) case Xml::TagStart: if (tag == "range") range=xml.parseInt(); + else if (tag == "velo_threshold") + velo_threshold=xml.parseInt(); + else if (tag == "velo_thres_used") + velo_thres_used=xml.parseInt(); + else if (tag == "len_threshold") + len_threshold=xml.parseInt(); + else if (tag == "len_thres_used") + len_thres_used=xml.parseInt(); else xml.unknown("Erase"); break; @@ -74,5 +90,9 @@ void Remove::write_configuration(int level, Xml& xml) { xml.tag(level++, "erase"); xml.intTag(level, "range", range); + xml.intTag(level, "velo_threshold", velo_threshold); + xml.intTag(level, "velo_thres_used", velo_thres_used); + xml.intTag(level, "len_threshold", len_threshold); + xml.intTag(level, "len_thres_used", len_thres_used); xml.tag(level, "/erase"); } diff --git a/muse2/muse/widgets/function_dialogs/remove.h b/muse2/muse/widgets/function_dialogs/remove.h index 5615ed42..33ac3fd0 100644 --- a/muse2/muse/widgets/function_dialogs/remove.h +++ b/muse2/muse/widgets/function_dialogs/remove.h @@ -15,8 +15,9 @@ class Xml; class Remove : public QDialog, public Ui::RemoveBase { + Q_OBJECT private: - Q_OBJECT + QButtonGroup* range_group; protected slots: @@ -27,6 +28,10 @@ class Remove : public QDialog, public Ui::RemoveBase Remove(QWidget* parent = 0); int range; + int velo_threshold; + bool velo_thres_used; + int len_threshold; + bool len_thres_used; void read_configuration(Xml& xml); void write_configuration(int level, Xml& xml); diff --git a/muse2/muse/widgets/function_dialogs/removebase.ui b/muse2/muse/widgets/function_dialogs/removebase.ui index 3381795c..79d541cc 100644 --- a/muse2/muse/widgets/function_dialogs/removebase.ui +++ b/muse2/muse/widgets/function_dialogs/removebase.ui @@ -10,7 +10,7 @@ <x>0</x> <y>0</y> <width>275</width> - <height>195</height> + <height>443</height> </rect> </property> <property name="windowTitle"> @@ -70,6 +70,101 @@ </widget> </item> <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Thresholds</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="1"> + <widget class="QSpinBox" name="velo_spinbox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="suffix"> + <string/> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>127</number> + </property> + <property name="singleStep"> + <number>1</number> + </property> + <property name="value"> + <number>16</number> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="len_spinbox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> ticks</string> + </property> + <property name="maximum"> + <number>10000</number> + </property> + <property name="value"> + <number>12</number> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="velo_checkbox"> + <property name="text"> + <string>Velocity</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="len_checkbox"> + <property name="text"> + <string>Length</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QLabel" name="label"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html></string> + </property> + <property name="textFormat"> + <enum>Qt::AutoText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout"> <property name="spacing"> <number>6</number> @@ -149,5 +244,37 @@ </hint> </hints> </connection> + <connection> + <sender>velo_checkbox</sender> + <signal>toggled(bool)</signal> + <receiver>velo_spinbox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>83</x> + <y>192</y> + </hint> + <hint type="destinationlabel"> + <x>198</x> + <y>193</y> + </hint> + </hints> + </connection> + <connection> + <sender>len_checkbox</sender> + <signal>toggled(bool)</signal> + <receiver>len_spinbox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>83</x> + <y>221</y> + </hint> + <hint type="destinationlabel"> + <x>198</x> + <y>222</y> + </hint> + </hints> + </connection> </connections> </ui> diff --git a/muse2/muse/widgets/function_dialogs/setlen.h b/muse2/muse/widgets/function_dialogs/setlen.h index ad66a38b..6a052bdb 100644 --- a/muse2/muse/widgets/function_dialogs/setlen.h +++ b/muse2/muse/widgets/function_dialogs/setlen.h @@ -15,8 +15,9 @@ class Xml; class Setlen : public QDialog, public Ui::SetlenBase { + Q_OBJECT private: - Q_OBJECT + QButtonGroup* range_group; protected slots: diff --git a/muse2/muse/widgets/function_dialogs/transpose.h b/muse2/muse/widgets/function_dialogs/transpose.h index 97dd443e..b85bb827 100644 --- a/muse2/muse/widgets/function_dialogs/transpose.h +++ b/muse2/muse/widgets/function_dialogs/transpose.h @@ -15,7 +15,6 @@ class Xml; class Transpose : public QDialog, public Ui::TransposeBase { - private: Q_OBJECT QButtonGroup* range_group; diff --git a/muse2/muse/widgets/function_dialogs/velocity.h b/muse2/muse/widgets/function_dialogs/velocity.h index cbea4e22..83aac54d 100644 --- a/muse2/muse/widgets/function_dialogs/velocity.h +++ b/muse2/muse/widgets/function_dialogs/velocity.h @@ -18,8 +18,9 @@ class Xml; //--------------------------------------------------------- class Velocity : public QDialog, public Ui::VelocityBase { - private: Q_OBJECT + private: + QButtonGroup* rangeGroup; protected slots: diff --git a/muse2/muse/widgets/genset.cpp b/muse2/muse/widgets/genset.cpp index edf3cfda..d8c76874 100644 --- a/muse2/muse/widgets/genset.cpp +++ b/muse2/muse/widgets/genset.cpp @@ -151,6 +151,7 @@ Shorter periods are desirable.</string> oldStyleStopCheckBox->setChecked(config.useOldStyleStopShortCut); moveArmedCheckBox->setChecked(config.moveArmedCheckBox); projectSaveCheckBox->setChecked(config.useProjectSaveDialog); + popsDefStayOpenCheckBox->setChecked(config.popupsDefaultStayOpen); //updateSettings(); // TESTING @@ -263,6 +264,7 @@ void GlobalSettingsConfig::updateSettings() oldStyleStopCheckBox->setChecked(config.useOldStyleStopShortCut); moveArmedCheckBox->setChecked(config.moveArmedCheckBox); projectSaveCheckBox->setChecked(config.useProjectSaveDialog); + popsDefStayOpenCheckBox->setChecked(config.popupsDefaultStayOpen); } //--------------------------------------------------------- @@ -344,6 +346,7 @@ void GlobalSettingsConfig::apply() config.useOldStyleStopShortCut = oldStyleStopCheckBox->isChecked(); config.moveArmedCheckBox = moveArmedCheckBox->isChecked(); config.useProjectSaveDialog = projectSaveCheckBox->isChecked(); + config.popupsDefaultStayOpen = popsDefStayOpenCheckBox->isChecked(); //muse->showMixer(config.mixerVisible); muse->showMixer1(config.mixer1Visible); diff --git a/muse2/muse/widgets/gensetbase.ui b/muse2/muse/widgets/gensetbase.ui index ca4b97f8..68f3ebb5 100644 --- a/muse2/muse/widgets/gensetbase.ui +++ b/muse2/muse/widgets/gensetbase.ui @@ -1333,7 +1333,7 @@ Adjusts responsiveness of audio controls and </property> </widget> </item> - <item row="4" column="0"> + <item row="5" column="0"> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -1346,6 +1346,27 @@ Adjusts responsiveness of audio controls and </property> </spacer> </item> + <item row="4" column="0"> + <widget class="QLabel" name="TextLabel1_3"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>Some popup menus stay open (else hold Ctrl)</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QCheckBox" name="popsDefStayOpenCheckBox"> + <property name="toolTip"> + <string>Allows some popup menus to stay open. +Otherwise, hold Ctrl to keep them open.</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/muse2/muse/widgets/header.cpp b/muse2/muse/widgets/header.cpp index 16cc374b..c12c8eaf 100644 --- a/muse2/muse/widgets/header.cpp +++ b/muse2/muse/widgets/header.cpp @@ -7,46 +7,69 @@ #include "header.h" #include "xml.h" +#include "popupmenu.h" #include <QStringList> #include <QStandardItemModel> +#include <QMouseEvent> //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void Header::readStatus(Xml& xml) - { - for (;;) { - Xml::Token token = xml.parse(); - const QString& tag = xml.s1(); - switch (token) { - case Xml::Error: - case Xml::End: - return; - case Xml::Text: - { - //QStringList l = QStringList::split(QString(" "), tag); - QStringList l = tag.split(QString(" "), QString::SkipEmptyParts); - int index = count() -1; - for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { - int section = visualIndex((*it).toInt()); - moveSection(section, index); - --index; +{ + + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::Text: + { + QStringList l = tag.split(QString(" "), QString::SkipEmptyParts); + int index = count() -1; + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + int logialIdx=abs((*it).toInt()); + bool isHidden = (*it).toInt() < 0 ? true:false; + int section = visualIndex(logialIdx); + moveSection(section, index); + if (isHidden) + hideSection(logialIdx-1); + else + showSection(logialIdx); + --index; + } + + // loop again looking for missing indexes + for (int i =0; i < count(); i++) { + bool foundIt=false; + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + int id=((*it).toInt()); + if ( id == i || i ==1-id ) + foundIt=true; } - } - break; - case Xml::TagStart: - xml.unknown("Header"); - break; - case Xml::TagEnd: - if (tag ==objectName()) - return; - default: - break; - } - } - } + if (foundIt == false) { + int section = visualIndex(i); + moveSection(section, i); + //printf("Adding missing i %d index %d section %d!\n", i, index, section); + } + } + } + break; + case Xml::TagStart: + xml.unknown("Header"); + break; + case Xml::TagEnd: + if (tag ==objectName()) + return; + default: + break; + } + } +} //--------------------------------------------------------- // writeStatus @@ -56,9 +79,13 @@ void Header::writeStatus(int level, Xml& xml) const { //xml.nput(level, "<%s> ", name()); xml.nput(level, "<%s> ", Xml::xmlString(objectName()).toLatin1().constData()); - int n = count() - 1; - for (int i = n; i >= 0; --i) - xml.nput("%d ", logicalIndex(i)); + int n = count(); + for (int i = n; i >= 0; --i) { + if (isSectionHidden(logicalIndex(i))) + xml.nput("%d ", -logicalIndex(i)-1); // hidden is stored as negative value starting from -1 + else + xml.nput("%d ", logicalIndex(i)); + } //xml.put("</%s>", name()); xml.put("</%s>", Xml::xmlString(objectName()).toLatin1().constData()); } @@ -73,8 +100,9 @@ Header::Header(QWidget* parent, const char* name) setObjectName(name); itemModel = new QStandardItemModel; setModel(itemModel); - //setResizeMode(QHeaderView::ResizeToContents); setDefaultSectionSize(30); + setStretchLastSection(true); + } //--------------------------------------------------------- @@ -86,7 +114,7 @@ void Header::setColumnLabel(const QString & text, int col, int width ) QStandardItem *sitem = new QStandardItem(text ); itemModel->setHorizontalHeaderItem(col, sitem); if (width > -1) - resizeSection(col, width); + resizeSection(col, width); } //--------------------------------------------------------- @@ -109,3 +137,40 @@ void Header::setWhatsThis(int col, const QString &text) item->setWhatsThis(text); } +void Header::mousePressEvent ( QMouseEvent * e ) +{ + if (e->button() == Qt::RightButton) { + + PopupMenu* p = new PopupMenu(); + p->disconnect(); + p->clear(); + p->setTitle(tr("Track Info Columns")); + QAction* act = 0; + + for(int i=0; i < count(); i++) { + act = p->addAction(itemModel->horizontalHeaderItem(logicalIndex(i))->text() + + "\t - "+ itemModel->horizontalHeaderItem(logicalIndex(i))->toolTip()); + + act->setCheckable(true); + act->setChecked(!isSectionHidden(logicalIndex(i))); + int data = logicalIndex(i); + act->setData(data); + } + connect(p, SIGNAL(triggered(QAction*)), SLOT(changeColumns(QAction*))); + p->exec(QCursor::pos()); + + delete p; + return; + } + + QHeaderView::mousePressEvent(e); + +} +void Header::changeColumns(QAction *a) +{ + int section = a->data().toInt(); + if (isSectionHidden(section)) + showSection(section); + else + hideSection(section); +} diff --git a/muse2/muse/widgets/header.h b/muse2/muse/widgets/header.h index 83680f8a..3e7b73a4 100644 --- a/muse2/muse/widgets/header.h +++ b/muse2/muse/widgets/header.h @@ -9,6 +9,7 @@ #define __HEADER_H__ #include <QHeaderView> +#include <QAction> class QStandardItemModel; @@ -26,6 +27,9 @@ class Header : public QHeaderView { void setColumnLabel( const QString & s, int col, int width = -1 ); void setToolTip(int col, const QString &text); void setWhatsThis(int col, const QString &text); + void mousePressEvent ( QMouseEvent * e ); + private slots: + void changeColumns(QAction* a); }; #endif diff --git a/muse2/muse/widgets/lcombo.h b/muse2/muse/widgets/lcombo.h index b125fce5..760d4512 100644 --- a/muse2/muse/widgets/lcombo.h +++ b/muse2/muse/widgets/lcombo.h @@ -20,8 +20,9 @@ class QString; //--------------------------------------------------------- class LabelCombo : public QWidget { - QComboBox* box; Q_OBJECT + QComboBox* box; + signals: void activated(int); diff --git a/muse2/muse/widgets/menutitleitem.cpp b/muse2/muse/widgets/menutitleitem.cpp new file mode 100644 index 00000000..8769eb02 --- /dev/null +++ b/muse2/muse/widgets/menutitleitem.cpp @@ -0,0 +1,48 @@ +//============================================================================= +// MusE +// Linux Music Editor +// (C) Copyright 1999-2001 Werner Schweer (ws@seh.de) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#include <QLabel> + +#include "menutitleitem.h" + +//--------------------------------------------------------- +// MenuTitleItem +//--------------------------------------------------------- + +MenuTitleItem::MenuTitleItem(const QString& ss, QWidget* parent) + : QWidgetAction(parent) + { + s = ss; + // Don't allow to click on it. + setEnabled(false); + // Just to be safe, set to -1 instead of default 0. + setData(-1); + } + +QWidget* MenuTitleItem::createWidget(QWidget *parent) +{ + QLabel* l = new QLabel(s, parent); + l->setAlignment(Qt::AlignCenter); + l->setAutoFillBackground(true); + //QPalette palette; + //palette.setColor(label->backgroundRole(), c); + //l->setPalette(palette); + l->setBackgroundRole(QPalette::Dark); + return l; +} + diff --git a/muse2/muse/widgets/menutitleitem.h b/muse2/muse/widgets/menutitleitem.h index 016d4663..20583bd1 100644 --- a/muse2/muse/widgets/menutitleitem.h +++ b/muse2/muse/widgets/menutitleitem.h @@ -15,6 +15,10 @@ //--------------------------------------------------------- class MenuTitleItem : public QWidgetAction { + Q_OBJECT + private: + + QString s; public: diff --git a/muse2/muse/widgets/meter.h b/muse2/muse/widgets/meter.h index 2b816040..cced6e7a 100644 --- a/muse2/muse/widgets/meter.h +++ b/muse2/muse/widgets/meter.h @@ -17,6 +17,7 @@ class QPainter; class Meter : public QFrame { + Q_OBJECT public: enum MeterType {DBMeter, LinMeter}; private: @@ -29,7 +30,7 @@ class Meter : public QFrame { void drawVU(QPainter& p, int, int, int); - Q_OBJECT + void paintEvent(QPaintEvent*); void resizeEvent(QResizeEvent*); void mousePressEvent(QMouseEvent*); diff --git a/muse2/muse/widgets/mtrackinfo.cpp b/muse2/muse/widgets/mtrackinfo.cpp index acdfb42f..6dd02931 100644 --- a/muse2/muse/widgets/mtrackinfo.cpp +++ b/muse2/muse/widgets/mtrackinfo.cpp @@ -26,6 +26,7 @@ #include "app.h" #include "route.h" #include "popupmenu.h" +#include "routepopup.h" //--------------------------------------------------------- // setTrack @@ -44,10 +45,13 @@ void MidiTrackInfo::setTrack(Track* t) selected = t; QPalette pal; - if(selected->type() == Track::DRUM) + if(selected->type() == Track::DRUM) { pal.setColor(trackNameLabel->backgroundRole(), config.drumTrackLabelBg); - else - pal.setColor(trackNameLabel->backgroundRole(), config.midiTrackLabelBg); + iOutputChannel->setEnabled(false); + } else { + pal.setColor(trackNameLabel->backgroundRole(), config.midiTrackLabelBg); + iOutputChannel->setEnabled(true); + } trackNameLabel->setPalette(pal); updateTrackInfo(-1); @@ -574,33 +578,6 @@ void MidiTrackInfo::iOutputPortChanged(int index) } //--------------------------------------------------------- -// routingPopupMenuActivated -//--------------------------------------------------------- - -//void MidiTrackInfo::routingPopupMenuActivated(int n) -void MidiTrackInfo::routingPopupMenuActivated(QAction* act) -{ - ///if(!midiTrackInfo || gRoutingPopupMenuMaster != midiTrackInfo || !selected || !selected->isMidiTrack()) - if((gRoutingPopupMenuMaster != this) || !selected || !selected->isMidiTrack()) - return; - muse->routingPopupMenuActivated(selected, act->data().toInt()); -} - -#if 0 -//--------------------------------------------------------- -// routingPopupViewActivated -//--------------------------------------------------------- - -void MidiTrackInfo::routingPopupViewActivated(const QModelIndex& mdi) -{ - ///if(!midiTrackInfo || gRoutingPopupMenuMaster != midiTrackInfo || !selected || !selected->isMidiTrack()) - if(gRoutingPopupMenuMaster != this || !selected || !selected->isMidiTrack()) - return; - muse->routingPopupMenuActivated(selected, mdi.data().toInt()); -} -#endif - -//--------------------------------------------------------- // inRoutesPressed //--------------------------------------------------------- @@ -611,44 +588,9 @@ void MidiTrackInfo::inRoutesPressed() if(!selected->isMidiTrack()) return; - PopupMenu* pup = muse->prepareRoutingPopupMenu(selected, false); - //PopupView* pup = muse->prepareRoutingPopupView(selected, false); - - /* - QPoint ppt = QCursor::pos(); - - int i = 0; - for( ; i < MIDI_PORTS; ++i) - { - if(midiPorts[i].device() && !midiPorts[pi].device()->isSynti()) - break; - } - if(!pup || i == MIDI_PORTS) - { - int ret = QMessageBox::warning(this, tr("No devices"), - tr("There are no midi port devices defined.\n" - "Do you want to open the midi configuration dialog?"), - QMessageBox::Ok | QMessageBox::Cancel, - QMessageBox::Ok); - if (ret == QMessageBox::Ok) { - //printf("open config midi ports\n"); - muse->configMidiPorts(); - } - if(!pup) - return; - } - */ - - ///gRoutingPopupMenuMaster = midiTrackInfo; - gRoutingPopupMenuMaster = this; - connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); - //connect(pup, SIGNAL(activated(const QModelIndex&)), SLOT(routingPopupViewActivated(const QModelIndex&))); - connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); - //connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupViewAboutToHide())); - pup->popup(QCursor::pos()); - //pup->setVisible(true); + RoutePopupMenu* pup = muse->getRoutingPopupMenu(); iRButton->setDown(false); - return; + pup->exec(QCursor::pos(), selected, false); } //--------------------------------------------------------- @@ -662,17 +604,9 @@ void MidiTrackInfo::outRoutesPressed() if(!selected->isMidiTrack()) return; - PopupMenu* pup = muse->prepareRoutingPopupMenu(selected, true); - if(!pup) - return; - - ///gRoutingPopupMenuMaster = midiTrackInfo; - gRoutingPopupMenuMaster = this; - connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); - connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); - pup->popup(QCursor::pos()); + RoutePopupMenu* pup = muse->getRoutingPopupMenu(); oRButton->setDown(false); - return; + pup->exec(QCursor::pos(), selected, true); } //--------------------------------------------------------- @@ -1036,6 +970,29 @@ void MidiTrackInfo::iPanChanged(int val) } //--------------------------------------------------------- +// instrPopupActivated +//--------------------------------------------------------- + +void MidiTrackInfo::instrPopupActivated(QAction* act) +{ + //printf("MidiTrackInfo::instrPopupActivated\n"); + + if(act && selected) + { + int rv = act->data().toInt(); + if(rv != -1) + { + MidiTrack* track = (MidiTrack*)selected; + int channel = track->outChannel(); + int port = track->outPort(); + MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, rv); + audio->msgPlayMidiEvent(&ev); + updateTrackInfo(-1); + } + } +} + +//--------------------------------------------------------- // instrPopup //--------------------------------------------------------- @@ -1047,26 +1004,35 @@ void MidiTrackInfo::instrPopup() int channel = track->outChannel(); int port = track->outPort(); MidiInstrument* instr = midiPorts[port].instrument(); - QMenu* pup = new QMenu; - ///instr->populatePatchPopup(pop, channel, song->mtype(), track->type() == Track::DRUM); + //QMenu* pup = new QMenu; + PopupMenu* pup = new PopupMenu(true); + + //instr->populatePatchPopup(pop, channel, song->mtype(), track->type() == Track::DRUM); instr->populatePatchPopup(pup, channel, song->mtype(), track->type() == Track::DRUM); - ///if(pop->actions().count() == 0) - /// return; + //if(pop->actions().count() == 0) + // return; if(pup->actions().count() == 0) { delete pup; return; } - ///QAction *act = pop->exec(iPatch->mapToGlobal(QPoint(10,5))); + connect(pup, SIGNAL(triggered(QAction*)), SLOT(instrPopupActivated(QAction*))); + //connect(pup, SIGNAL(hovered(QAction*)), SLOT(instrPopupActivated(QAction*))); + + //QAction *act = pop->exec(iPatch->mapToGlobal(QPoint(10,5))); QAction *act = pup->exec(iPatch->mapToGlobal(QPoint(10,5))); - if (act) { - int rv = act->data().toInt(); - MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, rv); - audio->msgPlayMidiEvent(&ev); - updateTrackInfo(-1); - } + if(act) + { + int rv = act->data().toInt(); + if(rv != -1) + { + MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, rv); + audio->msgPlayMidiEvent(&ev); + updateTrackInfo(-1); + } + } delete pup; } @@ -1275,15 +1241,6 @@ void MidiTrackInfo::updateTrackInfo(int flags) return; MidiTrack* track = (MidiTrack*)selected; - // p3.3.47 Update the routing popup menu if anything relevant changes. - //if(gRoutingPopupMenuMaster == midiTrackInfo && selected && (flags & (SC_ROUTE | SC_CHANNELS | SC_CONFIG))) - if(flags & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) // p3.3.50 - // Use this handy shared routine. - //muse->updateRouteMenus(selected); - ///muse->updateRouteMenus(selected, midiTrackInfo); // p3.3.50 - muse->updateRouteMenus(selected, this); - - // Added by Tim. p3.3.9 setLabelText(); setLabelFont(); diff --git a/muse2/muse/widgets/mtrackinfo.h b/muse2/muse/widgets/mtrackinfo.h index 4e06f1d0..ed229ad6 100644 --- a/muse2/muse/widgets/mtrackinfo.h +++ b/muse2/muse/widgets/mtrackinfo.h @@ -46,8 +46,7 @@ class MidiTrackInfo : public QWidget, public Ui::MidiTrackInfoBase void recEchoToggled(bool); void inRoutesPressed(); void outRoutesPressed(); - void routingPopupMenuActivated(QAction*); - //void routingPopupViewActivated(const QModelIndex&); + void instrPopupActivated(QAction*); protected slots: virtual void heartBeat(); diff --git a/muse2/muse/widgets/musewidgetsplug.cpp b/muse2/muse/widgets/musewidgetsplug.cpp index 8cb0b57e..993b0fb8 100644 --- a/muse2/muse/widgets/musewidgetsplug.cpp +++ b/muse2/muse/widgets/musewidgetsplug.cpp @@ -190,13 +190,14 @@ GlobalConfigValues config = { true, // useDenormalBias false, // useOutputLimiter true, // showDidYouKnow - false, // vstInPlace Enable VST in-place processing + false, // vstInPlace Enable VST in-place processing 44100, // Dummy audio preferred sample rate 512 // Dummy audio buffer size QString("./"), // projectBaseFolder true, // projectStoreInFolder true, // useProjectSaveDialog - 64 // minControlProcessPeriod + 64, // minControlProcessPeriod + false // popupsDefaultStayOpen }; //--------------------------------------------------------- diff --git a/muse2/muse/widgets/noteinfo.h b/muse2/muse/widgets/noteinfo.h index cc8fe16d..64842988 100644 --- a/muse2/muse/widgets/noteinfo.h +++ b/muse2/muse/widgets/noteinfo.h @@ -25,6 +25,8 @@ class Pos; //--------------------------------------------------------- class NoteInfo : public QToolBar { + Q_OBJECT + ///PosEdit* selTime; Awl::PosEdit* selTime; QSpinBox* selLen; @@ -33,7 +35,7 @@ class NoteInfo : public QToolBar { QSpinBox* selVelOff; bool deltaMode; - Q_OBJECT + public: enum ValType {VAL_TIME, VAL_LEN, VAL_VELON, VAL_VELOFF, VAL_PITCH }; diff --git a/muse2/muse/widgets/pitchlabel.h b/muse2/muse/widgets/pitchlabel.h index 6372f711..d29a4ee4 100644 --- a/muse2/muse/widgets/pitchlabel.h +++ b/muse2/muse/widgets/pitchlabel.h @@ -15,9 +15,11 @@ //--------------------------------------------------------- class PitchLabel : public QLabel { + Q_OBJECT + bool _pitchMode; int _value; - Q_OBJECT + protected: QSize sizeHint() const; diff --git a/muse2/muse/widgets/popupmenu.cpp b/muse2/muse/widgets/popupmenu.cpp index 862bda91..adbe7dd6 100644 --- a/muse2/muse/widgets/popupmenu.cpp +++ b/muse2/muse/widgets/popupmenu.cpp @@ -6,25 +6,51 @@ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // // PopupMenu sub-class of QMenu created by Tim. +// (C) Copyright 2010-2011 Tim E. Real (terminator356 A T sourceforge D O T net) //========================================================= //#include <stdio.h> #include <QMouseEvent> +#include <QHoverEvent> #include <QAction> +#include <QPoint> +#include <QDesktopWidget> +#include <QApplication> +//#include <QTimer> + #include <stdio.h> //#include <QStandardItemModel> #include "popupmenu.h" +#include "gconfig.h" +#include "route.h" + //====================== // PopupMenu //====================== -PopupMenu::PopupMenu(QWidget* parent) - : QMenu(parent) +//PopupMenu::PopupMenu() +//{ +// init(); +//} + +PopupMenu::PopupMenu(bool stayOpen) + : _stayOpen(stayOpen) { - // Menus will trigger! Set to make sure our trigger handlers ignore menus. - menuAction()->setData(-1); + init(); +} + +PopupMenu::PopupMenu(QWidget* parent, bool stayOpen) + : QMenu(parent), _stayOpen(stayOpen) +{ + init(); +} + +PopupMenu::PopupMenu(const QString& title, QWidget* parent, bool stayOpen) + : QMenu(title, parent), _stayOpen(stayOpen) +{ + init(); } PopupMenu::~PopupMenu() @@ -32,6 +58,23 @@ PopupMenu::~PopupMenu() //printf("PopupMenu::~PopupMenu\n"); } +void PopupMenu::init() +{ + // Menus will trigger! Set to make sure our trigger handlers ignore menus. + menuAction()->setData(-1); + + //_stayOpen = false; + moveDelta = 0; + + #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + timer = new QTimer(this); + timer->setInterval(100); + timer->setSingleShot(false); + connect(this, SIGNAL(hovered(QAction*)), SLOT(popHovered(QAction*))); + connect(timer, SIGNAL(timeout()), SLOT(timerHandler())); + #endif // POPUP_MENU_DISABLE_AUTO_SCROLL +} + void PopupMenu::clear() { QList<QAction*> list = actions(); @@ -41,7 +84,7 @@ void PopupMenu::clear() QMenu* menu = act->menu(); if(menu) { - menu->clear(); + menu->clear(); // Recursive. act->setMenu(0); // CHECK: Is this OK? delete menu; } @@ -49,9 +92,32 @@ void PopupMenu::clear() // Now let QT remove and delete this menu's actions. QMenu::clear(); + + #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + connect(this, SIGNAL(hovered(QAction*)), SLOT(popHovered(QAction*))); + connect(timer, SIGNAL(timeout()), SLOT(timerHandler())); + #endif // POPUP_MENU_DISABLE_AUTO_SCROLL } -QAction* PopupMenu::findActionFromData(QVariant v) +void PopupMenu::clearAllChecks() const +{ + QList<QAction*> list = actions(); + for(int i = 0; i < list.size(); ++i) + { + QAction* act = list[i]; + PopupMenu* menu = static_cast <PopupMenu*>(act->menu()); + if(menu) + menu->clearAllChecks(); // Recursive. + if(act->isCheckable()) + { + act->blockSignals(true); + act->setChecked(false); + act->blockSignals(false); + } + } +} + +QAction* PopupMenu::findActionFromData(const QVariant& v) const { QList<QAction*> list = actions(); for(int i = 0; i < list.size(); ++i) @@ -60,52 +126,224 @@ QAction* PopupMenu::findActionFromData(QVariant v) PopupMenu* menu = (PopupMenu*)act->menu(); if(menu) { - if(QAction* actm = menu->findActionFromData(v)) + if(QAction* actm = menu->findActionFromData(v)) // Recursive. return actm; } + + // "Operator == Compares this QVariant with v and returns true if they are equal, + // otherwise returns false. In the case of custom types, their equalness operators + // are not called. Instead the values' addresses are compared." + // + // Take care of struct Route first. Insert other future custom structures here too ! + if(act->data().canConvert<Route>() && v.canConvert<Route>()) + { + if(act->data().value<Route>() == v.value<Route>()) + return act; + } + else if(act->data() == v) return act; } return 0; } -void PopupMenu::mouseReleaseEvent(QMouseEvent *e) +bool PopupMenu::event(QEvent* event) { - //Q_D(QMenu); - //if (d->mouseEventTaken(e)) - // return; + //printf("PopupMenu::event type:%d\n", event->type()); + + switch(event->type()) + { + #ifndef POPUP_MENU_DISABLE_STAY_OPEN + case QEvent::MouseButtonDblClick: + { + if(_stayOpen) + //if(_stayOpen && config.popupsDefaultStayOpen) + { + QMouseEvent* e = static_cast<QMouseEvent*>(event); + if(e->modifiers() == Qt::NoModifier) + { + event->accept(); + // Convert into a return press, which selects the item and closes the menu. + // Note that with double click, it's a press followed by release followed by double click. + // That would toggle our item twice eg on->off->on, which is hopefully OK. + QKeyEvent ke(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); + //ke.ignore(); // Pass it on + return QMenu::event(&ke); + } + } + } + break; + case QEvent::KeyPress: + { + if(_stayOpen) + //if(_stayOpen && config.popupsDefaultStayOpen) + { + QKeyEvent* e = static_cast<QKeyEvent*>(event); + if(e->modifiers() == Qt::NoModifier && e->key() == Qt::Key_Space) + { + QAction* act = activeAction(); + if(act) + { + act->trigger(); + event->accept(); + return true; // We handled it. + } + } + } + } + break; + #endif // POPUP_MENU_DISABLE_STAY_OPEN + + #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + case QEvent::MouseMove: + { + QMouseEvent* e = static_cast<QMouseEvent*>(event); + QPoint globPos = e->globalPos(); + //QPoint pos = e->pos(); + int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. + + //printf("PopupMenu::event MouseMove: pos x:%d y:%d globPos x:%d y:%d\n", + // pos.x(), pos.y(), globPos.x(), globPos.y()); + + /* + //QAction* action = actionAt(globPos); + QAction* action = actionAt(pos); + if(action) + { + QRect r = actionGeometry(action); + //printf(" act x:%d y:%d w:%d h:%d popup px:%d py:%d pw:%d ph:%d\n", + // r.x(), r.y(), r.width(), r.height(), x(), y(), width(), height()); + + //action->hover(); + } + */ + + if(x() < 0 && globPos.x() <= 0) // If on the very first pixel (or beyond) + { + moveDelta = 32; + if(!timer->isActive()) + timer->start(); + event->accept(); + return true; + } + else + if(x() + width() >= dw && globPos.x() >= (dw -1)) // If on the very last pixel (or beyond) + { + moveDelta = -32; + if(!timer->isActive()) + timer->start(); + event->accept(); + return true; + } + + if(timer->isActive()) + timer->stop(); + + //event->accept(); + //return true; + + event->ignore(); // Pass it on + //return QMenu::event(event); + } + break; + #endif // POPUP_MENU_DISABLE_AUTO_SCROLL + + default: + break; + } + + return QMenu::event(event); +} - //d->mouseDown = false; - //QAction *action = d->actionAt(e->pos()); - QAction *action = actionAt(e->pos()); +#ifndef POPUP_MENU_DISABLE_AUTO_SCROLL +void PopupMenu::timerHandler() +{ + // printf("PopupMenu::timerHandler\n"); + + //if(!isVisible() || !hasFocus()) + if(!isVisible()) + { + timer->stop(); + return; + } + + int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. + int nx = x() + moveDelta; + if(moveDelta < 0 && nx + width() < dw) + { + timer->stop(); + nx = dw - width(); + } + else + if(moveDelta > 0 && nx > 0) + { + timer->stop(); + nx = 0; + } + + move(nx, y()); +} + +void PopupMenu::popHovered(QAction* action) +{ + //timer->stop(); + + //moveDelta = 0; + if(action) + { + int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. - //for(QWidget *caused = this; caused;) { - // if (QMenu *m = qobject_cast<QMenu*>(caused)) { - // QAction *currentAction = d->currentAction; - // if(currentAction && (!currentAction->isEnabled() || currentAction->menu() || currentAction->isSeparator())) - // currentAction = 0; - // caused = m->d_func()->causedPopup.widget; - // if (m->d_func()->eventLoop) - // m->d_func()->syncAction = currentAction; // synchronous operation - // } else { - // break; - // } - //} + QRect r = actionGeometry(action); + //printf("PopupMenu::popHovered x:%d y:%d w:%d h:%d px:%d py:%d pw:%d ph:%d\n", + // r.x(), r.y(), r.width(), r.height(), x(), y(), width(), height()); + //printf("PopupMenu::popHovered x:%d y:%d w:%d h:%d px:%d py:%d pw:%d ph:%d dtw:%d\n", + // r.x(), r.y(), r.width(), r.height(), x(), y(), width(), height(), dw); + //int x = r.x() + ctrlSubPop->x(); + if(x() + r.x() < 0) + //setGeometry(0, y(), width(), height()); + //scroll(-x, 0); + //move(-r.x() + 32, y()); // Allow some of left column to show so that mouse can move over it. + //move(-r.x() + r.width(), y()); // Allow some of left column to show so that mouse can move over it. + //moveDelta = x() - r.x() + 32; + move(-r.x(), y()); + else + if(r.x() + r.width() + x() > dw) + //setGeometry(1200 - r.x() - r.width(), y(), width(), height()); + //scroll(-x + 1200, 0); + //move(dw - r.x() - r.width() - 32, y()); // Allow some of right column to show so that mouse can move over it. + //move(dw - r.x(), y()); // Allow some of right column to show so that mouse can move over it. + //moveDelta = x() + dw - r.x() - r.width() - 32; + move(dw - r.x() - r.width(), y()); + } + + //if(moveDelta == 0) + // timer->stop(); - //if (action && action == d->currentAction) { - if (action && action == activeAction() && !action->isSeparator() && action->isEnabled()) +} +#endif // POPUP_MENU_DISABLE_AUTO_SCROLL + +void PopupMenu::mouseReleaseEvent(QMouseEvent *e) +{ + #ifdef POPUP_MENU_DISABLE_STAY_OPEN + QMenu::mouseReleaseEvent(e); + return; + + #else + // Check for Ctrl to stay open. + if(!_stayOpen || (!config.popupsDefaultStayOpen && (e->modifiers() & Qt::ControlModifier) == 0)) { - //if (action->menu()) - // action->menu()->d_func()->setFirstActionActive(); - //else - //d->activateAction(action, QAction::Trigger); - action->activate(QAction::Trigger); - } + QMenu::mouseReleaseEvent(e); + return; + } + + //printf("PopupMenu::mouseReleaseEvent\n"); + QAction *action = actionAt(e->pos()); + if (action && action == activeAction() && !action->isSeparator() && action->isEnabled()) + action->activate(QAction::Trigger); else - //if (d->motions > 6) { - // d->hideUpToMenuBar(); - // } QMenu::mouseReleaseEvent(e); + + #endif // POPUP_MENU_DISABLE_STAY_OPEN } /* diff --git a/muse2/muse/widgets/popupmenu.h b/muse2/muse/widgets/popupmenu.h index c06d51f4..e0e7d26f 100644 --- a/muse2/muse/widgets/popupmenu.h +++ b/muse2/muse/widgets/popupmenu.h @@ -6,12 +6,21 @@ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // // PopupMenu sub-class of QMenu created by Tim. +// (C) Copyright 2010-2011 Tim E. Real (terminator356 A T sourceforge D O T net) //========================================================= #ifndef __POPUPMENU_H__ #define __POPUPMENU_H__ +// Just in case Qt ever adds these features natively, we would need to turn our features off! +//#define POPUP_MENU_DISABLE_STAY_OPEN +//#define POPUP_MENU_DISABLE_AUTO_SCROLL + #include <QMenu> +#ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + #include <QTimer> +#endif + //#include <QMouseEvent> //#include <QColumnView> @@ -19,20 +28,41 @@ class QWidget; class QMouseEvent; class QVariant; class QAction; +class QEvent; +//class QTimer; //class QStandardItemModel; class PopupMenu : public QMenu { Q_OBJECT + bool _stayOpen; + #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + QTimer* timer; + #endif + int moveDelta; + void init(); + + private slots: + #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + void popHovered(QAction*); + void timerHandler(); + #endif + protected: void mouseReleaseEvent(QMouseEvent *); + bool event(QEvent*); public: - PopupMenu(QWidget* parent=0); + //PopupMenu(); + PopupMenu(bool stayOpen); + PopupMenu(QWidget* parent=0, bool stayOpen = false); + PopupMenu(const QString& title, QWidget* parent = 0, bool stayOpen = false); ~PopupMenu(); void clear(); - QAction* findActionFromData(QVariant); + QAction* findActionFromData(const QVariant&) const; + bool stayOpen() const { return _stayOpen; } + void clearAllChecks() const; }; diff --git a/muse2/muse/widgets/poslabel.h b/muse2/muse/widgets/poslabel.h index 29c5297d..7be236ec 100644 --- a/muse2/muse/widgets/poslabel.h +++ b/muse2/muse/widgets/poslabel.h @@ -15,10 +15,12 @@ //--------------------------------------------------------- class PosLabel : public QLabel { + Q_OBJECT + bool _smpte; unsigned _tickValue; unsigned _sampleValue; - Q_OBJECT + void updateValue(); diff --git a/muse2/muse/widgets/projectcreateimpl.h b/muse2/muse/widgets/projectcreateimpl.h index 77547c1a..3ca61e36 100644 --- a/muse2/muse/widgets/projectcreateimpl.h +++ b/muse2/muse/widgets/projectcreateimpl.h @@ -6,7 +6,7 @@ class ProjectCreateImpl : public QDialog, Ui::ProjectCreate { -Q_OBJECT + Q_OBJECT QString directoryPath; public: diff --git a/muse2/muse/widgets/routepopup.cpp b/muse2/muse/widgets/routepopup.cpp new file mode 100644 index 00000000..910d693d --- /dev/null +++ b/muse2/muse/widgets/routepopup.cpp @@ -0,0 +1,1416 @@ +//========================================================= +// 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 version 2. +// +// 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., 675 Mass Ave, Cambridge, MA 02139, 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" + +//--------------------------------------------------------- +// addMenuItem +//--------------------------------------------------------- + +int RoutePopupMenu::addMenuItem(AudioTrack* track, Track* route_track, PopupMenu* lb, int id, int channel, int channels, bool isOutput) +{ + // totalInChannels is only used by syntis. + int toch = ((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; + + 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; + + Route r(route_track, isOutput ? ach : bch, channels); + + r.remoteChannel = isOutput ? bch : ach; + + act->setData(qVariantFromValue(r)); + + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == 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; + } + } + } + return ++id; +} + +//--------------------------------------------------------- +// addAuxPorts +//--------------------------------------------------------- + +int RoutePopupMenu::addAuxPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) + { + AuxList* al = song->auxs(); + for (iAudioAux i = al->begin(); i != al->end(); ++i) { + Track* track = *i; + if (t == track) + continue; + id = addMenuItem(t, track, lb, id, channel, channels, isOutput); + } + return id; + } + +//--------------------------------------------------------- +// addInPorts +//--------------------------------------------------------- + +int RoutePopupMenu::addInPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) + { + InputList* al = song->inputs(); + for (iAudioInput i = al->begin(); i != al->end(); ++i) { + Track* track = *i; + if (t == track) + continue; + id = addMenuItem(t, track, lb, id, channel, channels, isOutput); + } + return id; + } + +//--------------------------------------------------------- +// addOutPorts +//--------------------------------------------------------- + +int RoutePopupMenu::addOutPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) + { + OutputList* al = song->outputs(); + for (iAudioOutput i = al->begin(); i != al->end(); ++i) { + Track* track = *i; + if (t == track) + continue; + id = addMenuItem(t, track, lb, id, channel, channels, isOutput); + } + return id; + } + +//--------------------------------------------------------- +// addGroupPorts +//--------------------------------------------------------- + +int RoutePopupMenu::addGroupPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) + { + GroupList* al = song->groups(); + for (iAudioGroup i = al->begin(); i != al->end(); ++i) { + Track* track = *i; + if (t == track) + continue; + id = addMenuItem(t, track, lb, id, channel, channels, isOutput); + } + return id; + } + +//--------------------------------------------------------- +// addWavePorts +//--------------------------------------------------------- + +int RoutePopupMenu::addWavePorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) + { + WaveTrackList* al = song->waves(); + for (iWaveTrack i = al->begin(); i != al->end(); ++i) { + Track* track = *i; + if (t == track) + continue; + id = addMenuItem(t, track, lb, id, channel, channels, isOutput); + } + return id; + } + +//--------------------------------------------------------- +// addSyntiPorts +//--------------------------------------------------------- + +int RoutePopupMenu::addSyntiPorts(AudioTrack* t, PopupMenu* lb, int id, + int channel, int channels, bool isOutput) +{ + RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); + + QAction* act; + + SynthIList* al = song->syntis(); + for (iSynthI i = al->begin(); i != al->end(); ++i) + { + Track* track = *i; + if (t == track) + continue; + int toch = ((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() != Track::AUDIO_SOFTSYNTH) ? toch : ((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", chpup->tr("Channel").toLatin1().constData(), ch+1, ch+2); + else + snprintf(buffer, 128, "%s %d", chpup->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; + + Route rt(track, (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans); + rt.remoteChannel = (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach; + + act->setData(qVariantFromValue(rt)); + + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == 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; + } + } + } + ++id; + } + + lb->addMenu(chpup); + } + } + return id; +} + +//--------------------------------------------------------- +// addMultiChannelOutPorts +//--------------------------------------------------------- + +int RoutePopupMenu::addMultiChannelPorts(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() != Track::AUDIO_SOFTSYNTH) ? toch : t->totalInChannels(); + + if(chans > 1) + pup->addAction(new MenuTitleItem("<Mono>", 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 Track::AUDIO_INPUT: + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + case 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 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 Track::WAVE: + case Track::AUDIO_SOFTSYNTH: + case 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", pup->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("<Stereo>", 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 Track::AUDIO_INPUT: + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + case 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 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 Track::WAVE: + case Track::AUDIO_SOFTSYNTH: + case 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", pup->tr("Channel").toLatin1().constData(), ch+1, ch+2); + chpup->setTitle(QString(buffer)); + pup->addMenu(chpup); + } + } + } + + return id; +} + +//--------------------------------------------------------- +// nonSyntiTrackAddSyntis +//--------------------------------------------------------- + +int RoutePopupMenu::nonSyntiTrackAddSyntis(AudioTrack* t, PopupMenu* lb, int id, bool isOutput) +{ + RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); + + QAction* act; + SynthIList* al = song->syntis(); + for (iSynthI i = al->begin(); i != al->end(); ++i) + { + Track* track = *i; + if (t == track) + continue; + + int toch = ((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() != Track::AUDIO_SOFTSYNTH) ? toch : ((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("<Mono>", chpup)); + + for(int ch = 0; ch < chans; ++ch) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d", chpup->tr("Channel").toLatin1().constData(), ch+1); + act = chpup->addAction(QString(buffer)); + act->setCheckable(true); + + int ach = ch; + int bch = -1; + + Route rt(track, isOutput ? bch : ach, 1); + + rt.remoteChannel = isOutput ? ach : bch; + + act->setData(qVariantFromValue(rt)); + + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == 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; + } + } + } + ++id; + } + + chans -= 1; + if(chans > 0) + { + // Ignore odd numbered left-over channels. + //int schans = (chans & ~1) - 1; + + chpup->addSeparator(); + chpup->addAction(new MenuTitleItem("<Stereo>", chpup)); + + for(int ch = 0; ch < chans; ++ch) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").toLatin1().constData(), ch+1, ch+2); + act = chpup->addAction(QString(buffer)); + act->setCheckable(true); + + int ach = ch; + int bch = -1; + + Route rt(track, isOutput ? bch : ach, 2); + + rt.remoteChannel = isOutput ? ach : bch; + + act->setData(qVariantFromValue(rt)); + + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == 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; + } + } + } + ++id; + } + } + + lb->addMenu(chpup); + } + } + return id; +} + +//--------------------------------------------------------- +// addMidiPorts +//--------------------------------------------------------- + +int RoutePopupMenu::addMidiPorts(AudioTrack* t, PopupMenu* pup, int id, bool isOutput) +{ + QAction* act; + for(int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* mp = &midiPorts[i]; + 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->rwFlags() & (isOutput ? 1 : 2))) + // continue; + + // Do not list synth devices! + if(md->isSynti()) + continue; + + RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); + + PopupMenu* subp = new PopupMenu(pup, true); + subp->setTitle(md->name()); + + 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(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i) + { + // We have a route to the midi port. Grab the channel mask. + chanmask = ir->channel; + break; + } + } + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + act = subp->addAction(QString("Channel %1").arg(ch+1)); + act->setCheckable(true); + + int chbit = 1 << ch; + 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); + Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. + act->setData(qVariantFromValue(togRoute)); + ++id; + + pup->addMenu(subp); + } + return id; +} + + +//====================== +// RoutePopupMenu +//====================== + +RoutePopupMenu::RoutePopupMenu(QWidget* parent, Track* track, bool isOutput) + : _track(track), _isOutMenu(isOutput) +{ + _pup = new PopupMenu(parent, true); + init(); +} + +RoutePopupMenu::RoutePopupMenu(const QString& title, QWidget* parent, Track* track, bool isOutput) + : _track(track), _isOutMenu(isOutput) +{ + _pup = new PopupMenu(title, parent, true); + init(); +} + +RoutePopupMenu::~RoutePopupMenu() +{ + //printf("RoutePopupMenu::~RoutePopupMenu\n"); + // Make sure to clear which clears and deletes any sub popups. + _pup->clear(); + delete _pup; +} + +void RoutePopupMenu::init() +{ + connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int))); +} + +void RoutePopupMenu::songChanged(int 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. + + //printf("RoutePopupMenu::updateRouteMenus\n"); + + if(!_track || !_pup || _pup->actions().isEmpty() || !_pup->isVisible()) + return; + + RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); + + // Clear all the action check marks. + _pup->clearAllChecks(); + + // Take care of Midi Port to Audio Input routes first... + if(_isOutMenu && _track->isMidiTrack()) + { + int port = ((MidiTrack*)_track)->outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + MidiPort* mp = &midiPorts[port]; + RouteList* mprl = mp->outRoutes(); + for (ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) + { + if(ir->type == Route::TRACK_ROUTE && ir->track && ir->track->type() == Track::AUDIO_INPUT) + { + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + int chbits = 1 << ch; + if(ir->channel & chbits) + { + Route r(ir->track, chbits); + //printf("RoutePopupMenu::updateRouteMenus MidiPort to AudioInput chbits:%d\n", chbits); + QAction* act = _pup->findActionFromData(qVariantFromValue(r)); + if(act) + act->setChecked(true); + } + } + } + } + } + } + + // Now check the ones that are found in the route list. + for(ciRoute irl = rl->begin(); irl != rl->end(); ++irl) + { + // Do MidiTrack to MidiPort routes... + if(irl->type == Route::MIDI_PORT_ROUTE) + { + //printf("RoutePopupMenu::updateRouteMenus MIDI_PORT_ROUTE\n"); + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + int chbits = 1 << ch; + if(irl->channel & chbits) + { + Route r(irl->midiPort, chbits); + QAction* act = _pup->findActionFromData(qVariantFromValue(r)); + if(act) + act->setChecked(true); + } + } + } + else + // Do all other routes... + { + //printf("RoutePopupMenu::updateRouteMenus other irl type:%d\n", irl->type); + QAction* act = _pup->findActionFromData(qVariantFromValue(*irl)); + if(act) + act->setChecked(true); + } + } +} + +void RoutePopupMenu::popupActivated(QAction* action) +{ + if(!action || !_track || !_pup || _pup->actions().isEmpty()) + return; + + if(_track->isMidiTrack()) + { + RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); + + // Take care of Route data items first... + if(qVariantCanConvert<Route>(action->data())) + { + Route aRoute = action->data().value<Route>(); + + // Support Midi Port to Audio Input track routes. + if(aRoute.type == Route::TRACK_ROUTE && aRoute.track && aRoute.track->type() == Track::AUDIO_INPUT) + { + //if(gIsOutRoutingPopupMenu) // Try to avoid splitting like this. + { + int chbit = aRoute.channel; + int port = ((MidiTrack*)_track)->outPort(); + if(port < 0 || port >= MIDI_PORTS) + return; + + MidiPort* mp = &midiPorts[port]; + //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; + + Route bRoute(port, chbit); + + int chmask = 0; + RouteList* mprl = _isOutMenu ? mp->outRoutes() : mp->inRoutes(); + ciRoute ir = mprl->begin(); + for (; ir != mprl->end(); ++ir) + { + if(ir->type == 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) + audio->msgRemoveRoute(bRoute, aRoute); + else + audio->msgRemoveRoute(aRoute, bRoute); + } + else + { + // connect + if(_isOutMenu) + audio->msgAddRoute(bRoute, aRoute); + else + audio->msgAddRoute(aRoute, bRoute); + } + + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + + } + return; + } + else if(aRoute.type == Route::MIDI_PORT_ROUTE) + { + int chbit = aRoute.channel; + Route bRoute(_track, chbit); + int mdidx = aRoute.midiPort; + + MidiPort* mp = &midiPorts[mdidx]; + 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; + ciRoute iir = rl->begin(); + for (; iir != rl->end(); ++iir) + { + if(iir->type == 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) + audio->msgRemoveRoute(bRoute, aRoute); + else + audio->msgRemoveRoute(aRoute, bRoute); + } + else + { + // connect + if(_isOutMenu) + audio->msgAddRoute(bRoute, aRoute); + else + audio->msgAddRoute(aRoute, bRoute); + } + + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + } + else + // ... now take care of integer data items. + if(qVariantCanConvert<int>(action->data())) + { + int n = action->data().value<int>(); + if(!_isOutMenu && n == 0) + muse->configMidiPorts(); + return; + } + } + else + { + AudioTrack* t = (AudioTrack*)_track; + RouteList* rl = _isOutMenu ? t->outRoutes() : t->inRoutes(); + + if(!qVariantCanConvert<Route>(action->data())) + return; + + if(_isOutMenu) + { + Route dstRoute = action->data().value<Route>(); + Route srcRoute(t, dstRoute.channel, dstRoute.channels); + srcRoute.remoteChannel = dstRoute.remoteChannel; + + // check if route src->dst exists: + ciRoute irl = rl->begin(); + for (; irl != rl->end(); ++irl) { + if (*irl == dstRoute) + break; + } + if (irl != rl->end()) { + // disconnect if route exists + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect if route does not exist + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + else + { + Route srcRoute = action->data().value<Route>(); + + // Support Midi Port to Audio Input routes. + if(_track->type() == Track::AUDIO_INPUT && srcRoute.type == Route::MIDI_PORT_ROUTE) + { + int chbit = srcRoute.channel; + Route dstRoute(t, chbit); + int mdidx = srcRoute.midiPort; + int chmask = 0; + ciRoute iir = rl->begin(); + for (; iir != rl->end(); ++iir) + { + if(iir->type == 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); + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else + { + //printf("routingPopupMenuActivated: adding src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel); + audio->msgAddRoute(srcRoute, dstRoute); + } + + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + return; + } + + Route dstRoute(t, srcRoute.channel, srcRoute.channels); + dstRoute.remoteChannel = srcRoute.remoteChannel; + + ciRoute irl = rl->begin(); + for (; irl != rl->end(); ++irl) { + if (*irl == srcRoute) + break; + } + if (irl != rl->end()) { + // disconnect + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + + + } + //else + //{ + //} +} + +void RoutePopupMenu::prepare() +{ + _pup->disconnect(); + _pup->clear(); + + if(!_track) + return; + + connect(_pup, SIGNAL(triggered(QAction*)), SLOT(popupActivated(QAction*))); + + if(_track->isMidiTrack()) + { + 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 = ((MidiTrack*)_track)->outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + MidiPort* mp = &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()) + { + RouteList* mprl = mp->outRoutes(); + int chbits = 1 << ((MidiTrack*)_track)->outChannel(); + //MidiDevice* md = mp->device(); + //if(!md) + // continue; + + _pup->addSeparator(); + _pup->addAction(new MenuTitleItem(tr("Soloing chain"), _pup)); + PopupMenu* subp = new PopupMenu(_pup, true); + subp->setTitle(tr("Audio returns")); + _pup->addMenu(subp); + + InputList* al = song->inputs(); + for (ciAudioInput i = al->begin(); i != al->end(); ++i) + { + Track* t = *i; + QString s(t->name()); + act = subp->addAction(s); + act->setCheckable(true); + Route r(t, chbits); + act->setData(qVariantFromValue(r)); + for(ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) + { + if(ir->type == 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) + { + MidiDevice* md = midiPorts[pi].device(); + if(md && !md->isSynti() && (md->rwFlags() & 2)) + break; + } + if(pi == MIDI_PORTS) + { + act = _pup->addAction(tr("Warning: No midi input devices!")); + act->setCheckable(false); + act->setData(-1); + _pup->addSeparator(); + } + act = _pup->addAction(QIcon(*settings_midiport_softsynthsIcon), tr("Open midi config...")); + act->setCheckable(false); + act->setData(gid); + _pup->addSeparator(); + ++gid; + + _pup->addAction(new MenuTitleItem("Midi input ports", _pup)); + + for(int i = 0; i < MIDI_PORTS; ++i) + { + // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick with ports. + MidiPort* mp = &midiPorts[i]; + MidiDevice* md = mp->device(); + //if(!md) + // continue; + + // Do not list synth devices! + if(md && md->isSynti()) + continue; + + if(md && !(md->rwFlags() & 2)) + 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. + ciRoute ir = rl->begin(); + for( ; ir != rl->end(); ++ir) + { + if(ir->type == 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; + + PopupMenu* subp = new PopupMenu(_pup, true); + subp->setTitle(QString("%1:").arg(i+1) + (md ? md->name() : tr("<none>"))); + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + act = subp->addAction(QString("Channel %1").arg(ch+1)); + act->setCheckable(true); + int chbit = 1 << ch; + 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); + Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. + act->setData(qVariantFromValue(togRoute)); + ++gid; + _pup->addMenu(subp); + } + + #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) + { + MidiPort* mp = &midiPorts[i]; + if(mp->device()) + continue; + + PopupMenu* subp = new PopupMenu(morep, true); + subp->setTitle(QString("%1:").arg(i) + tr("<none>")); + + // 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 == 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; + 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); + 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 + { + AudioTrack* t = (AudioTrack*)_track; + int channel = t->channels(); + if(_isOutMenu) + { + RouteList* orl = t->outRoutes(); + + QAction* act = 0; + int gid = 0; + gid = 0; + + switch(_track->type()) + { + case 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), _pup); + _pup->addAction(titel); + + if(!checkAudioDevice()) + { + _pup->clear(); + return; + } + std::list<QString> ol = audioDevice->inputPorts(); + for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + act = _pup->addAction(*ip); + act->setCheckable(true); + + Route dst(*ip, true, i, Route::JACK_ROUTE); + act->setData(qVariantFromValue(dst)); + ++gid; + for(ciRoute ir = orl->begin(); ir != orl->end(); ++ir) + { + if(*ir == dst) + { + act->setChecked(true); + break; + } + } + } + if(i+1 != channel) + _pup->addSeparator(); + } + + // + // Display using separate menu for audio inputs: + // + _pup->addSeparator(); + _pup->addAction(new MenuTitleItem(tr("Soloing chain"), _pup)); + PopupMenu* subp = new PopupMenu(_pup, true); + subp->setTitle(tr("Audio returns")); + _pup->addMenu(subp); + gid = addInPorts(t, subp, gid, -1, -1, true); + // + // Display all in the same menu: + // + //_pup->addSeparator(); + //MenuTitleItem* title = new MenuTitleItem(tr("Audio returns"), _pup); + //_pup->addAction(title); + //gid = addInPorts(t, _pup, gid, -1, -1, true); + } + break; + case Track::AUDIO_SOFTSYNTH: + gid = addMultiChannelPorts(t, _pup, gid, true); + break; + + case Track::AUDIO_INPUT: + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + gid = addWavePorts( t, _pup, gid, -1, -1, true); + gid = addOutPorts( t, _pup, gid, -1, -1, true); + gid = addGroupPorts( t, _pup, gid, -1, -1, true); + gid = nonSyntiTrackAddSyntis(t, _pup, gid, true); + break; + default: + _pup->clear(); + return; + } + } + else + { + if(_track->type() == Track::AUDIO_AUX) + return; + + RouteList* irl = t->inRoutes(); + + QAction* act = 0; + int gid = 0; + gid = 0; + + switch(_track->type()) + { + case 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), _pup); + _pup->addAction(titel); + + if(!checkAudioDevice()) + { + _pup->clear(); + return; + } + std::list<QString> ol = audioDevice->outputPorts(); + for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + act = _pup->addAction(*ip); + act->setCheckable(true); + + Route dst(*ip, true, i, Route::JACK_ROUTE); + act->setData(qVariantFromValue(dst)); + ++gid; + for(ciRoute ir = irl->begin(); ir != irl->end(); ++ir) + { + if(*ir == dst) + { + act->setChecked(true); + break; + } + } + } + if(i+1 != channel) + _pup->addSeparator(); + } + + // + // Display using separate menus for midi ports and audio outputs: + // + _pup->addSeparator(); + _pup->addAction(new MenuTitleItem(tr("Soloing chain"), _pup)); + PopupMenu* subp = new PopupMenu(_pup, true); + subp->setTitle(tr("Audio sends")); + _pup->addMenu(subp); + gid = addOutPorts(t, subp, gid, -1, -1, false); + subp = new PopupMenu(_pup, true); + subp->setTitle(tr("Midi port sends")); + _pup->addMenu(subp); + addMidiPorts(t, subp, gid, false); + // + // Display all in the same menu: + // + //_pup->addAction(new MenuTitleItem(tr("Audio sends"), _pup)); + //gid = addOutPorts(t, _pup, gid, -1, -1, false); + //_pup->addSeparator(); + //_pup->addAction(new MenuTitleItem(tr("Midi sends"), _pup)); + //addMidiPorts(t, _pup, gid, false); + } + break; + case Track::AUDIO_OUTPUT: + gid = addWavePorts( t, _pup, gid, -1, -1, false); + gid = addInPorts( t, _pup, gid, -1, -1, false); + gid = addGroupPorts(t, _pup, gid, -1, -1, false); + gid = addAuxPorts( t, _pup, gid, -1, -1, false); + gid = nonSyntiTrackAddSyntis(t, _pup, gid, false); + break; + case Track::WAVE: + gid = addWavePorts( t, _pup, gid, -1, -1, false); + gid = addInPorts( t, _pup, gid, -1, -1, false); + gid = addGroupPorts(t, _pup, gid, -1, -1, false); + gid = addAuxPorts( t, _pup, gid, -1, -1, false); + gid = nonSyntiTrackAddSyntis(t, _pup, gid, false); + break; + case Track::AUDIO_GROUP: + gid = addWavePorts( t, _pup, gid, -1, -1, false); + gid = addInPorts( t, _pup, gid, -1, -1, false); + gid = addGroupPorts(t, _pup, gid, -1, -1, false); + gid = addAuxPorts( t, _pup, gid, -1, -1, false); + gid = nonSyntiTrackAddSyntis(t, _pup, gid, false); + break; + + case Track::AUDIO_SOFTSYNTH: + gid = addMultiChannelPorts(t, _pup, gid, false); + break; + default: + _pup->clear(); + return; + } + } + } +} + +void RoutePopupMenu::exec(Track* track, bool isOutput) +{ + if(track) + { + _track = track; + _isOutMenu = isOutput; + } + prepare(); + _pup->exec(); +} + +void RoutePopupMenu::exec(const QPoint& p, Track* track, bool isOutput) +{ + if(track) + { + _track = track; + _isOutMenu = isOutput; + } + prepare(); + _pup->exec(p); +} + +void RoutePopupMenu::popup(const QPoint& p, Track* track, bool isOutput) +{ + if(track) + { + _track = track; + _isOutMenu = isOutput; + } + prepare(); + _pup->popup(p); +} + diff --git a/muse2/muse/widgets/routepopup.h b/muse2/muse/widgets/routepopup.h new file mode 100644 index 00000000..6772e8ca --- /dev/null +++ b/muse2/muse/widgets/routepopup.h @@ -0,0 +1,73 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// RoutePopupMenu.h +// (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 version 2. +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#ifndef __ROUTEPOPUPMENU_H__ +#define __ROUTEPOPUPMENU_H__ + +#include <QObject> + +class Track; +class AudioTrack; +class PopupMenu; +class QWidget; +class QString; +class QAction; +class QPoint; + +class RoutePopupMenu : public QObject +{ + Q_OBJECT + + PopupMenu* _pup; + Track* _track; + // Whether the route popup was shown by clicking the output routes button, or input routes button. + bool _isOutMenu; + + void init(); + void prepare(); + + int addMenuItem(AudioTrack* track, Track* route_track, PopupMenu* lb, int id, int channel, + int channels, bool isOutput); + int addAuxPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); + int addInPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); + int addOutPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); + int addGroupPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); + int addWavePorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); + int addSyntiPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); + int addMultiChannelPorts(AudioTrack* t, PopupMenu* pup, int id, bool isOutput); + int nonSyntiTrackAddSyntis(AudioTrack* t, PopupMenu* lb, int id, bool isOutput); + int addMidiPorts(AudioTrack* t, PopupMenu* pup, int id, bool isOutput); + + private slots: + void popupActivated(QAction*); + void songChanged(int); + + public: + RoutePopupMenu(QWidget* parent = 0, Track* track = 0, bool isOutput = false); + RoutePopupMenu(const QString& title, QWidget* parent = 0, Track* track = 0, bool isOutput = false); + ~RoutePopupMenu(); + + void updateRouteMenus(); + void exec(Track* track = 0, bool isOutput = false); + void exec(const QPoint& p, Track* track = 0, bool isOutput = false); + void popup(const QPoint& p, Track* track = 0, bool isOutput = false); +}; + +#endif diff --git a/muse2/muse/widgets/scrollscale.h b/muse2/muse/widgets/scrollscale.h index 32043070..67cfe573 100644 --- a/muse2/muse/widgets/scrollscale.h +++ b/muse2/muse/widgets/scrollscale.h @@ -21,6 +21,8 @@ class QToolButton; //--------------------------------------------------------- class ScrollScale : public QWidget { + Q_OBJECT + QSlider* scale; QScrollBar* scroll; int minVal, maxVal; @@ -38,8 +40,7 @@ class ScrollScale : public QWidget { double logbase; virtual void resizeEvent(QResizeEvent*); - Q_OBJECT - + private slots: void pageUp(); void pageDown(); diff --git a/muse2/muse/widgets/swidget.h b/muse2/muse/widgets/swidget.h index c5f4fd6a..45f8cad0 100644 --- a/muse2/muse/widgets/swidget.h +++ b/muse2/muse/widgets/swidget.h @@ -17,8 +17,9 @@ //--------------------------------------------------------- class SWidget : public QWidget { - virtual void resizeEvent(QResizeEvent*); Q_OBJECT + virtual void resizeEvent(QResizeEvent*); + signals: void heightChanged(int); diff --git a/muse2/muse/widgets/tb1.h b/muse2/muse/widgets/tb1.h index ff31593f..3e721e74 100644 --- a/muse2/muse/widgets/tb1.h +++ b/muse2/muse/widgets/tb1.h @@ -23,14 +23,15 @@ class LabelCombo; //--------------------------------------------------------- class Toolbar1 : public QToolBar { + Q_OBJECT + QToolButton* solo; PosLabel* pos; PitchLabel* pitch; LabelCombo* raster; QTableWidget* rlist; bool showPitch; - Q_OBJECT - + private slots: void _rasterChanged(int); diff --git a/muse2/muse/widgets/tempolabel.h b/muse2/muse/widgets/tempolabel.h index 71aeb4b8..69dc1450 100644 --- a/muse2/muse/widgets/tempolabel.h +++ b/muse2/muse/widgets/tempolabel.h @@ -16,9 +16,11 @@ //--------------------------------------------------------- class TempoLabel : public QLabel { + Q_OBJECT + double _value; - Q_OBJECT + protected: QSize sizeHint() const; diff --git a/muse2/muse/widgets/tools.h b/muse2/muse/widgets/tools.h index be479a50..2116b958 100644 --- a/muse2/muse/widgets/tools.h +++ b/muse2/muse/widgets/tools.h @@ -46,6 +46,7 @@ extern ToolB toolList[]; class EditToolBar : public QToolBar { Q_OBJECT + Action** actions; int nactions; diff --git a/muse2/muse/widgets/unusedwavefiles.h b/muse2/muse/widgets/unusedwavefiles.h index fd1f524c..e28754de 100644 --- a/muse2/muse/widgets/unusedwavefiles.h +++ b/muse2/muse/widgets/unusedwavefiles.h @@ -10,6 +10,7 @@ namespace Ui { class UnusedWaveFiles : public QDialog { Q_OBJECT + QStringList allWaveFiles; public: explicit UnusedWaveFiles(QWidget *parent = 0); diff --git a/muse2/muse/widgets/verticalmeter.h b/muse2/muse/widgets/verticalmeter.h index 699be1e5..facc8b8c 100644 --- a/muse2/muse/widgets/verticalmeter.h +++ b/muse2/muse/widgets/verticalmeter.h @@ -17,6 +17,8 @@ class QMouseEvent; class QPainter; class VerticalMeter : public Meter { + Q_OBJECT + private: MeterType mtype; bool overflow; @@ -27,7 +29,7 @@ class VerticalMeter : public Meter { void drawVU(QPainter& p, int, int, int); - Q_OBJECT + void paintEvent(QPaintEvent*); void resizeEvent(QResizeEvent*); diff --git a/muse2/muse/widgets/view.h b/muse2/muse/widgets/view.h index f8b0c90f..f53c4c72 100644 --- a/muse2/muse/widgets/view.h +++ b/muse2/muse/widgets/view.h @@ -24,12 +24,14 @@ class QResizeEvent; //--------------------------------------------------------- class View : public QWidget { + Q_OBJECT + QPixmap pm; // for double buffering bool pmValid; QPixmap bgPixmap; // background Pixmap QBrush brush; bool _virt; - Q_OBJECT + protected: int xorg; diff --git a/muse2/muse/widgets/visibletracks.cpp b/muse2/muse/widgets/visibletracks.cpp index e261c274..f6acf206 100644 --- a/muse2/muse/widgets/visibletracks.cpp +++ b/muse2/muse/widgets/visibletracks.cpp @@ -87,7 +87,7 @@ void VisibleTracks::updateVisibleTracksButtons() void VisibleTracks::visibilityChanged(QAction* action) { - printf("update visibility\n"); +// printf("update visibility\n"); switch (((Action*)action)->id()) { case 0: WaveTrack::setVisible(action->isChecked()); |