From 0f3ed66e83d6639452a5aa219b9a6bf2bfd54897 Mon Sep 17 00:00:00 2001 From: "Tim E. Real" Date: Thu, 19 May 2011 07:43:06 +0000 Subject: Popup menus now auto-scroll if too large to fit on desktop. Midi track info patch popup now stays open for auditioning patches. Editor 'ctrl' controller popups: Split instrument/other (+ common controls!) + show ctrl numbers. --- muse2/ChangeLog | 6 + muse2/muse/app.cpp | 301 +-------------------------------- muse2/muse/app.h | 3 +- muse2/muse/arranger/tlist.cpp | 18 +- muse2/muse/arranger/tlist.h | 6 +- muse2/muse/confmport.cpp | 4 +- muse2/muse/ctrl/ctrledit.cpp | 10 +- muse2/muse/ctrl/ctrlpanel.cpp | 265 ++++++++++++++++++++++++++++- muse2/muse/ctrl/ctrlpanel.h | 3 +- muse2/muse/dssihost.cpp | 6 +- muse2/muse/dssihost.h | 6 +- muse2/muse/instruments/minstrument.cpp | 9 +- muse2/muse/instruments/minstrument.h | 6 +- muse2/muse/liste/editevent.cpp | 27 ++- muse2/muse/midictrl.cpp | 78 ++++++++- muse2/muse/midictrl.h | 3 +- muse2/muse/mixer/astrip.cpp | 16 +- muse2/muse/synth.cpp | 5 +- muse2/muse/synth.h | 12 +- muse2/muse/ticksynth.cpp | 6 +- muse2/muse/vst.h | 6 +- muse2/muse/widgets/mtrackinfo.cpp | 46 ++++- muse2/muse/widgets/mtrackinfo.h | 1 + muse2/muse/widgets/popupmenu.cpp | 221 +++++++++++++++++++++++- muse2/muse/widgets/popupmenu.h | 30 +++- 25 files changed, 729 insertions(+), 365 deletions(-) diff --git a/muse2/ChangeLog b/muse2/ChangeLog index fce03eb0..956b0c3f 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,9 @@ +19.05.2011: + * Feature: Popup menus now auto-scroll if too large to fit on desktop. (p4.0.25 Tim) + Added auto-scroll to my class PopupMenu. Added selectable stay-open. TODO: Use it more, where needed. + - Changed midi track info patch popup behaviour. Now stays open for auditioning patches. (Tim) + - Changed editor 'ctrl' controller popups. Split instrument/other (+ common controls!) + show ctrl numbers. (Tim) + TODO: Add custom (R)NRPN dialog to 'other' section. 15.05.2011: - Changed mouse wheel behaviour in graphical editors except the score editor (rj) * wheel scrolls left-right diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index c42a1a7c..5b8da28e 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -2303,7 +2303,7 @@ void MusE::showTransport(bool flag) PopupMenu* MusE::getRoutingPopupMenu() { if(!routingPopupMenu) - routingPopupMenu = new PopupMenu(this); + routingPopupMenu = new PopupMenu(this, true); return routingPopupMenu; } @@ -2884,7 +2884,7 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) pup->addSeparator(); pup->addAction(new MenuTitleItem(tr("Soloing chain"), pup)); - PopupMenu* subp = new PopupMenu(pup); + PopupMenu* subp = new PopupMenu(pup, true); subp->setTitle(tr("Audio returns")); pup->addMenu(subp); @@ -2978,7 +2978,7 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) if(!md && ir == rl->end()) continue; - PopupMenu* subp = new PopupMenu(pup); + PopupMenu* subp = new PopupMenu(pup, true); subp->setTitle(QString("%1:").arg(i+1) + (md ? md->name() : tr(""))); for(int ch = 0; ch < MIDI_CHANNELS; ++ch) @@ -3009,7 +3009,7 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) #if 0 // p4.0.17 List ports with no device and no in routes, in a separate popup. - PopupMenu* morep = new PopupMenu(pup); + PopupMenu* morep = new PopupMenu(pup, true); morep->setTitle(tr("More...")); for(int i = 0; i < MIDI_PORTS; ++i) { @@ -3017,7 +3017,7 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) if(mp->device()) continue; - PopupMenu* subp = new PopupMenu(morep); + PopupMenu* subp = new PopupMenu(morep, true); subp->setTitle(QString("%1:").arg(i) + tr("")); // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! @@ -3080,297 +3080,6 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) 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 ol = audioDevice->outputPorts(); - for (std::list::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 e940c1ad..45b2efff 100644 --- a/muse2/muse/app.h +++ b/muse2/muse/app.h @@ -287,7 +287,7 @@ class MusE : public QMainWindow void startMidiTransformer(); void writeGlobalConfiguration() const; - void startEditInstrument(); + //void startEditInstrument(); void startClipList(bool); void openRecentMenu(); @@ -359,6 +359,7 @@ class MusE : public QMainWindow void importMidi(const QString &file); void setUsedTool(int); void showDidYouKnowDialog(); + void startEditInstrument(); void routingPopupMenuAboutToHide(); void configMidiPorts(); diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index b907b555..ccd213da 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -11,7 +11,7 @@ #include #include -#include +//#include #include #include #include @@ -44,6 +44,7 @@ #include "midiedit/drummap.h" #include "synth.h" #include "config.h" +#include "popupmenu.h" #ifdef DSSI_SUPPORT #include "dssihost.h" @@ -348,7 +349,7 @@ void TList::paint(const QRect& r) if (cl->isVisible()) countVisible++; } - int count = ((AudioTrack*)track)->controller()->size(); + //int count = ((AudioTrack*)track)->controller()->size(); s.sprintf(" %d(%d) visible",countVisible, countAll); } @@ -883,9 +884,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); @@ -1058,7 +1061,7 @@ void TList::mousePressEvent(QMouseEvent* ev) { 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 +1078,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; diff --git a/muse2/muse/arranger/tlist.h b/muse2/muse/arranger/tlist.h index 8bebef95..4d787030 100644 --- a/muse2/muse/arranger/tlist.h +++ b/muse2/muse/arranger/tlist.h @@ -19,7 +19,8 @@ class QPaintEvent; class QResizeEvent; class QScrollBar; class QWheelEvent; -class QMenu; +//class QMenu; +class PopupMenu; class ScrollScale; class Track; @@ -85,7 +86,8 @@ 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(); diff --git a/muse2/muse/confmport.cpp b/muse2/muse/confmport.cpp index 8c27d9eb..fc005923 100644 --- a/muse2/muse/confmport.cpp +++ b/muse2/muse/confmport.cpp @@ -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/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/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 +//#include +#include #include #include #include @@ -20,10 +21,12 @@ #include +#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 sList; + typedef std::list::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..92911b8e 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,7 @@ class MidiTrack; //--------------------------------------------------------- class CtrlPanel: public QWidget { - ///QMenu* pop; + //QMenu* pop; QPushButton* selCtrl; MidiEditor* editor; 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 #include -#include +//#include #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 +//#include +#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/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 #include #include -#include +//#include #include #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 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..0643c34d 100644 --- a/muse2/muse/liste/editevent.cpp +++ b/muse2/muse/liste/editevent.cpp @@ -36,6 +36,7 @@ #include "midiedit/drummap.h" #include "instruments/minstrument.h" #include "midi.h" +#include "popupmenu.h" //--------------------------------------------------------- // string2qhex @@ -715,7 +716,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,14 +726,21 @@ 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 +854,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/midictrl.cpp b/muse2/muse/midictrl.cpp index 66f8d87e..4cf1886a 100644 --- a/muse2/muse/midictrl.cpp +++ b/muse2/muse/midictrl.cpp @@ -138,16 +138,86 @@ void initMidiController() defaultMidiController.add(&panCtrl); } +//--------------------------------------------------------- +// 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/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index 265061ad..c1e92e59 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -1185,7 +1185,7 @@ static int addSyntiPorts(AudioTrack* t, PopupMenu* lb, int id, if(chans > 0) { - PopupMenu* chpup = new PopupMenu(lb); + PopupMenu* chpup = new PopupMenu(lb, true); chpup->setTitle(track->name()); for(int ch = 0; ch < chans; ++ch) { @@ -1270,7 +1270,7 @@ static int addMultiChannelPorts(AudioTrack* t, PopupMenu* pup, int id, RouteMenu { // If more than one channel, create the sub-menu. if(chans > 1) - chpup = new PopupMenu(pup); + chpup = new PopupMenu(pup, true); if(isOutput) { @@ -1356,7 +1356,7 @@ static int addMultiChannelPorts(AudioTrack* t, PopupMenu* pup, int id, RouteMenu { // If more than two channels, create the sub-menu. if(chans > 2) - chpup = new PopupMenu(pup); + chpup = new PopupMenu(pup, true); if(isOutput) { @@ -1460,7 +1460,7 @@ static int nonSyntiTrackAddSyntis(AudioTrack* t, PopupMenu* lb, int id, RouteMen if(chans > 0) { - PopupMenu* chpup = new PopupMenu(lb); + PopupMenu* chpup = new PopupMenu(lb, true); chpup->setTitle(track->name()); if(chans > 1) chpup->addAction(new MenuTitleItem("", chpup)); @@ -1597,7 +1597,7 @@ static int addMidiPorts(AudioTrack* t, PopupMenu* pup, int id, RouteMenuMap& mm, RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); - PopupMenu* subp = new PopupMenu(pup); + PopupMenu* subp = new PopupMenu(pup, true); subp->setTitle(md->name()); int chanmask = 0; @@ -1830,11 +1830,11 @@ void AudioStrip::iRoutePressed() // pup->addSeparator(); pup->addAction(new MenuTitleItem(tr("Soloing chain"), pup)); - PopupMenu* subp = new PopupMenu(pup); + PopupMenu* subp = new PopupMenu(pup, true); subp->setTitle(tr("Audio sends")); pup->addMenu(subp); gid = addOutPorts(t, subp, gid, gRoutingMenuMap, -1, -1, false); - subp = new PopupMenu(pup); + subp = new PopupMenu(pup, true); subp->setTitle(tr("Midi port sends")); pup->addMenu(subp); addMidiPorts(t, subp, gid, gRoutingMenuMap, false); @@ -1980,7 +1980,7 @@ void AudioStrip::oRoutePressed() // pup->addSeparator(); pup->addAction(new MenuTitleItem(tr("Soloing chain"), pup)); - PopupMenu* subp = new PopupMenu(pup); + PopupMenu* subp = new PopupMenu(pup, true); subp->setTitle(tr("Audio returns")); pup->addMenu(subp); gid = addInPorts(t, subp, gid, gRoutingMenuMap, -1, -1, true); 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 #include -#include +//#include #include "app.h" #include "synth.h" @@ -35,6 +35,7 @@ #include "midiseq.h" #include "midictrl.h" //#include "stringparam.h" +#include "popupmenu.h" std::vector 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 -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 +//#include +#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/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/widgets/mtrackinfo.cpp b/muse2/muse/widgets/mtrackinfo.cpp index acdfb42f..47576823 100644 --- a/muse2/muse/widgets/mtrackinfo.cpp +++ b/muse2/muse/widgets/mtrackinfo.cpp @@ -1035,6 +1035,29 @@ void MidiTrackInfo::iPanChanged(int val) song->update(SC_MIDI_CONTROLLER); } +//--------------------------------------------------------- +// instrPopupActivated +//--------------------------------------------------------- + +void MidiTrackInfo::instrPopupActivated(QAction* act) +{ + printf("MidiTrackInfo::instrPopupActivated\n"); // REMOVE Tim + + 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,7 +1070,9 @@ void MidiTrackInfo::instrPopup() int channel = track->outChannel(); int port = track->outPort(); MidiInstrument* instr = midiPorts[port].instrument(); - QMenu* pup = new QMenu; + //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); @@ -1059,14 +1084,21 @@ void MidiTrackInfo::instrPopup() return; } + 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; } diff --git a/muse2/muse/widgets/mtrackinfo.h b/muse2/muse/widgets/mtrackinfo.h index 4e06f1d0..0e559f33 100644 --- a/muse2/muse/widgets/mtrackinfo.h +++ b/muse2/muse/widgets/mtrackinfo.h @@ -48,6 +48,7 @@ class MidiTrackInfo : public QWidget, public Ui::MidiTrackInfoBase void outRoutesPressed(); void routingPopupMenuActivated(QAction*); //void routingPopupViewActivated(const QModelIndex&); + void instrPopupActivated(QAction*); protected slots: virtual void heartBeat(); diff --git a/muse2/muse/widgets/popupmenu.cpp b/muse2/muse/widgets/popupmenu.cpp index 862bda91..b59e8d43 100644 --- a/muse2/muse/widgets/popupmenu.cpp +++ b/muse2/muse/widgets/popupmenu.cpp @@ -10,21 +10,44 @@ //#include #include +#include #include +#include +#include +#include +//#include + #include //#include #include "popupmenu.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 +55,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 list = actions(); @@ -49,6 +89,11 @@ 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) @@ -69,8 +114,175 @@ QAction* PopupMenu::findActionFromData(QVariant v) return 0; } +bool PopupMenu::event(QEvent* event) +{ + //printf("PopupMenu::event type:%d\n", event->type()); // REMOVE Tim. + + #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + if(event->type() == QEvent::MouseMove) + { + QMouseEvent* e = static_cast(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()); // REMOVE Tim. + + /* + //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()); // REMOVE Tim. + + //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); + } + #endif // POPUP_MENU_DISABLE_AUTO_SCROLL + /* + else + if(event->type() == QEvent::HoverEnter) + { + // Nope! Hovering over menu items did not invoke this. + printf("PopupMenu::event hover\n"); // REMOVE Tim. + QHoverEvent* he = static_cast(event); + QPoint oldPos = he->oldPos(); + QPoint pos = he->pos(); + + QAction* action = actionAt(pos); + + if(action) + { + QRect r = actionGeometry(action); + printf("PopupMenu::event hover: 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()); // REMOVE Tim. + printf(" pos x:%d y:%d oldPos px:%d py:%d\n", + pos.x(), pos.y(), oldPos.x(), oldPos.y()); // REMOVE Tim. + + + } + + + //return true; + } + */ + + return QMenu::event(event); +} + +#ifndef POPUP_MENU_DISABLE_AUTO_SCROLL +void PopupMenu::timerHandler() +{ + // printf("PopupMenu::timerHandler\n"); // REMOVE Tim. + + //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. + + 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()); // REMOVE Tim. + //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); // REMOVE Tim. + //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(); + +} +#endif // POPUP_MENU_DISABLE_AUTO_SCROLL + void PopupMenu::mouseReleaseEvent(QMouseEvent *e) { + #ifdef POPUP_MENU_DISABLE_STAY_OPEN + QMenu::mouseReleaseEvent(e); + return; + + #else + if(!_stayOpen) + { + QMenu::mouseReleaseEvent(e); + return; + } + + //printf("PopupMenu::mouseReleaseEvent\n"); // REMOVE Tim. + //Q_D(QMenu); //if (d->mouseEventTaken(e)) // return; @@ -106,6 +318,7 @@ void PopupMenu::mouseReleaseEvent(QMouseEvent *e) // 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..4982d199 100644 --- a/muse2/muse/widgets/popupmenu.h +++ b/muse2/muse/widgets/popupmenu.h @@ -11,7 +11,15 @@ #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 +#ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + #include +#endif + //#include //#include @@ -19,20 +27,40 @@ 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); + bool stayOpen() { return _stayOpen; } }; -- cgit v1.2.3