diff options
Diffstat (limited to 'muse2')
28 files changed, 807 insertions, 424 deletions
diff --git a/muse2/ChangeLog b/muse2/ChangeLog index c46b7f7f..b16462d8 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -30,6 +30,14 @@ - added "reorder list" function to drum roll - changed ctrl-edit's behaviour when control key is pressed +20.05.2011: + - Popup menus: If stay-open mode, space triggers item and double-click simulates return (closing). (Tim) +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 adac840a..f94f4a64 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -2297,7 +2297,7 @@ void MusE::showTransport(bool flag) PopupMenu* MusE::getRoutingPopupMenu() { if(!routingPopupMenu) - routingPopupMenu = new PopupMenu(this); + routingPopupMenu = new PopupMenu(this, true); return routingPopupMenu; } @@ -2878,7 +2878,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); @@ -2972,7 +2972,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("<none>"))); for(int ch = 0; ch < MIDI_CHANNELS; ++ch) @@ -3003,7 +3003,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) { @@ -3011,7 +3011,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("<none>")); // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! @@ -3074,297 +3074,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<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..2aa2834f 100644 --- a/muse2/muse/app.h +++ b/muse2/muse/app.h @@ -286,7 +286,7 @@ class MusE : public QMainWindow void startMidiTransformer(); void writeGlobalConfiguration() const; - void startEditInstrument(); + //void startEditInstrument(); void startClipList(bool); void openRecentMenu(); @@ -357,6 +357,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 6fea279a..2dd19b4d 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> @@ -45,6 +45,7 @@ #include "synth.h" #include "config.h" #include "scoreedit.h" +#include "popupmenu.h" #ifdef DSSI_SUPPORT #include "dssihost.h" @@ -896,9 +897,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); @@ -1099,7 +1102,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")); @@ -1116,12 +1119,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 7691b6cc..15be450a 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; @@ -86,7 +87,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/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/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 <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..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 <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/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/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/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("<Mono>", 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/song.cpp b/muse2/muse/song.cpp index 1c451f55..28ea1210 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -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/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/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..e6e618bd 100644 --- a/muse2/muse/widgets/mtrackinfo.cpp +++ b/muse2/muse/widgets/mtrackinfo.cpp @@ -1036,6 +1036,29 @@ void MidiTrackInfo::iPanChanged(int val) } //--------------------------------------------------------- +// 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..263c8475 100644 --- a/muse2/muse/widgets/popupmenu.cpp +++ b/muse2/muse/widgets/popupmenu.cpp @@ -6,25 +6,49 @@ // (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" + //====================== // 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 +56,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(); @@ -49,9 +90,14 @@ 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) +QAction* PopupMenu::findActionFromData(QVariant v) const { QList<QAction*> list = actions(); for(int i = 0; i < list.size(); ++i) @@ -69,8 +115,194 @@ QAction* PopupMenu::findActionFromData(QVariant v) return 0; } +bool PopupMenu::event(QEvent* event) +{ + //printf("PopupMenu::event type:%d\n", event->type()); // REMOVE Tim. + + switch(event->type()) + { + #ifndef POPUP_MENU_DISABLE_STAY_OPEN + case QEvent::MouseButtonDblClick: + { + if(_stayOpen) + { + 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) + { + 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()); // 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); + } + break; + #endif // POPUP_MENU_DISABLE_AUTO_SCROLL + + default: + break; + } + + 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 +338,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..47be57ae 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,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); + QAction* findActionFromData(QVariant) const; + bool stayOpen() const { return _stayOpen; } }; |