1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //    $Id: tlist.cpp,v 1.31.2.31 2009/12/15 03:39:58 terminator356 Exp $
5 //  (C) Copyright 1999 Werner Schweer (ws@seh.de)
6 //  (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge)
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; version 2 of
11 //  the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 //=========================================================
23 
24 #include "muse_math.h"
25 
26 #include <QAction>
27 #include <QActionGroup>
28 #include <QMessageBox>
29 #include <QPainter>
30 #include <QPixmap>
31 #include <QResizeEvent>
32 #include <QIcon>
33 #include <QToolTip>
34 #include <QList>
35 #include <QColorDialog>
36 
37 #include "popupmenu.h"
38 #include "globals.h"
39 #include "icons.h"
40 #include "tlist.h"
41 #include "mididev.h"
42 #include "midiport.h"
43 #include "midictrl.h"
44 #include "midiseq.h"
45 #include "comment.h"
46 #include "track.h"
47 #include "song.h"
48 #include "header.h"
49 #include "audio.h"
50 #include "instruments/minstrument.h"
51 #include "app.h"
52 #include "helper.h"
53 #include "gconfig.h"
54 #include "event.h"
55 #include "midiedit/drummap.h"
56 #include "synth.h"
57 #include "config.h"
58 #include "filedialog.h"
59 #include "menutitleitem.h"
60 #include "arranger.h"
61 #include "midi_audio_control.h"
62 #include "ctrl.h"
63 #include "plugin.h"
64 #include "operations.h"
65 #include "shortcuts.h"
66 #include "drumedit.h"
67 #include "utils.h"
68 
69 
70 #ifdef DSSI_SUPPORT
71 #include "dssihost.h"
72 #endif
73 
74 #ifdef LV2_SUPPORT
75 #include "lv2host.h"
76 #endif
77 
78 // Forwards from header:
79 #include <QKeyEvent>
80 #include <QLineEdit>
81 #include <QSpinBox>
82 #include <QMouseEvent>
83 #include <QPaintEvent>
84 #include <QScrollBar>
85 #include <QWheelEvent>
86 #include <QMenu>
87 #include "xml.h"
88 #include "undo.h"
89 #include "header.h"
90 #include "popupmenu.h"
91 #include "scrollscale.h"
92 
93 using MusECore::UndoOp;
94 
95 namespace MusEGui {
96 
97 static const int MIN_TRACKHEIGHT = 20;
98 //static const int WHEEL_DELTA = 120;
99 QColor collist[] = { Qt::red, Qt::yellow, Qt::blue , Qt::black, Qt::white, Qt::green };
100 QString colnames[] = { "Red", "Yellow", "Blue", "Black", "White", "Green"};
101 enum { AUTO_INVALID = -1, AUTO_SHOW_ALL = 251, AUTO_HIDE_ALL = 252, AUTO_CLEAR_AUTO = 253, AUTO_CLEAR_MIDI = 254, AUTO_MIDI_ASSIGN = 255 };
102 
103 //---------------------------------------------------------
104 //   TList
105 //---------------------------------------------------------
106 
TList(Header * hdr,QWidget * parent,const char * name)107 TList::TList(Header* hdr, QWidget* parent, const char* name)
108     : QWidget(parent) // Qt::WNoAutoErase | Qt::WResizeNoErase are no longer needed according to Qt4 doc
109 {
110     setBackgroundRole(QPalette::NoRole);
111     setAttribute(Qt::WA_NoSystemBackground);
112     setAttribute(Qt::WA_StaticContents);
113     // This is absolutely required for speed! Otherwise painfully slow because we get
114     //  full rect paint events even on small scrolls! See help on QPainter::scroll().
115     setAttribute(Qt::WA_OpaquePaintEvent);
116 
117     setStatusTip(tr("Track list: LMB to select track, CTRL+LMB to add to selection, SHIFT+LMB for range select. RMB in empty area to create tracks. Press F1 for help."));
118 
119     setObjectName(name);
120     ypos = 0;
121     editMode = false;
122     editJustFinished=false;
123     setFocusPolicy(Qt::ClickFocus);
124     setMouseTracking(true);
125     header    = hdr;
126 
127     _scroll   = nullptr;
128     editTrack = nullptr;
129     editor    = nullptr;
130     chan_edit = nullptr;
131     ctrl_edit = nullptr;
132     mode      = NORMAL;
133 
134     _sel3d = true;
135     _curSelBorder = false;
136     _curSelBorderColor = Qt::red;
137 
138     //setBackgroundMode(Qt::NoBackground); // ORCAN - FIXME. DELETETHIS?
139     //setAttribute(Qt::WA_OpaquePaintEvent);
140     resizeFlag = false;
141 
142     connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t)));
143     connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(redraw()));
144     connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(maybeUpdateVolatileCustomColumns()));
145 }
146 
147 //---------------------------------------------------------
148 //   songChanged
149 //---------------------------------------------------------
150 
songChanged(MusECore::SongChangedStruct_t flags)151 void TList::songChanged(MusECore::SongChangedStruct_t flags)
152 {
153     if (flags & (SC_MUTE | SC_SOLO | SC_RECFLAG | SC_TRACK_REC_MONITOR
154                  | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED
155                  | SC_TRACK_MOVED
156                  | SC_TRACK_SELECTION | SC_ROUTE | SC_CHANNELS
157                  | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED
158                  | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED ))
159         update();
160     if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED))
161         adjustScrollbar();
162 }
163 
164 //---------------------------------------------------------
165 //   drawCenteredPixmap
166 //    small helper function for "draw()" below
167 //---------------------------------------------------------
168 
169 //static void drawCenteredPixmap(QPainter& p, const QPixmap* pm, const QRect& r)
170 //{
171 //    p.drawPixmap(r.x() + (r.width() - pm->width())/2, r.y() + (r.height() - pm->height())/2, *pm);
172 //}
173 
174 //---------------------------------------------------------
175 //   paintEvent
176 //---------------------------------------------------------
177 
paintEvent(QPaintEvent * ev)178 void TList::paintEvent(QPaintEvent* ev)
179 {
180     paint(ev->rect());
181 }
182 
183 //---------------------------------------------------------
184 //   redraw
185 //---------------------------------------------------------
186 
redraw()187 void TList::redraw()
188 {
189     update();
190 }
191 
192 //---------------------------------------------------------
193 //   redraw
194 //---------------------------------------------------------
195 
redraw(const QRect & r)196 void TList::redraw(const QRect& r)
197 {
198     update(r);
199 }
200 
201 
202 //---------------------------------------------------------
203 //   event
204 //---------------------------------------------------------
205 
event(QEvent * event)206 bool TList::event(QEvent *event)
207 {
208     if (event->type() == QEvent::ToolTip) {
209         QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
210         MusECore::TrackList* l = MusEGlobal::song->tracks();
211         int idx = 0;
212         int yy  = -ypos;
213         for (MusECore::iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i) {
214             MusECore::Track* track = *i;
215             MusECore::Track::TrackType type = track->type();
216             int trackHeight = track->height();
217             if (trackHeight==0) // not visible
218                 continue;
219             if (helpEvent->pos().y() > yy && helpEvent->pos().y() < yy + trackHeight) {
220                 if (type == MusECore::Track::AUDIO_SOFTSYNTH) {
221                     MusECore::SynthI *s = (MusECore::SynthI*)track;
222                     QToolTip::showText(helpEvent->globalPos(),track->name() + QString(" : ") +
223                                        (s->synth() ? s->synth()->description() : QString(tr("SYNTH IS UNAVAILABLE!"))) +
224                                        (s->synth() ? (s->synth()->uri().isEmpty() ? QString() :
225                                                                                     QString(" \n") + s->synth()->uri()) :
226                                                      (s->initConfig()._uri.isEmpty() ? QString() :
227                                                                                        QString(" \n") + s->initConfig()._uri)));
228                 }
229                 else
230                     QToolTip::showText(helpEvent->globalPos(),track->name());
231             }
232         }
233         return true;
234     }
235     return QWidget::event(event);
236 }
237 
238 //---------------------------------------------------------
239 //   paint
240 //---------------------------------------------------------
241 
paint(const QRect & r)242 void TList::paint(const QRect& r)
243 {
244     if (!isVisible())
245         return;
246     QRect rect(r);
247     QPainter p(this);
248 
249     if (bgPixmap.isNull())
250         p.fillRect(rect, MusEGlobal::config.trackBg);
251     else
252         p.drawTiledPixmap(rect, bgPixmap, QPoint(rect.x(), ypos + rect.y()));
253     p.setClipRegion(rect);
254 
255     //printf("TList::paint hasClipping:%d\n", p.hasClipping());   // Tested true.
256 
257     int y  = rect.y();
258     int w  = rect.width();
259     int h  = rect.height();
260     int x1 = rect.x();
261     int x2 = rect.x() + w;
262 
263     QFont fit(font());
264     fit.setItalic(true);
265 
266     QFont fbd(font());
267     fbd.setBold(true);
268 
269 
270     //---------------------------------------------------
271     //    Tracks
272     //---------------------------------------------------
273 
274     QColor mask_edge = QColor(90, 90, 90, 45);
275     QColor mask_center = QColor(240, 240, 240, 175);
276     QLinearGradient mask;
277     mask.setColorAt(0, mask_edge);
278     mask.setColorAt(0.15, mask_center);
279     mask.setColorAt(0.3, mask_center);
280     mask.setColorAt(0.85, mask_edge);
281     mask.setColorAt(1, mask_edge);
282 
283     // Find up to two tracks that are soloed.
284     MusECore::Track* solo_t_1 = nullptr;
285     MusECore::Track* solo_t_2 = nullptr;
286     {
287         MusECore::TrackList* tl = MusEGlobal::song->tracks();
288         for(MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it)
289         {
290             MusECore::Track* t = *it;
291             if(t->internalSolo() || t->solo())
292             {
293                 if(!solo_t_1)
294                     solo_t_1 = t;
295                 else if(!solo_t_2)
296                     solo_t_2 = t;
297             }
298             // Did we find at least two tracks? Done.
299             if(solo_t_1 && solo_t_2)
300                 break;
301         }
302     }
303 
304     //      const int header_fh = header->fontMetrics().lineSpacing();
305     //      const int svg_sz = qMin(header_fh + 2, qMax(MIN_TRACKHEIGHT - 5, 6)); // ???
306     const int svg_sz = MIN_TRACKHEIGHT - 4; // 2px margin
307 
308     MusECore::TrackList* l = MusEGlobal::song->tracks();
309     const MusECore::Track* cur_sel_track = l->currentSelection();
310     int idx = 0;
311     int yy  = -ypos;
312     for (MusECore::iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i) {
313         MusECore::Track* track = *i;
314         MusECore::Track::TrackType type = track->type();
315         int trackHeight = track->height();
316         if (trackHeight==0) // not visible
317             continue;
318         if (yy >= (y + h))
319             break;
320         if ((yy + trackHeight) < y)
321             continue;
322         //
323         // clear one row
324         //
325         QColor bg;
326         if (track->selected()) {
327             if (track == cur_sel_track)
328                 bg = MusEGlobal::config.selectTrackCurBg;
329             else
330                 bg = MusEGlobal::config.selectTrackBg;
331             p.setPen(MusEGlobal::config.selectTrackFg);
332         }
333         else {
334             bg = track->color();
335             if (MusECore::isColorBright(bg))
336                 p.setPen(Qt::black);
337             else
338                 p.setPen(Qt::white);
339         }
340 
341         p.fillRect(x1, yy, w, trackHeight,
342                    MusECore::getGradientFromColor(bg, QPoint(x1,yy), QPoint(x1, yy+trackHeight),
343                                 qBound(0, MusEGlobal::config.trackGradientStrength, 100)));
344 
345         if (track->selected() && _sel3d) {
346             mask.setStart(QPointF(0, yy));
347             mask.setFinalStop(QPointF(0, yy + trackHeight));
348             p.fillRect(x1, yy, w, trackHeight, mask);
349         }
350 
351         if (track == cur_sel_track && _curSelBorder) {
352             p.save();
353             p.setPen(_curSelBorderColor);
354             p.setClipRect(x1 - 1, yy, w + 2, trackHeight);
355             p.drawRect(0, yy + 1, header->length() - 2, trackHeight - 2);
356             p.setClipping(false);
357             p.restore();
358         }
359 
360         int x = -1; // compensate for the buggy QHeaderView, would even have to be -2 for perfect alignment
361         for (int index = 0; index < header->count(); ++index) {
362             int section = header->logicalIndex(index);
363             int w   = header->sectionSize(section);
364             QRect r = p.transform().mapRect(QRect(x + 2, yy, w - 4, trackHeight));
365             QRect svg_r = p.transform().mapRect(
366                         QRect(x + w / 2 - svg_sz / 2,
367                               yy + trackHeight / 2 - svg_sz / 2,
368                               svg_sz,
369                               svg_sz));
370 
371             if(!header->isSectionHidden(section))
372             {
373                 switch (section) {
374                 case COL_TRACK_IDX:
375                     if (track->selected())
376                         p.setFont(fbd);
377                     p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, QString::number(MusEGlobal::song->tracks()->index(track) + 1));
378                     if (p.font().bold())
379                         p.setFont(font());
380                     break;
381 
382                 case COL_INPUT_MONITOR:
383                     if (track->canRecordMonitor()) {
384                         (track->recMonitor() ? monitorOnSVGIcon : monitorOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
385                     }
386                     break;
387 
388                 case COL_RECORD:
389                     if (track->canRecord()) {
390                         if (track->type() == MusECore::Track::AUDIO_OUTPUT)
391                             (track->recordFlag() ? downmixOnSVGIcon : downmixOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
392                         else
393                             (track->recordFlag() ? recArmOnSVGIcon : recArmOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
394                     }
395                     break;
396 
397                 case COL_CLASS:
398                 {
399                     if(const QIcon* icon = MusECore::Track::trackTypeIcon(type))
400                         icon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
401                 }
402                     break;
403 
404                 case COL_MUTE:
405                     if (track->off())
406                         trackOffSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
407                     else
408                     {
409                         if(!track->internalSolo() && !track->solo() &&
410                                 ((solo_t_1 && solo_t_1 != track) || (solo_t_2 && solo_t_2 != track)))
411                             (track->mute() ? muteAndProxyOnSVGIcon : muteProxyOnSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
412                         else if(track->mute())
413                             muteOnSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
414                     }
415                     break;
416 
417                 case COL_SOLO:
418                     if(track->solo() && track->internalSolo())
419                         soloAndProxyOnSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
420                     else
421                         if(track->internalSolo())
422                             soloProxyOnAloneSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
423                         else
424                             if (track->solo())
425                                 soloOnAloneSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On);
426                     break;
427 
428 //                case COL_TIMELOCK: // unused
429 //                    if (track->isMidiTrack() && track->locked()) {
430 //                        drawCenteredPixmap(p, lockIcon, r);
431 //                    }
432 //                    break;
433 
434                 case COL_NAME:
435                     if (track->selected())
436                         p.setFont(fbd);
437                     p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, track->name());
438                     if (p.font().bold())
439                         p.setFont(font());
440                     break;
441 
442                 case COL_OCHANNEL:
443                 {
444                     QString s;
445                     int n;
446                     // Default to track port if -1 and track channel if -1.
447                     if (track->isMidiTrack()) {
448                         n = ((MusECore::MidiTrack*)track)->outChannel() + 1;
449                     }
450                     else {
451                         // show number of ports
452                         n = ((MusECore::WaveTrack*)track)->channels();
453                     }
454                     s.setNum(n);
455                     p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, s);
456                 }
457                     break;
458 
459                 case COL_OPORT:
460                 {
461                     QString s;
462                     if (track->isMidiTrack()) {
463                         int outport = ((MusECore::MidiTrack*)track)->outPort();
464                         s = QString("%1:%2").arg(outport+1).arg(MusEGlobal::midiPorts[outport].portname());
465                     }
466                     else if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH)
467                     {
468                         MusECore::MidiDevice* md = dynamic_cast<MusECore::MidiDevice*>(track);
469                         if(md)
470                         {
471                             int outport = md->midiPort();
472                             if((outport >= 0) && (outport < MusECore::MIDI_PORTS))
473                                 s = QString("%1:%2").arg(outport+1).arg(MusEGlobal::midiPorts[outport].portname());
474                             else
475                                 s = tr("<none>");
476                         }
477                     }
478                     else
479                     {
480                         p.setFont(fit);
481                         s = "n/a";
482                     }
483 
484                     p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s);
485                     if (p.font().italic())
486                         p.setFont(font());
487                 }
488                     break;
489 
490                 case COL_AUTOMATION:
491                 {
492                     QString s;
493 
494                     if (!track->isMidiTrack()) {
495                         MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)track)->controller();
496                         int countAll=0, countVisible=0;
497                         for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) {
498                             MusECore::CtrlList *cl = icll->second;
499                             if (!cl->dontShow())
500                                 countAll++;
501                             if (cl->isVisible())
502                                 countVisible++;
503                         }
504                         s = QString(" %1(%2) %3").arg(countVisible).arg(countAll).arg(tr("visible"));
505                     } else {
506                         p.setFont(fit);
507                         s = "n/a";
508                     }
509 
510                     p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s);
511                     if (p.font().italic())
512                         p.setFont(font());
513                 }
514                     break;
515 
516                 case COL_CLEF:
517                 {
518                     QString s;
519                     if (track->isMidiTrack() && track->type() == MusECore::Track::MIDI) { // no drum tracks!
520                         if (((MusECore::MidiTrack*)track)->getClef() == trebleClef)
521                             s=tr("Treble");
522                         else if (((MusECore::MidiTrack*)track)->getClef() == bassClef)
523                             s=tr("Bass");
524                         else if (((MusECore::MidiTrack*)track)->getClef() == grandStaff)
525                             s=tr("Grand");
526                     } else {
527                         p.setFont(fit);
528                         s = "n/a";
529                     }
530 
531                     p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s);
532                     if (p.font().italic())
533                         p.setFont(font());
534                 }
535                     break;
536 
537                 default:
538                     if (section >= COL_CUSTOM_MIDICTRL_OFFSET)
539                     {
540                         if (track->isMidiTrack())
541                         {
542                             int col_ctrl_no=Arranger::custom_columns[section - COL_CUSTOM_MIDICTRL_OFFSET].ctrl;
543                             MusECore::MidiTrack* mt=dynamic_cast<MusECore::MidiTrack*>(track);
544                             MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()];
545                             const int chan = mt->outChannel();
546                             MusECore::MidiController* mctl = mp->midiController(col_ctrl_no, chan);
547                             int val;
548                             if (Arranger::custom_columns[section - COL_CUSTOM_MIDICTRL_OFFSET].affected_pos ==
549                                     Arranger::custom_col_t::AFFECT_BEGIN)
550                                 val=mt->getControllerChangeAtTick(0,col_ctrl_no,MusECore::CTRL_VAL_UNKNOWN);
551                             else
552                             {
553                                 val=mp->hwCtrlState(mt->outChannel(), col_ctrl_no);
554                                 old_ctrl_hw_states[mt][section]=val;
555                             }
556 
557                             if (val!=MusECore::CTRL_VAL_UNKNOWN)
558                                 val-=mctl->bias();
559 
560                             if (col_ctrl_no!=MusECore::CTRL_PROGRAM)
561                             {
562                                 p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter,
563                                            (val!=MusECore::CTRL_VAL_UNKNOWN)?QString::number(val):tr("off"));
564                             }
565                             else
566                             {
567                                 MusECore::MidiInstrument* instr = mp->instrument();
568                                 QString name;
569                                 if (val!=MusECore::CTRL_VAL_UNKNOWN)
570                                     name = instr->getPatchName(mt->outChannel(), val, mt->isDrumTrack(), true); // Include default.
571                                 else
572                                     name = tr("<unknown>");
573 
574                                 p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, name);
575                             }
576                         }
577                     }
578                     break;
579                 }
580             }
581             x += header->sectionSize(section);
582         }
583         p.setPen(MusEGlobal::config.trackSectionDividerColor);
584         p.drawLine(x1, yy, x2, yy);
585     }
586     p.drawLine(x1, yy, x2, yy);
587 
588     if (mode == DRAG) {
589         int yy = curY - dragYoff;
590         p.setPen(Qt::green);
591         p.drawLine(x1, yy, x2, yy);
592         p.drawLine(x1, yy + dragHeight, x2, yy+dragHeight);
593     }
594 
595     //---------------------------------------------------
596     //    draw vertical lines
597     //---------------------------------------------------
598 
599     int n = header->count();
600     int xpos = -1;
601     p.setPen(MusEGlobal::config.trackSectionDividerColor);
602     for (int index = 0; index < n; index++) {
603         int section = header->logicalIndex(index);
604         xpos += header->sectionSize(section);
605         p.drawLine(xpos, 0, xpos, height());
606     }
607 }
608 
maybeUpdateVolatileCustomColumns()609 void TList::maybeUpdateVolatileCustomColumns()
610 {
611     MusECore::TrackList* l = MusEGlobal::song->tracks();
612     int idx = 0;
613     int yy  = -ypos;
614     for (MusECore::iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i)
615     {
616         MusECore::Track* track = *i;
617         int trackHeight = track->height();
618         if (trackHeight==0) // not visible
619             continue;
620 
621 
622         int x = 0;
623         for (int index = 0; index < header->count(); ++index)
624         {
625             int section = header->logicalIndex(index);
626 
627             if (section>=COL_CUSTOM_MIDICTRL_OFFSET && track->isMidiTrack() &&
628                     (Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos ==
629                      Arranger::custom_col_t::AFFECT_CPOS) )
630             {
631                 int w   = header->sectionSize(section);
632                 QRect r = QRect(x+2, yy, w-4, trackHeight);
633 
634                 int ctrl_no = Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].ctrl;
635 
636                 MusECore::MidiTrack* mt=(MusECore::MidiTrack*)track;
637                 MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()];
638                 int new_val = mp->hwCtrlState(mt->outChannel(), ctrl_no);
639 
640                 if (new_val != old_ctrl_hw_states[track][section])
641                     update(r);
642             }
643 
644             x += header->sectionSize(section);
645         }
646     }
647 }
648 
649 //---------------------------------------------------------
650 //   returnPressed
651 //---------------------------------------------------------
652 
returnPressed()653 void TList::returnPressed()
654 {
655     if(editTrack)
656     {
657         if(editor && editor->isVisible())
658         {
659             //editor->hide();
660             if (editor->text() != editTrack->name()) {
661                 MusECore::TrackList* tl = MusEGlobal::song->tracks();
662                 for (MusECore::iTrack i = tl->begin(); i != tl->end(); ++i) {
663                     if ((*i)->name() == editor->text()) {
664                         editTrack = nullptr;
665                         editor->blockSignals(true);
666                         editor->hide();
667                         editor->blockSignals(false);
668                         QMessageBox::critical(this,
669                                               tr("MusE: bad trackname"),
670                                               tr("Please choose a unique track name"),
671                                               QMessageBox::Ok,
672                                               Qt::NoButton,
673                                               Qt::NoButton);
674                         setFocus();
675                         return;
676                     }
677                 }
678 
679                 MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackName,
680                                                                   editTrack,
681                                                                   editTrack->name(),
682                                                                   editor->text()));
683             }
684         }
685 
686         editTrack = nullptr;
687     }
688 
689     editMode = false;
690     editJustFinished = true;
691     if(editor && editor->isVisible())
692     {
693         editor->blockSignals(true);
694         editor->hide();
695         editor->blockSignals(false);
696     }
697     setFocus();
698 }
699 
chanValueFinished()700 void TList::chanValueFinished()
701 {
702     if(editTrack)
703     {
704         // Default to track port if -1 and track channel if -1.
705         const int channel = chan_edit->value() - (editTrack->isMidiTrack() ? 1 : 0); // Subtract 1 from midi channel display.
706         setTrackChannel(editTrack, false, channel, 0);
707         editTrack = nullptr;
708     }
709 
710     editMode = false;
711     editJustFinished=true;
712     if(chan_edit->isVisible())
713     {
714         chan_edit->blockSignals(true);
715         chan_edit->hide();
716         chan_edit->blockSignals(false);
717     }
718     setFocus();
719 }
720 
ctrlValueFinished()721 void TList::ctrlValueFinished()
722 {
723     if(editTrack && editTrack->isMidiTrack())
724     {
725         MusECore::MidiTrack* mt = dynamic_cast<MusECore::MidiTrack*>(editTrack);
726         //if (mt && mt->type() != MusECore::Track::DRUM)
727         // Default to track port if -1 and track channel if -1.
728         // TODO TEST: Why was DRUM excluded? I want to say just "if(mt)", but will it break something with dynamic columns?  // REMOVE Tim.
729         if (mt)
730         {
731             int val = ctrl_edit->value();
732             MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()];
733             const int chan = mt->outChannel();
734             MusECore::MidiController* mctl = mp->midiController(ctrl_num, chan);
735 
736             if (val==ctrl_edit->minimum())
737                 val=MusECore::CTRL_VAL_UNKNOWN;
738             else
739                 val+=mctl->bias();
740 
741             if (val!=MusECore::CTRL_VAL_UNKNOWN)
742             {
743                 record_controller_change_and_maybe_send(ctrl_at_tick, ctrl_num, val, mt);
744             }
745             else
746             {
747                 MusECore::Undo operations;
748                 for (MusECore::iPart p = mt->parts()->begin(); p!=mt->parts()->end(); p++)
749                 {
750                     if (p->second->tick()==0)
751                     {
752                         for (MusECore::ciEvent ev=p->second->events().begin(); ev!=p->second->events().end(); ev++)
753                         {
754                             if (ev->second.tick()!=0) break;
755                             else if (ev->second.type()==MusECore::Controller && ev->second.dataA()==ctrl_num)
756                             {
757                                 using MusECore::UndoOp;
758                                 operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, p->second, false, false));
759                                 break;
760                             }
761                         }
762                     }
763                 }
764                 MusEGlobal::song->applyOperationGroup(operations);
765             }
766         }
767 
768         editTrack = nullptr;
769     }
770 
771     editMode = false;
772     editJustFinished=true;
773     if(ctrl_edit->isVisible())
774     {
775         ctrl_edit->blockSignals(true);
776         ctrl_edit->hide();
777         ctrl_edit->blockSignals(false);
778     }
779     setFocus();
780 }
781 
782 //---------------------------------------------------------
783 //   adjustScrollbar
784 //---------------------------------------------------------
785 
adjustScrollbar()786 void TList::adjustScrollbar()
787 {
788     int h = 0;
789     MusECore::TrackList* l = MusEGlobal::song->tracks();
790     for (MusECore::iTrack it = l->begin(); it != l->end(); ++it)
791         h += (*it)->height();
792     _scroll->setMaximum(h +30);
793     redraw();
794 }
795 
796 //---------------------------------------------------------
797 //   y2Track
798 //---------------------------------------------------------
799 
y2Track(int y) const800 MusECore::Track* TList::y2Track(int y) const
801 {
802     MusECore::TrackList* l = MusEGlobal::song->tracks();
803     int ty = 0;
804     for (MusECore::iTrack it = l->begin(); it != l->end(); ++it) {
805         int h = (*it)->height();
806         if (y >= ty && y < ty + h)
807             return *it;
808         ty += h;
809     }
810     return 0;
811 }
812 
muteSelectedTracksSlot()813 void TList::muteSelectedTracksSlot()
814 {
815     bool stateDefined=false;
816     bool setTo;
817     MusECore::PendingOperationList operations;
818     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
819     for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t)
820     {
821         if ((*t)->selected()){
822             if (!stateDefined)
823             {
824                 setTo = !(*t)->isMute();
825                 stateDefined = true;
826             }
827             operations.add(MusECore::PendingOperationItem((*t), setTo, MusECore::PendingOperationItem::SetTrackMute));
828         }
829     }
830     MusEGlobal::audio->msgExecutePendingOperations(operations, true);
831     update();
832 }
833 
soloSelectedTracksSlot()834 void TList::soloSelectedTracksSlot()
835 {
836     bool stateDefined=false;
837     bool setTo;
838     MusECore::PendingOperationList operations;
839     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
840     for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t)
841     {
842         if ((*t)->selected()){
843             if (!stateDefined)
844             {
845                 setTo = !(*t)->soloMode();
846                 stateDefined = true;
847             }
848             operations.add(MusECore::PendingOperationItem((*t), setTo, MusECore::PendingOperationItem::SetTrackSolo));
849         }
850     }
851     MusEGlobal::audio->msgExecutePendingOperations(operations, true);
852     update();
853 }
854 
incrementController(MusECore::Track * t,int controllerType,int incrementValue)855 void TList::incrementController(MusECore::Track* t, int controllerType, int incrementValue)
856 {
857     MusECore::MidiTrack* midiTrack = static_cast<MusECore::MidiTrack*>(t);
858     const int channel = midiTrack->outChannel();
859 
860     MusECore::MidiPort* mp = &MusEGlobal::midiPorts[midiTrack->outPort()];
861     MusECore::MidiCtrlValListList* mcvll = mp->controller();
862     MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, controllerType);
863     MusECore::MidiCtrlValList* mcvl = imcvl->second;
864 
865     MusECore::MidiController* mc = mp->midiController(controllerType, channel, false);
866 
867     int value = mcvl->lastValidHWVal();
868     int max = 127;
869     int min = 0;
870     int bias = 0;
871     if(mc)
872     {
873         max = mc->maxVal();
874         min = mc->minVal();
875         bias = mc->bias();
876 
877         if (value == MusECore::CTRL_VAL_UNKNOWN)
878         {
879             value = mc->initVal() + bias;
880             //printf("Controller not yet set, resetting to default (%d)\n", value);
881         }
882     }
883 
884     // checking ranges seems to need bias removed
885     value -= bias;
886     value += incrementValue;
887 
888     if (value > max)
889         value = max;
890     if (value < min)
891         value = min;
892 
893     value += bias;
894 
895     mp->putControllerValue(midiTrack->outPort(), channel, controllerType, value, false);
896 }
897 
volumeSelectedTracksSlot(int incrementValue)898 void TList::volumeSelectedTracksSlot(int incrementValue)
899 {
900     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
901     for (auto t: *tracks)
902     {
903         if (!t->selected())
904             continue;
905 
906         if (t->type() == MusECore::Track::MIDI || t->type() == MusECore::Track::DRUM)
907         {
908             incrementController(t, MusECore::CTRL_VOLUME, incrementValue*2);
909         }
910         else
911         {
912             MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
913             float vol = at->volume();
914             float dbVol = muse_val2dbr(vol);
915             float newVolume = dbVol + float(incrementValue)/2;
916             if (newVolume < MusEGlobal::config.minSlider)
917                 newVolume = MusEGlobal::config.minSlider;
918             if (newVolume > 10.0)
919                 newVolume = 10.0;
920             at->setVolume(muse_db2val(newVolume));
921         }
922     }
923 }
924 
panSelectedTracksSlot(int incrementValue)925 void TList::panSelectedTracksSlot(int incrementValue)
926 {
927     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
928     for (auto t : *tracks)
929     {
930         if (!t->selected())
931             continue;
932 
933         if (t->type() == MusECore::Track::MIDI || t->type() == MusECore::Track::DRUM)
934         {
935             incrementController(t, MusECore::CTRL_PANPOT, incrementValue);
936         }
937         else
938         {
939             MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
940             float newPan = at->pan() + 0.01 * incrementValue;
941             if (newPan < -1.0)
942                 newPan = -1.0;
943             if (newPan > 1.0)
944                 newPan = 1.0;
945             at->setPan(newPan);
946         }
947     }
948 }
949 
950 
editTrackNameSlot()951 void TList::editTrackNameSlot()
952 {
953     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
954     if (tracks->countSelected() == 1) {
955         for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t)
956             if ((*t)->selected()){
957                 editTrackName(*t);
958                 break;
959             }
960     }
961 }
962 
editTrackName(MusECore::Track * t)963 void TList::editTrackName(MusECore::Track *t)
964 {
965     int colx = header->sectionPosition(COL_NAME);
966     int colw = header->sectionSize(COL_NAME);
967     int coly = t->y() - ypos;
968     int colh = t->height();
969     editTrack = t;
970     if (editor == nullptr) {
971         editor = new QLineEdit(this);
972         editor->setFrame(false);
973         connect(editor, SIGNAL(editingFinished()), SLOT(returnPressed()));
974     }
975     editor->setText(editTrack->name());
976     editor->selectAll();
977     editor->setGeometry(colx, coly, colw, colh);
978     editMode = true;
979     editor->show();
980     editor->setFocus();
981 }
982 
setTrackChannel(MusECore::Track * track,bool isDelta,int channel,int delta,bool doAllTracks)983 void TList::setTrackChannel(MusECore::Track *track, bool isDelta, int channel, int delta, bool doAllTracks)
984 {
985     MusECore::Undo operations;
986     if(track->isMidiTrack())
987     {
988         MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(track);
989 
990         if(!doAllTracks && !track->selected())
991         {
992             if(isDelta) {
993                 channel = mt->outChannel() + delta;
994                 if(channel >= MusECore::MUSE_MIDI_CHANNELS)
995                     channel = 0;
996                 else if(channel < 0)
997                     channel = MusECore::MUSE_MIDI_CHANNELS - 1;
998             }
999 
1000             channel = qMin(channel, MusECore::MUSE_MIDI_CHANNELS - 1);
1001             channel = qMax(channel, 0);
1002 
1003             if(channel != mt->outChannel())
1004                 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, mt, mt->outChannel(), channel));
1005         }
1006         else
1007         {
1008             for (const auto& t : *MusEGlobal::song->midis())
1009             {
1010                 if (doAllTracks && (t->type() != mt->type()))
1011                     continue; // only tracks of the same type
1012 
1013                 if(isDelta) {
1014                     channel = t->outChannel() + delta;
1015                     if(channel >= MusECore::MUSE_MIDI_CHANNELS)
1016                         channel = 0;
1017                     else if (channel < 0)
1018                         channel = MusECore::MUSE_MIDI_CHANNELS - 1;
1019                 }
1020 
1021                 channel = qMin(channel, MusECore::MUSE_MIDI_CHANNELS - 1);
1022                 channel = qMax(channel, 0);
1023 
1024                 if(channel != t->outChannel() && (doAllTracks || t->selected()))
1025                     operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, t, t->outChannel(), channel));
1026             }
1027         }
1028 
1029         if(!operations.empty())
1030             MusEGlobal::song->applyOperationGroup(operations);
1031     }
1032     else
1033     {
1034         if(track->type() != MusECore::Track::AUDIO_SOFTSYNTH)
1035         {
1036             if(!doAllTracks && !track->selected())
1037             {
1038                 if(isDelta) {
1039                     channel = track->channels() + delta;
1040                     if(channel > MusECore::MAX_CHANNELS)
1041                         channel = 1;
1042                     else if(channel < 1)
1043                         channel = MusECore::MAX_CHANNELS;
1044                 }
1045 
1046                 channel = qMin(channel, MusECore::MAX_CHANNELS);
1047                 channel = qMax(channel, 1);
1048 
1049                 if(channel != track->channels())
1050                     operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, track, track->channels(), channel));
1051             }
1052             else
1053             {
1054                 for(const auto& t : *MusEGlobal::song->tracks())
1055                 {
1056                     if(t->isMidiTrack())
1057                         continue;
1058                     if (doAllTracks && (t->type() != track->type()))
1059                         continue; // only tracks of the same type
1060 
1061                     if(isDelta) {
1062                         channel = t->channels() + delta;
1063                         if(channel > MusECore::MAX_CHANNELS)
1064                             channel = 1;
1065                         else if(channel < 1)
1066                             channel = MusECore::MAX_CHANNELS;
1067                     }
1068 
1069                     channel = qMin(channel, MusECore::MAX_CHANNELS);
1070                     channel = qMax(channel, 1);
1071 
1072                     if(channel != t->channels() && (doAllTracks || t->selected()))
1073                         operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, t, t->channels(), channel));
1074                 }
1075             }
1076 
1077             if(!operations.empty())
1078                 MusEGlobal::song->applyOperationGroup(operations);
1079         }
1080     }
1081 }
1082 
1083 //---------------------------------------------------------
1084 //   viewMouseDoubleClickEvent
1085 //---------------------------------------------------------
1086 
mouseDoubleClickEvent(QMouseEvent * ev)1087 void TList::mouseDoubleClickEvent(QMouseEvent* ev)
1088 {
1089     if((editor && (editor->isVisible() || editor->hasFocus())) ||
1090             (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) ||
1091             (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus())))
1092     {
1093         ev->accept();
1094         return;
1095     }
1096 
1097     int button  = ev->button();
1098     if(button != Qt::LeftButton) {
1099         ev->accept();
1100         return;
1101     }
1102 
1103     int x       = ev->x();
1104     int section = header->logicalIndexAt(x);
1105     if (section == -1)
1106     {
1107         ev->accept();
1108         return;
1109     }
1110 
1111     MusECore::Track* t = y2Track(ev->y() + ypos);
1112     if(t == nullptr)
1113     {
1114         ev->accept();
1115         return;
1116     }
1117 
1118     const bool shift = ((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier;
1119 
1120     int colx = header->sectionPosition(section);
1121     int colw = header->sectionSize(section);
1122     int coly = t->y() - ypos;
1123     int colh = t->height();
1124 
1125     if (t) {
1126         if (section == COL_NAME) {
1127             editTrackName(t);
1128         }
1129         else if (section == COL_OPORT || section == COL_CLASS) {
1130             if (t->isSynthTrack() || t->isMidiTrack())
1131                 openSynthGui(t);
1132         }
1133         else if (section == COL_TRACK_IDX) {
1134             if (button == Qt::LeftButton) {
1135                 if (shift) {
1136                     // Select all tracks of the same type
1137                     MusEGlobal::song->selectAllTracks(false);
1138                     MusECore::TrackList* all_tl = MusEGlobal::song->tracks();
1139                     for (const auto tit : *all_tl) {
1140                         if (tit->type() == t->type())
1141                             tit->setSelected(true);
1142                     }
1143                 } else {
1144                     MusEGlobal::song->selectAllTracks(true);
1145                 }
1146             }
1147             MusEGlobal::song->update(SC_TRACK_SELECTION);
1148         }
1149         else if (section == COL_OCHANNEL) {
1150             // Enabled for audio tracks. And synth channels cannot be changed ATM.
1151             // Default to track port if -1 and track channel if -1.
1152             if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH)
1153             {
1154                 ev->accept();
1155                 return;
1156             }
1157 
1158             // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up.
1159             editTrack=t;
1160             if (!chan_edit)
1161             {
1162                 chan_edit=new QSpinBox(this);
1163                 chan_edit->setFrame(false);
1164                 chan_edit->setMinimum(1);
1165                 connect(chan_edit, SIGNAL(editingFinished()), SLOT(chanValueFinished()));
1166             }
1167             if (t->isMidiTrack())
1168             {
1169                 chan_edit->setMaximum(MusECore::MUSE_MIDI_CHANNELS);
1170                 chan_edit->setValue(((MusECore::MidiTrack*)editTrack)->outChannel()+1);
1171             }
1172             else // if(t->type() != MusECore::Track::AUDIO_SOFTSYNTH)
1173             {
1174                 chan_edit->setMaximum(MusECore::MAX_CHANNELS);
1175                 chan_edit->setValue(((MusECore::AudioTrack*)editTrack)->channels());
1176             }
1177             int w=colw;
1178             if (w < chan_edit->sizeHint().width()) w=chan_edit->sizeHint().width();
1179             chan_edit->setGeometry(colx, coly, w, colh);
1180             chan_edit->selectAll();
1181             editMode = true;
1182             chan_edit->show();
1183             chan_edit->setFocus();
1184         }
1185         else if (section >= COL_CUSTOM_MIDICTRL_OFFSET)
1186         {
1187             if (t->isMidiTrack())
1188             {
1189                 editTrack=t;
1190 
1191                 ctrl_num=Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].ctrl;
1192 
1193                 MusECore::MidiTrack* mt=(MusECore::MidiTrack*)t;
1194                 MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()];
1195                 const int chan = mt->outChannel();
1196                 MusECore::MidiController* mctl = mp->midiController(ctrl_num, chan);
1197 
1198                 if (ctrl_num!=MusECore::CTRL_PROGRAM)
1199                 {
1200                     if (Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos ==
1201                             Arranger::custom_col_t::AFFECT_BEGIN)
1202                         ctrl_at_tick=0;
1203                     else
1204                         ctrl_at_tick=MusEGlobal::song->cpos();
1205 
1206                     if (ctrl_edit==nullptr)
1207                     {
1208                         ctrl_edit=new QSpinBox(this);
1209                         ctrl_edit->setSpecialValueText(tr("off"));
1210                         connect(ctrl_edit, SIGNAL(editingFinished()), SLOT(ctrlValueFinished()));
1211                     }
1212 
1213                     ctrl_edit->setMinimum(mctl->minVal()-1); // -1 because of the specialValueText
1214                     ctrl_edit->setMaximum(mctl->maxVal());
1215                     ctrl_edit->setValue(((MusECore::MidiTrack*)editTrack)->getControllerChangeAtTick(0,ctrl_num)-mctl->bias());
1216                     int w=colw;
1217                     if (w < ctrl_edit->sizeHint().width()) w=ctrl_edit->sizeHint().width();
1218                     ctrl_edit->setGeometry(colx, coly, w, colh);
1219                     editMode = true;
1220                     ctrl_edit->show();
1221                     ctrl_edit->setFocus();
1222                 }
1223             }
1224         }
1225     }
1226     ev->accept();
1227 }
1228 
1229 //---------------------------------------------------------
1230 //   synthGUI context menu
1231 //---------------------------------------------------------
1232 
showMidiClassPopupMenu(MusECore::Track * t,int x,int y)1233 void TList::showMidiClassPopupMenu(MusECore::Track* t, int x, int y)
1234 {
1235     if (t->type() == MusECore::Track::AUDIO_SOFTSYNTH)
1236     {
1237         MusECore::SynthI* synth = static_cast<MusECore::SynthI*>(t);
1238         PopupMenu* p = new PopupMenu;
1239 
1240         QAction* cact = p->addAction(*MusEGui::ankerSVGIcon, tr("MIDI Ports/Soft Synths..."));
1241         p->addSeparator();
1242 
1243         if(!synth->synth())
1244             p->addAction(tr("SYNTH IS UNAVAILABLE!"));
1245 
1246         QAction* gact = p->addAction(tr("Show Generic Synth GUI"));
1247         gact->setCheckable(true);
1248         gact->setEnabled(synth->hasGui());
1249         gact->setChecked(synth->guiVisible());
1250 
1251         QAction* nact = p->addAction(tr("Show Native Synth GUI"));
1252         nact->setCheckable(true);
1253         nact->setEnabled(synth->hasNativeGui());
1254         nact->setChecked(synth->nativeGuiVisible());
1255 
1256 #ifdef LV2_SUPPORT
1257         PopupMenu *mSubPresets = nullptr;
1258         //show presets submenu for lv2 synths
1259         if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::LV2_SYNTH)
1260         {
1261             mSubPresets = new PopupMenu(tr("Presets"));
1262             p->addMenu(mSubPresets);
1263             static_cast<MusECore::LV2SynthIF *>(synth->sif())->populatePresetsMenu(mSubPresets);
1264         }
1265 #endif
1266 
1267         // If it has a gui but we don't have OSC, disable the action.
1268 #ifndef OSC_SUPPORT
1269 #ifdef DSSI_SUPPORT
1270         if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::DSSI_SYNTH)
1271         {
1272             nact->setChecked(false);
1273             nact->setEnabled(false);
1274         }
1275 #endif
1276 #endif
1277 
1278         QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), nullptr);
1279         if (ract == gact) {
1280             bool show = !synth->guiVisible();
1281             synth->showGui(show);
1282         }
1283         else if (ract == nact) {
1284             bool show = !synth->nativeGuiVisible();
1285             synth->showNativeGui(show);
1286         }
1287         else if (ract == cact) {
1288             MusEGlobal::muse->configMidiPorts();
1289         }
1290 
1291 #ifdef LV2_SUPPORT
1292         else if (mSubPresets != nullptr && ract != nullptr && ract->data().canConvert<void *>()) {
1293             static_cast<MusECore::LV2SynthIF *>(synth->sif())->applyPreset(ract->data().value<void *>());
1294         }
1295 #endif
1296         delete p;
1297         return;
1298     }
1299 
1300 
1301     // MIDI tracks
1302     if (t->type() != MusECore::Track::MIDI && t->type() != MusECore::Track::DRUM)
1303         return;
1304 
1305     int oPort = static_cast<MusECore::MidiTrack*>(t)->outPort();
1306     MusECore::MidiPort* port = &MusEGlobal::midiPorts[oPort];
1307 
1308     PopupMenu* p = new PopupMenu;
1309 
1310     QAction *switchact = nullptr, *gact = nullptr, *nact = nullptr;
1311 
1312 #ifdef LV2_SUPPORT
1313     PopupMenu *mSubPresets = nullptr;
1314 #endif
1315 
1316     if (t->type() == MusECore::Track::MIDI)
1317         switchact = p->addAction(*drumeditSVGIcon, tr("Convert MIDI to Drum Track"));
1318     else
1319         switchact = p->addAction(*pianorollSVGIcon, tr("Convert Drum to MIDI Track"));
1320 
1321 
1322     if(port->device() && port->device()->isSynti())
1323     {
1324         MusECore::SynthI* synth = static_cast<MusECore::SynthI*>(port->device());
1325         if(synth->synth()) {
1326 
1327             p->addSeparator();
1328 
1329             gact = p->addAction(tr("Show Generic Synth GUI"));
1330             gact->setCheckable(true);
1331             gact->setEnabled(port->hasGui());
1332             gact->setChecked(port->guiVisible());
1333 
1334             nact = p->addAction(tr("Show Native Synth GUI"));
1335             nact->setCheckable(true);
1336             nact->setEnabled(port->hasNativeGui());
1337             nact->setChecked(port->nativeGuiVisible());
1338 
1339             // If it has a gui but we don't have OSC, disable the action.
1340 #ifndef OSC_SUPPORT
1341 #ifdef DSSI_SUPPORT
1342             if(port->device() && port->device()->isSynti())
1343             {
1344                 MusECore::SynthI* synth = static_cast<MusECore::SynthI*>(port->device());
1345                 if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::DSSI_SYNTH)
1346                 {
1347                     nact->setChecked(false);
1348                     nact->setEnabled(false);
1349                 }
1350             }
1351 #endif
1352 #endif
1353 
1354 #ifdef LV2_SUPPORT
1355             if(port->device() && port->device()->isSynti())
1356             {
1357                 MusECore::SynthI* synth = static_cast<MusECore::SynthI*>(port->device());
1358                 //show presets submenu for lv2 synths
1359                 if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::LV2_SYNTH)
1360                 {
1361                     mSubPresets = new PopupMenu(tr("Presets"));
1362                     p->addMenu(mSubPresets);
1363                     static_cast<MusECore::LV2SynthIF *>(synth->sif())->populatePresetsMenu(mSubPresets);
1364                 }
1365             }
1366 #endif
1367         }
1368     }
1369 
1370     QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), nullptr);
1371     if (ract == gact) {
1372         port->showGui(!port->guiVisible());
1373     }
1374     else if (ract == nact) {
1375         port->showNativeGui(!port->nativeGuiVisible());
1376     }
1377     else if (ract == switchact) {
1378         if (!t->selected())
1379             changeTrackToType(t, t->type() == MusECore::Track::MIDI ? MusECore::Track::DRUM : MusECore::Track::MIDI);
1380         else
1381         {
1382             MusECore::Track::TrackType curType = t->type();
1383             for (auto const myt : *MusEGlobal::song->tracks())
1384             {
1385                 if (myt->selected() && myt->type() == curType)
1386                     changeTrackToType(myt, myt->type() == MusECore::Track::MIDI ? MusECore::Track::DRUM : MusECore::Track::MIDI);
1387             } // track for-loop
1388         }
1389     }
1390 
1391 
1392 #ifdef LV2_SUPPORT
1393     else if (mSubPresets != nullptr && ract != nullptr && ract->data().canConvert<void *>())
1394     {
1395         if (port->device() && port->device()->isSynti())
1396         {
1397             MusECore::SynthI* synth = static_cast<MusECore::SynthI*>(port->device());
1398             static_cast<MusECore::LV2SynthIF *>(synth->sif())->applyPreset(ract->data().value<void *>());
1399         }
1400     }
1401 #endif
1402 
1403     delete p;
1404 }
1405 
1406 //---------------------------------------------------------
1407 //   audio output context menu
1408 //---------------------------------------------------------
1409 
showAudioOutPopupMenu(MusECore::Track * t,int x,int y)1410 void TList::showAudioOutPopupMenu(MusECore::Track* t, int x, int y)
1411 {
1412     if (t->type() != MusECore::Track::AUDIO_OUTPUT)
1413         return;
1414 
1415     PopupMenu* p = new PopupMenu;
1416 
1417     QAction* actTrack = p->addAction(*MusEGui::downmixTrackSVGIcon, tr("Render Downmix to Selected Wave Track"));
1418     actTrack->setEnabled(!MusEGlobal::audio->bounce());
1419     QAction* actFile = p->addAction(*MusEGui::downmixOnSVGIcon, tr("Render Downmix to a File..."));
1420     actFile->setEnabled(!MusEGlobal::audio->bounce());
1421 
1422     QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), nullptr);
1423     if (ract == actFile)
1424         MusEGlobal::muse->bounceToFile(static_cast<MusECore::AudioOutput*>(t));
1425     else if (ract == actTrack)
1426         MusEGlobal::muse->bounceToTrack(static_cast<MusECore::AudioOutput*>(t));
1427 
1428     delete p;
1429 }
1430 
1431 //---------------------------------------------------------
1432 //   tracklistChanged
1433 //---------------------------------------------------------
1434 
tracklistChanged()1435 void TList::tracklistChanged()
1436 {
1437     redraw();
1438 }
1439 
1440 //---------------------------------------------------------
1441 //   keyPressEvent
1442 //---------------------------------------------------------
1443 
keyPressEvent(QKeyEvent * e)1444 void TList::keyPressEvent(QKeyEvent* e)
1445 {
1446     if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
1447     {
1448         e->accept();
1449         return;
1450     }
1451 
1452     if (editMode)
1453     {
1454         if ( e->key() == Qt::Key_Escape )
1455         {
1456             if(editor && editor->isVisible())
1457             {
1458                 editor->blockSignals(true);
1459                 editor->hide();
1460                 editor->blockSignals(false);
1461             }
1462             if(chan_edit && chan_edit->isVisible())
1463             {
1464                 chan_edit->blockSignals(true);
1465                 chan_edit->hide();
1466                 chan_edit->blockSignals(false);
1467             }
1468             if(ctrl_edit && ctrl_edit->isVisible())
1469             {
1470                 ctrl_edit->blockSignals(true);
1471                 ctrl_edit->hide();
1472                 ctrl_edit->blockSignals(false);
1473             }
1474             editTrack = nullptr;
1475             editMode = false;
1476             setFocus();
1477             return;
1478         }
1479         return;
1480     }
1481     else if (!editJustFinished)
1482     {
1483         emit keyPressExt(e); //redirect keypress events to main app. don't call this when confirming an editor
1484     }
1485     else
1486         editJustFinished=false;
1487 
1488     emit keyPressExt(e); //redirect keypress events to main app
1489 }
1490 
1491 //---------------------------------------------------------
1492 //   moveSelection
1493 //---------------------------------------------------------
1494 
moveSelection(int n)1495 void TList::moveSelection(int n)
1496 {
1497     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
1498 
1499     int nselect = tracks->countSelected();
1500     if (nselect > 1) {
1501       // remove selection for all but the first track
1502       MusECore::Track* selTrack = nullptr;
1503       for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) {
1504 
1505         if (selTrack == nullptr) {
1506           if ((*t)->selected())
1507             selTrack = *t;
1508         } else {
1509           if ((*t)->selected())
1510             (*t)->setSelected(false);
1511         }
1512       }
1513     }
1514 
1515     MusECore::Track* selTrack = nullptr;
1516     for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) {
1517         MusECore::iTrack s = t;
1518         if ((*t)->selected()) {
1519             if (n > 0) { // Move down
1520                 while (n--) {
1521                     ++t;
1522                     if (t == tracks->end()) {
1523                         --t;
1524                         break;
1525                     }
1526                     // skip over hidden tracks
1527                     if (!(*t)->isVisible()) {
1528                         n++;
1529                         continue;
1530                     }
1531                     selTrack = *t;
1532                     break;
1533                 }
1534             }
1535             else { // Move up
1536                 while (n++ != 0) {
1537                     if (t == tracks->begin())
1538                         break;
1539                     --t;
1540                     // skip over hidden tracks
1541                     if (!(*t)->isVisible()) {
1542                         n--;
1543                         continue;
1544                     }
1545                     selTrack = *t;
1546                     break;
1547                 }
1548             }
1549             if(selTrack)
1550             {
1551                 (*s)->setSelected(false);
1552                 selTrack->setSelected(true);
1553 
1554                 // if selected track is outside of view, enforce scrolling
1555                 if (selTrack->y() > this->height()+ypos-20)
1556                 {
1557                     emit verticalScrollSetYpos(ypos+selTrack->height());
1558                 }
1559                 else if (selTrack->y() < ypos)
1560                 {
1561                     emit verticalScrollSetYpos(selTrack->y());
1562                 }
1563 
1564                 // rec enable track if expected
1565                 MusECore::TrackList recd = getRecEnabledTracks();
1566 
1567                 if (!MusEGlobal::audio->isRecording() &&
1568                         recd.size() == 1 &&
1569                         MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection
1570                     MusEGlobal::song->setRecordFlag((MusECore::Track*)recd.front(),false);
1571                     MusEGlobal::song->setRecordFlag((selTrack),true);
1572                 }
1573 
1574                 if (editTrack && editTrack != selTrack)
1575                     returnPressed();
1576                 redraw();
1577             }
1578             break;
1579         }
1580     }
1581     if(selTrack)
1582         MusEGlobal::song->update(SC_TRACK_SELECTION);
1583 }
1584 
getRecEnabledTracks()1585 MusECore::TrackList TList::getRecEnabledTracks()
1586 {
1587     MusECore::TrackList recEnabled;
1588     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
1589     for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) {
1590         if ((*t)->recordFlag())
1591             recEnabled.push_back(*t);
1592     }
1593     return recEnabled;
1594 }
1595 
1596 //---------------------------------------------------------
1597 //   changeAutomation
1598 //---------------------------------------------------------
1599 
changeAutomation(QAction * act)1600 void TList::changeAutomation(QAction* act)
1601 {
1602     if(!editAutomation || editAutomation->isMidiTrack())
1603         return;
1604 
1605     if (act->data().toInt() == AUTO_INVALID)
1606         return;
1607 
1608     bool setRead = false;
1609 
1610     if (act->data().toInt() == AUTO_SHOW_ALL) {
1611         MusECore::CtrlListList* cll = static_cast<MusECore::AudioTrack*>(editAutomation)->controller();
1612         for (auto& it : *cll) {
1613             MusECore::CtrlList *cl = it.second;
1614             if (!cl->dontShow() && !cl->isVisible() && cl->size() > 0) {
1615                 cl->setVisible(true);
1616                 setRead = true;
1617             }
1618         }
1619     }
1620     else if (act->data().toInt() == AUTO_HIDE_ALL) {
1621         MusECore::CtrlListList* cll = static_cast<MusECore::AudioTrack*>(editAutomation)->controller();
1622         for (auto& it : *cll) {
1623             MusECore::CtrlList *cl = it.second;
1624             if (cl->isVisible())
1625                 cl->setVisible(false);
1626         }
1627     }
1628     else {
1629         int colindex = act->data().toInt() & 0xff;
1630         int id = (act->data().toInt() & 0x00ffffff) >> 8;
1631         // Is it the midi control action or clear action item?
1632         if (colindex == AUTO_CLEAR_MIDI || colindex == AUTO_MIDI_ASSIGN)
1633             return;
1634 
1635         if (colindex < 100)
1636             return; // this was meant for changeAutomationColor
1637         // one of these days I'll rewrite this so it's understandable
1638         // this is just to get it up and running...
1639 
1640         MusECore::CtrlListList* cll = static_cast<MusECore::AudioTrack*>(editAutomation)->controller();
1641         for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) {
1642             MusECore::CtrlList *cl = icll->second;
1643             if (id == cl->id())  // got it, change state
1644                 cl->setVisible(act->isChecked());
1645         }
1646         setRead = true;
1647     }
1648 
1649     // if automation is OFF for the track we change it to READ as a convenience
1650     // hopefully this confuses users far less than not understanding why the
1651     // automation does not do anything.
1652     if (setRead && static_cast<MusECore::AudioTrack*>(editAutomation)->automationType() == MusECore::AUTO_OFF)
1653     {
1654         MusEGlobal::audio->msgSetTrackAutomationType(static_cast<MusECore::AudioTrack*>(editAutomation), MusECore::AUTO_READ);
1655         if (MusEGlobal::debugMsg)
1656             printf("Changing automation from OFF to READ\n");
1657     }
1658 
1659     MusEGlobal::song->update(SC_TRACK_MODIFIED|SC_AUTOMATION);
1660 }
1661 
1662 //---------------------------------------------------------
1663 //   changeAutomation
1664 //---------------------------------------------------------
changeAutomationColor(QAction * act)1665 void TList::changeAutomationColor(QAction* act)
1666 {
1667     if(!editAutomation || editAutomation->isMidiTrack())
1668         return;
1669     if(act->data().toInt() == AUTO_INVALID)
1670         return;
1671     int colindex = act->data().toInt() & 0xff;
1672     int id = (act->data().toInt() & 0x00ffffff) >> 8;
1673 
1674     // Is it the clear automation action item?
1675     // (As commented below, we should rewrite this to make it easier to understand..)
1676     if (colindex == AUTO_CLEAR_AUTO)
1677     {
1678         if(QMessageBox::question(MusEGlobal::muse, QString("Muse"),
1679                                  tr("Clear all controller events?"), tr("&Ok"), tr("&Cancel"),
1680                                  QString(), 0, 1 ) == 0)
1681         {
1682             MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(editAutomation);
1683             MusEGlobal::audio->msgClearControllerEvents(track, id);
1684 
1685         }
1686     }
1687 
1688 
1689     // Is it the clear midi control action item?
1690     if(colindex == AUTO_CLEAR_MIDI)
1691     {
1692         MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(editAutomation);
1693         MusECore::MidiAudioCtrlMap* macp = track->controller()->midiControls();
1694         MusECore::AudioMidiCtrlStructMap amcs;
1695         macp->find_audio_ctrl_structs(id, &amcs);
1696         if(!amcs.empty())
1697             MusEGlobal::audio->msgIdle(true);  // Gain access to structures, and sync with audio
1698         for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs)
1699             macp->erase(*iamcs);
1700         if(!amcs.empty())
1701             MusEGlobal::audio->msgIdle(false);
1702 
1703         // Hm, need to remove the 'clear' item, and the status lines below it. Try this:
1704         QActionGroup* midi_actgrp = act->actionGroup();
1705         if(midi_actgrp)
1706         {
1707             QList<QAction*> act_list = midi_actgrp->actions();
1708             int sz = act_list.size();
1709             for(int i = 0; i < sz; ++i)
1710             {
1711                 QAction* list_act = act_list.at(i);
1712                 ///midi_actgrp->removeAction(list_act);
1713                 // list_act has no parent now.
1714                 ///delete list_act;
1715                 list_act->setVisible(false); // HACK Cannot delete any actions! Causes crash with our PopupMenu due to recent fixes.
1716             }
1717         }
1718         return;
1719     }
1720 
1721     // Is it the midi control action item?
1722     if(colindex == AUTO_MIDI_ASSIGN)
1723     {
1724         MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(editAutomation);
1725         MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls();
1726         MusECore::AudioMidiCtrlStructMap amcs;
1727         macm->find_audio_ctrl_structs(id, &amcs);
1728 
1729         int port = -1, chan = 0, ctrl = 0;
1730         for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs)
1731         {
1732             macm->hash_values((*iamcs)->first, &port, &chan, &ctrl);
1733             break; // Only a single item for now, thanks!
1734         }
1735 
1736         MidiAudioControl* pup = new MidiAudioControl(port, chan, ctrl);
1737 
1738         if(pup->exec() == QDialog::Accepted)
1739         {
1740             MusEGlobal::audio->msgIdle(true);  // Gain access to structures, and sync with audio
1741             // Erase all for now.
1742             for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs)
1743                 macm->erase(*iamcs);
1744 
1745             port = pup->port(); chan = pup->chan(); ctrl = pup->ctrl();
1746             if(port >= 0 && chan >=0 && ctrl >= 0)
1747                 // Add will replace if found.
1748                 macm->add_ctrl_struct(port, chan, ctrl, MusECore::MidiAudioCtrlStruct(id));
1749 
1750             MusEGlobal::audio->msgIdle(false);
1751         }
1752 
1753         delete pup;
1754         return;
1755     }
1756 
1757     if (colindex > 100)
1758         return; // this was meant for changeAutomation
1759     // one of these days I'll rewrite this so it's understandable
1760     // this is just to get it up and running...
1761 
1762     MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)editAutomation)->controller();
1763     for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) {
1764         MusECore::CtrlList *cl = icll->second;
1765         if (cl->id() == id) { // got it, change color and enable
1766             cl->setColor(collist[colindex]);
1767             cl->setVisible(true);
1768         }
1769     }
1770     MusEGlobal::song->update(SC_TRACK_MODIFIED);
1771 }
1772 
1773 //---------------------------------------------------------
1774 //   colorMenu
1775 //---------------------------------------------------------
colorMenu(QColor c,int id,QWidget * parent)1776 PopupMenu* TList::colorMenu(QColor c, int id, QWidget* parent)
1777 {
1778     PopupMenu * m = new PopupMenu(parent, true);
1779 
1780     QActionGroup* col_actgrp = new QActionGroup(m);
1781     m->addAction(new MusEGui::MenuTitleItem(tr("Change color"), m));
1782     col_actgrp->setExclusive(true);
1783     for (int i = 0; i< 6; i++) {
1784         QPixmap pix(10,10);
1785         QPainter p(&pix);
1786         p.fillRect(0,0,10,10,collist[i]);
1787         QIcon icon(pix);
1788         QAction *act = col_actgrp->addAction(icon,colnames[i]);
1789         act->setCheckable(true);
1790         if (c == collist[i])
1791             act->setChecked(true);
1792         act->setData((id<<8) + i); // Shift 8 bits. Color in the bottom 8 bits.
1793     }
1794     m->addActions(col_actgrp->actions());
1795 
1796     //m->addSeparator();
1797     m->addAction(new MenuTitleItem(tr("Midi control"), m));
1798 
1799     if(editAutomation && !editAutomation->isMidiTrack())
1800     {
1801         QAction *act = m->addAction(tr("Assign"));
1802         act->setCheckable(false);
1803         act->setData((id<<8) + AUTO_MIDI_ASSIGN); // Shift 8 bits. Make midi menu the last item at 255.
1804 
1805         MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(editAutomation);
1806         MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls();
1807         MusECore::AudioMidiCtrlStructMap amcs;
1808         macm->find_audio_ctrl_structs(id, &amcs);
1809 
1810         // Group only the clear and status items so they can both be easily removed when clear is clicked.
1811         if(!amcs.empty())
1812         {
1813             QActionGroup* midi_actgrp = new QActionGroup(m);
1814             QAction *cact = midi_actgrp->addAction(tr("Clear"));
1815             cact->setData((id<<8) + AUTO_CLEAR_MIDI); // Shift 8 bits. Make clear the second-last item at 254
1816             for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs)
1817             {
1818                 int port, chan, mctrl;
1819                 macm->hash_values((*iamcs)->first, &port, &chan, &mctrl);
1820                 //QString s = QString("Port:%1 Chan:%2 Ctl:%3-%4").arg(port + 1)
1821                 QString s = QString("Port:%1 Chan:%2 Ctl:%3").arg(port + 1)
1822                         .arg(chan + 1)
1823                         //.arg((mctrl >> 8) & 0xff)
1824                         //.arg(mctrl & 0xff);
1825                         .arg(MusECore::midiCtrlName(mctrl, true));
1826                 QAction *mact = midi_actgrp->addAction(s);
1827                 mact->setEnabled(false);
1828                 mact->setData(AUTO_INVALID); // Not used
1829             }
1830             m->addActions(midi_actgrp->actions());
1831         }
1832     }
1833     m->addAction(new MenuTitleItem(tr("Other"), m));
1834     QAction *act = m->addAction(tr("Clear automation"));
1835     act->setCheckable(false);
1836     act->setData((id<<8) + AUTO_CLEAR_AUTO); // Shift 8 bits. Make clear menu item 253
1837 
1838     connect(m, SIGNAL(triggered(QAction*)), SLOT(changeAutomationColor(QAction*)));
1839     return m;
1840 
1841 }
1842 
1843 //---------------------------------------------------------
1844 //   mousePressEvent
1845 //---------------------------------------------------------
mousePressEvent(QMouseEvent * ev)1846 void TList::mousePressEvent(QMouseEvent* ev)
1847 {
1848     if((editor && (editor->isVisible() || editor->hasFocus())) ||
1849             (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) ||
1850             (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus())))
1851     {
1852         ev->accept();
1853         return;
1854     }
1855 
1856     const int x       = ev->x();
1857     const int y       = ev->y();
1858     const int button  = ev->button();
1859     const bool ctrl   = ((QInputEvent*)ev)->modifiers() & Qt::ControlModifier;
1860     const bool shift  = ((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier;
1861 
1862     MusECore::Track* t    = y2Track(y + ypos);
1863 
1864     TrackColumn col = TrackColumn(header->logicalIndexAt(x));
1865     if (t == nullptr) {
1866         if (button == Qt::RightButton) {
1867 
1868             // Show the menu
1869             QAction* act = addTrackMenu->exec(ev->globalPos(), nullptr);
1870 
1871             // Valid click?
1872             if(act)
1873             {
1874                 t = MusEGlobal::song->addNewTrack(act);  // Add at end of list.
1875                 if(t && t->isVisible())
1876                 {
1877                     MusEGlobal::song->selectAllTracks(false);
1878                     t->setSelected(true);
1879                     MusEGlobal::song->update(SC_TRACK_SELECTION);
1880                     adjustScrollbar();
1881                 }
1882             }
1883 
1884         }
1885         return;
1886     }
1887 
1888     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
1889     dragYoff = y - (t->y() - ypos);
1890     startY   = y;
1891 
1892     if (resizeFlag) {
1893         mode = RESIZE;
1894 
1895         int y  = ev->y();
1896         int ty = -ypos;
1897         sTrack = 0;
1898         for (MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it, ++sTrack) {
1899             int h = (*it)->height();
1900             ty += h;
1901             if (y >= (ty-2)) {
1902 
1903                 if ( (*it) == tracks->back() && y > ty ) { // DELETETHIS, only retain if(foo) break;?
1904                     //printf("tracks->back() && y > ty\n");
1905                 }
1906                 else if ( y > (ty+2) ) {
1907                     //printf(" y > (ty+2) \n");
1908                 }
1909                 else {
1910                     //printf("ogga ogga\n");
1911                     break;
1912                 }
1913                 //&& y < (ty)) DELETETHIS
1914                 //     break;
1915             }
1916         }
1917 
1918         return;
1919     }
1920 
1921     mode = NORMAL;
1922 
1923     if (button == Qt::LeftButton && col != COL_INPUT_MONITOR && col != COL_RECORD && col != COL_MUTE && col != COL_SOLO)
1924     {
1925         mode = START_DRAG;  // Allow a track drag to start.
1926 
1927         if (ctrl) {
1928             if (tracks->countSelected() == 1 && tracks->currentSelection() == t)
1929                 return;
1930 
1931             t->setSelected(!t->selected());
1932         }
1933 
1934         else if (shift) {
1935             if (tracks->countSelected() == 1 && tracks->currentSelection() == t)
1936                 return;
1937 
1938             else if (tracks->countSelected() == 0)
1939                 t->setSelected(true);
1940 
1941             else {
1942                 int indexRange = 0, i1 = -1, i2 = -1;
1943                 int indexClicked = tracks->index(t);
1944                 for (MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) {
1945                     if ( (*it)->selected() ) {
1946                         indexRange = tracks->index(*it);
1947                         if (indexRange < indexClicked) {
1948                             i1 = indexRange;
1949                             i2 = indexClicked;
1950                             break;
1951                         }
1952                     }
1953                 }
1954 
1955                 if (i1 == -1) {
1956                     i1 = indexClicked;
1957                     i2 = indexRange;
1958                 }
1959 
1960                 MusEGlobal::song->selectAllTracks(false);
1961                 for (MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) {
1962                     if (tracks->index(*it) >= i1 && tracks->index(*it) <= i2 && (*it) != t)
1963                         (*it)->setSelected(true);
1964                 }
1965                 t->setSelected(true);
1966             }
1967 
1968         } else {
1969 
1970             MusEGlobal::song->selectAllTracks(false);
1971             t->setSelected(true);
1972 
1973             // rec enable track if expected
1974             MusECore::TrackList recd = getRecEnabledTracks();
1975             if (!MusEGlobal::audio->isRecording() &&
1976                     recd.size() == 1 &&
1977                     MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection
1978                 MusEGlobal::song->setRecordFlag((MusECore::Track*)recd.front(),false);
1979                 MusEGlobal::song->setRecordFlag(t,true);
1980             }
1981         }
1982 
1983 
1984         MusEGlobal::song->update(SC_TRACK_SELECTION);
1985     }
1986     else
1987     {
1988         switch (col) {
1989 
1990         case COL_TRACK_IDX:
1991             break;
1992 
1993         case COL_CLEF:
1994         {
1995             if (button == Qt::RightButton && t->isMidiTrack() && t->type() == MusECore::Track::MIDI) {
1996                 QMenu* p = new QMenu;
1997                 p->addAction(tr("Treble clef"))->setData(0);
1998                 p->addAction(tr("Bass clef"))->setData(1);
1999                 p->addAction(tr("Grand Staff"))->setData(2);
2000 
2001                 // Show the menu
2002                 QAction* act = p->exec(ev->globalPos(), nullptr);
2003                 if (act) {
2004                     switch (act->data().toInt()) {
2005                     case 0:
2006                         ((MusECore::MidiTrack*)t)->setClef(trebleClef);
2007                         break;
2008                     case 1:
2009                         ((MusECore::MidiTrack*)t)->setClef(bassClef);
2010                         break;
2011                     case 2:
2012                         ((MusECore::MidiTrack*)t)->setClef(grandStaff);
2013                         break;
2014                     default:
2015                         break;
2016                     }
2017                 }
2018                 delete p;
2019             }
2020             break;
2021         }
2022 
2023         case COL_AUTOMATION:
2024         {
2025             if (button == Qt::RightButton && !t->isMidiTrack()) {
2026                 editAutomation = t;
2027                 PopupMenu* p = new PopupMenu(true);
2028                 p->disconnect();
2029                 p->clear();
2030                 p->setTitle(tr("Viewable automation"));
2031                 MusECore::CtrlListList* cll = static_cast<MusECore::AudioTrack*>(t)->controller();
2032                 QAction* act = nullptr;
2033                 int last_rackpos = -1;
2034                 bool internalHeaderDone = false;
2035                 bool synthHeaderDone = false;
2036 
2037                 p->addAction(new MusEGui::MenuTitleItem(tr("Automation Display"), p));
2038                 act = p->addAction(*dummySVGIcon, tr("Show All with Events"));
2039                 act->setData(AUTO_SHOW_ALL);
2040                 act = p->addAction(tr("Hide All"));
2041                 act->setData(AUTO_HIDE_ALL);
2042 
2043                 QList<const MusECore::CtrlList*> tmpList;
2044 
2045                 for (const auto& icll : *cll) {
2046                     MusECore::CtrlList *cl = icll.second;
2047                     if (cl->dontShow())
2048                         continue;
2049 
2050                     int ctrl = cl->id();
2051 
2052                     if(ctrl < AC_PLUGIN_CTL_BASE)
2053                     {
2054                         if(!internalHeaderDone) {
2055                             p->addAction(new MusEGui::MenuTitleItem(tr("Internal"), p));
2056                             internalHeaderDone = true;
2057                         }
2058                         addAutoMenuAction(p, cl);
2059                     }
2060                     else
2061                     {
2062                         if (ctrl < static_cast<int>(MusECore::genACnum(MusECore::MAX_PLUGINS, 0)))  // The beginning of the special dssi synth controller block.
2063                         {
2064                             int rackpos = (ctrl - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW;
2065                             if (rackpos < MusECore::PipelineDepth)
2066                             {
2067                                 if(rackpos != last_rackpos)
2068                                 {
2069                                     outputAutoMenuSorted(p, tmpList);
2070 
2071                                     QString s = static_cast<MusECore::AudioTrack*>(t)->efxPipe() ?
2072                                                 static_cast<MusECore::AudioTrack*>(t)->efxPipe()->name(rackpos) : QString();
2073                                     p->addAction(new MusEGui::MenuTitleItem(s, p));
2074                                     last_rackpos = rackpos;
2075                                 }
2076                             }
2077                             tmpList.append(cl);
2078                         }
2079                         else
2080                         {
2081                             if (t->type() == MusECore::Track::AUDIO_SOFTSYNTH)
2082                             {
2083                                 if(!synthHeaderDone) {
2084                                     outputAutoMenuSorted(p, tmpList);
2085                                     p->addAction(new MusEGui::MenuTitleItem(tr("Synth"), p));
2086                                     synthHeaderDone = true;
2087                                 }
2088                                 tmpList.append(cl);
2089                             }
2090                         }
2091                     }
2092                 }
2093 
2094                 outputAutoMenuSorted(p, tmpList);
2095 
2096                 connect(p, SIGNAL(triggered(QAction*)), SLOT(changeAutomation(QAction*)));
2097                 p->exec(QCursor::pos());
2098 
2099                 delete p;
2100             }
2101             break;
2102         }
2103 
2104         case COL_INPUT_MONITOR:
2105         {
2106             if(!t->canRecordMonitor())
2107                 break;
2108 
2109             const bool val = !(t->recMonitor());
2110 
2111             if (button == Qt::LeftButton)
2112             {
2113                 // apply to selected tracks
2114                 if (t->selected() && tracks->countSelected() > 1) {
2115                     MusECore::Undo operations;
2116                     MusECore::TrackList* tl = MusEGlobal::song->tracks();
2117                     for (const auto tit : *tl)
2118                     {
2119                         if (tit->selected() && tit->canRecordMonitor())
2120                             operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackRecMonitor, tit, val));
2121                     }
2122                     if (!operations.empty())
2123                         MusEGlobal::song->applyOperationGroup(operations);
2124                 }
2125                 else
2126                 {
2127                 // This is a minor operation easily manually undoable. Let's not clog the undo list with it.
2128                 MusECore::PendingOperationList operations;
2129                 operations.add(MusECore::PendingOperationItem(t, val, MusECore::PendingOperationItem::SetTrackRecMonitor));
2130                 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
2131                 }
2132             }
2133             else if (button == Qt::RightButton)
2134             {
2135                 // enable or disable ALL tracks of this type
2136                 // This is a major operation not easily manually undoable. Let's make it undoable.
2137                 MusECore::Undo operations;
2138                 MusECore::TrackList* all_tl = MusEGlobal::song->tracks();
2139                 foreach (MusECore::Track *other_t, *all_tl)
2140                 {
2141                     if(other_t->type() != t->type())
2142                         continue;
2143                     operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackRecMonitor, other_t, val));
2144                 }
2145                 if(!operations.empty())
2146                 {
2147                     MusEGlobal::song->applyOperationGroup(operations);
2148                     // Not required if undoable.
2149                     //MusEGlobal::song->update(SC_TRACK_REC_MONITOR);
2150                 }
2151             }
2152             break;
2153         }
2154 
2155         case COL_RECORD:
2156         {
2157             if (!t->canRecord())
2158                 break;
2159 
2160             bool val = !(t->recordFlag());
2161 
2162             if (button == Qt::LeftButton) {
2163                 if (t->type() == MusECore::Track::AUDIO_OUTPUT)
2164                 {
2165                     if (val && !t->recordFlag())
2166                         MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)t);
2167 
2168                     break;
2169                 }
2170 
2171                 // apply to selected tracks
2172                 if (t->selected() && tracks->countSelected() > 1) {
2173                     MusECore::Undo operations;
2174                     MusECore::TrackList* tl = MusEGlobal::song->tracks();
2175                     for (const auto tit : *tl)
2176                     {
2177                         if (tit->selected() && tit->canRecord() && tit->type() != MusECore::Track::AUDIO_OUTPUT)
2178                             MusEGlobal::song->setRecordFlag(tit, val, &operations);
2179                     }
2180                     if (!operations.empty())
2181                         MusEGlobal::song->applyOperationGroup(operations);
2182                 }
2183                 else {
2184                     // This is a minor operation easily manually undoable. Let's not clog the undo list with it.
2185                     if (!t->setRecordFlag1(val))
2186                         break;
2187                     MusECore::PendingOperationList operations;
2188                     operations.add(MusECore::PendingOperationItem(t, val, MusECore::PendingOperationItem::SetTrackRecord));
2189                     MusEGlobal::audio->msgExecutePendingOperations(operations, true);
2190                 }
2191             }
2192             else if (button == Qt::RightButton)
2193             {
2194                 // enable or disable ALL tracks of this type
2195                 // This is a major operation not easily manually undoable. Let's make it undoable.
2196                 MusECore::Undo operations;
2197                 if (!t->isMidiTrack()) {
2198                     if (t->type() == MusECore::Track::AUDIO_OUTPUT) {
2199                         return;
2200                     }
2201                     MusECore::WaveTrackList* wtl = MusEGlobal::song->waves();
2202                     foreach (MusECore::WaveTrack *wt, *wtl) {
2203                         MusEGlobal::song->setRecordFlag(wt, val, &operations);
2204                     }
2205                 }
2206                 else {
2207                     MusECore::MidiTrackList* mtl = MusEGlobal::song->midis();
2208                     foreach (MusECore::MidiTrack *mt, *mtl) {
2209                         if (mt->type() == t->type())
2210                             MusEGlobal::song->setRecordFlag(mt, val, &operations);
2211                     }
2212                 }
2213                 if (!operations.empty())
2214                 {
2215                     MusEGlobal::song->applyOperationGroup(operations);
2216                     // Not required if undoable.
2217                     //MusEGlobal::song->update(SC_RECFLAG | SC_TRACK_REC_MONITOR);
2218                 }
2219             }
2220             break;
2221         }
2222 
2223         case COL_NONE:
2224             mode = START_DRAG;
2225             break;
2226 
2227         case COL_CLASS:
2228         {
2229             if (button == Qt::RightButton) {
2230                 if (t->isMidiTrack() || t->isSynthTrack())
2231                     showMidiClassPopupMenu(t, x, t->y() - ypos);
2232                 else if (t->type() == MusECore::Track::AUDIO_OUTPUT)
2233                     showAudioOutPopupMenu(t, x, t->y() - ypos);
2234             }
2235 
2236             break;
2237         }
2238 
2239         case COL_OPORT:
2240         {
2241             if (button == Qt::RightButton) {
2242                 if (t->isSynthTrack())
2243                     showMidiClassPopupMenu(t, x, t->y() - ypos);
2244                 else if (t->isMidiTrack())
2245                     MusEGui::midiPortsPopupMenu(t, x, t->y() - ypos, ctrl, this);
2246             }
2247 
2248             break;
2249         }
2250 
2251         case COL_MUTE:
2252         {
2253             bool turnOff = (button == Qt::RightButton) || shift;
2254             bool state = turnOff ? !t->off() : !t->mute();
2255 
2256             if (((t->selected() && tracks->countSelected() > 1) || ctrl) && t->type() != MusECore::Track::AUDIO_OUTPUT)
2257             {
2258                 // These are major operations not easily manually undoable. Let's make them undoable.
2259                 MusECore::Undo operations;
2260                 if (t->selected() && tracks->countSelected() > 1) // toggle all selected tracks
2261                 {
2262                     for (const auto it : *tracks) {
2263                         if (it->selected() && it->type() != MusECore::Track::AUDIO_OUTPUT)
2264                             setMute(operations, it, turnOff, state);
2265                     }
2266                 }
2267                 else if (ctrl) // toggle ALL tracks
2268                 {
2269                     for (const auto it : *tracks) {
2270                         if (it->type() != MusECore::Track::AUDIO_OUTPUT)
2271                             setMute(operations, it, turnOff, state);
2272                     }
2273                 }
2274                 if (!operations.empty())
2275                 {
2276                     MusEGlobal::song->applyOperationGroup(operations);
2277                     // Not required if undoable.
2278                     //MusEGlobal::song->update(SC_MUTE);
2279                 }
2280             }
2281             else { // toggle the clicked track
2282                 // This is a minor operation easily manually undoable. Let's not clog the undo list with it.
2283                 MusECore::PendingOperationList operations;
2284                 if (turnOff)
2285                     operations.add(MusECore::PendingOperationItem(t, !t->off(), MusECore::PendingOperationItem::SetTrackOff));
2286                 else if (t->off())
2287                     operations.add(MusECore::PendingOperationItem(t, false, MusECore::PendingOperationItem::SetTrackOff));
2288                 else
2289                     operations.add(MusECore::PendingOperationItem(t, !t->mute(), MusECore::PendingOperationItem::SetTrackMute));
2290 
2291                 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
2292             }
2293 
2294             break;
2295         }
2296 
2297         case COL_SOLO:
2298         {
2299             bool state = !t->solo();
2300 
2301             if (((t->selected() && tracks->countSelected() > 1) || ctrl) && t->type() != MusECore::Track::AUDIO_OUTPUT)
2302             {
2303                 // These are major operations not easily manually undoable. Let's make them undoable.
2304                 MusECore::Undo operations;
2305                 if (t->selected() && tracks->countSelected() > 1) // toggle all selected tracks
2306                 {
2307                     for (const auto it : *tracks) {
2308                         if (it->selected() && it->type() != MusECore::Track::AUDIO_OUTPUT)
2309                             operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackSolo, it, state));
2310                     }
2311                 }
2312                 else if (ctrl) // toggle ALL tracks
2313                 {
2314                     for (const auto it : *tracks) {
2315                         if (it->type() != MusECore::Track::AUDIO_OUTPUT)
2316                             operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackSolo, it, state));
2317                     }
2318                 }
2319                 if(!operations.empty())
2320                 {
2321                     MusEGlobal::song->applyOperationGroup(operations);
2322                     // Not required if undoable.
2323                     //MusEGlobal::song->update(SC_SOLO);
2324                 }
2325             }
2326             else // toggle the clicked track
2327             {
2328                 // This is a minor operation easily manually undoable. Let's not clog the undo list with it.
2329                 MusECore::PendingOperationList operations;
2330                 operations.add(MusECore::PendingOperationItem(t, state, MusECore::PendingOperationItem::SetTrackSolo));
2331                 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
2332             }
2333             break;
2334         }
2335 
2336         case COL_NAME:
2337         {
2338             if (button == Qt::RightButton) {
2339                 mode = NORMAL;
2340                 QMenu* p = new QMenu;
2341                 // Leave room for normal track IDs - base these at AUDIO_SOFTSYNTH.
2342                 int selCnt = MusEGlobal::song->countSelectedTracks();
2343 
2344                 QAction *a;
2345                 a = p->addAction(*duplSelTrackSVGIcon, tr("Duplicate Track"));
2346                 a->setData(1005);
2347                 a = p->addAction(*minusSVGIcon, tr("Delete Track"));
2348                 a->setData(1001);
2349 
2350                 if (selCnt > 1){
2351                     p->addSeparator();
2352                     a = p->addAction(*duplSelTracksSVGIcon, tr("Duplicate Selected"));
2353                     a->setData(1004);
2354                     a->setShortcut(shortcuts[SHRT_DUPLICATE_TRACK].key);
2355                     a = p->addAction(*delSelTracksSVGIcon, tr("Delete Selected"));
2356                     a->setData(1003);
2357                 }
2358 
2359                 if (selCnt > 0) {
2360                     p->addSeparator();
2361                     a = p->addAction(tr("Move Selected Up"));
2362                     a->setData(1006);
2363                     a->setShortcut(shortcuts[SHRT_MOVEUP_TRACK].key);
2364                     a = p->addAction(tr("Move Selected Down"));
2365                     a->setData(1007);
2366                     a->setShortcut(shortcuts[SHRT_MOVEDOWN_TRACK].key);
2367                     a = p->addAction(tr("Move Selected to Top"));
2368                     a->setData(1008);
2369                     a->setShortcut(shortcuts[SHRT_MOVETOP_TRACK].key);
2370                     a = p->addAction(*dummySVGIcon, tr("Move Selected to Bottom"));
2371                     a->setData(1009);
2372                     a->setShortcut(shortcuts[SHRT_MOVEBOTTOM_TRACK].key);
2373                 }
2374 
2375                 p->addSeparator();
2376                 a = p->addAction(tr("Track Comment..."));
2377                 a->setData(1002);
2378 
2379                 p->addSeparator();
2380                 a = p->addAction(tr("Set Track Color..."));
2381                 a->setData(1020);
2382                 a = p->addAction(tr("Reset Track Color to Default"));
2383                 a->setData(1021);
2384                 p->addSeparator();
2385 
2386                 if (t->type()==MusECore::Track::DRUM)
2387                 {
2388                     a=p->addAction(tr("Save Track's Drumlist"));
2389                     a->setData(1010);
2390                     a->setEnabled(!static_cast<MusECore::MidiTrack*>(t)->workingDrumMap()->empty());
2391                     a=p->addAction(tr("Load Track's Drumlist"));
2392                     a->setData(1012);
2393                     a=p->addAction(tr("Reset Track's Drumlist"));
2394                     a->setData(1013);
2395                     a->setEnabled(!static_cast<MusECore::MidiTrack*>(t)->workingDrumMap()->empty());
2396                     a=p->addAction(tr("Reset Track's Drumlist Ordering"));
2397                     a->setData(1016);
2398                     a->setEnabled(!((MusECore::MidiTrack*)t)->drummap_ordering_tied_to_patch());
2399                     a=p->addAction(tr("Copy Track's Drumlist to All Selected Tracks"));
2400                     a->setData(1014);
2401                     a->setEnabled(!static_cast<MusECore::MidiTrack*>(t)->workingDrumMap()->empty());
2402                     // 1016 is occupied.
2403                     p->addSeparator();
2404                 }
2405                 insertTrackMenu->setTitle(tr("Insert Track"));
2406                 p->addMenu(insertTrackMenu);
2407 
2408                 QAction* act = p->exec(ev->globalPos(), nullptr);
2409                 if (act) {
2410                     //fprintf(stderr, "TList::mousePressEvent act:%p\n", act);
2411                     int n = act->data().toInt();
2412                     if(n >= 1000 && n < MENU_ADD_SYNTH_ID_BASE)
2413                     {
2414                         //fprintf(stderr, "   n:%d\n", n);
2415                         switch (n) {
2416                         case 1001:     // delete track
2417                             MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(t), t));
2418                             break;
2419                         case 1003:     // delete track(s)
2420                             MusEGlobal::audio->msgRemoveTracks();
2421                             break;
2422                         case 1002:     // show track comment
2423                         {
2424                             TrackComment* tc = new TrackComment(t, nullptr);
2425                             tc->show();
2426                         }
2427                             break;
2428                         case 1004:
2429                             MusEGlobal::song->duplicateTracks();
2430                             break;
2431                         case 1005:
2432                             MusEGlobal::song->duplicateTracks(t);
2433                             break;
2434                         case 1006:
2435                             moveSelectedTracks(true, false);
2436                             break;
2437                         case 1007:
2438                             moveSelectedTracks(false, false);
2439                             break;
2440                         case 1008:
2441                             moveSelectedTracks(true, true);
2442                             break;
2443                         case 1009:
2444                             moveSelectedTracks(false, true);
2445                             break;
2446                         case 1020:
2447                         {
2448                             QColor c = QColorDialog::getColor(t->color());
2449                             if (c.isValid()) {
2450                                 if (t->selected())
2451                                     for (const auto& it : *tracks) {
2452                                         if (it->selected())
2453                                             it->setColor(c);
2454                                     }
2455                                 else
2456                                     t->setColor(c);
2457                                 MusEGlobal::song->update(SC_TRACK_MODIFIED);
2458                             }
2459                         }
2460                             break;
2461                         case 1021:
2462                         {
2463                             if (t->selected())
2464                                 for (const auto& it : *tracks) {
2465                                     if (it->selected())
2466                                         it->resetColor();
2467                                 }
2468                             else
2469                                 t->resetColor();
2470                             MusEGlobal::song->update(SC_TRACK_MODIFIED);
2471                         }
2472                             break;
2473 
2474                         case 1010:
2475                             saveTrackDrummap((MusECore::MidiTrack*)t, true);
2476                             break;
2477 
2478                         case 1012:
2479                             loadTrackDrummap((MusECore::MidiTrack*)t);
2480                             break;
2481 
2482                         case 1013:
2483                             if (QMessageBox::warning(this, tr("Drum map"),
2484                                                      tr("Reset the track's drum map with instrument defaults?"),
2485                                                      QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok)
2486                             {
2487                                 // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the
2488                                 //  original lists will be deleted, in the operation following.
2489                                 MusECore::PendingOperationList operations;
2490                                 // Completely blank replacement list.
2491                                 MusECore::WorkingDrumMapPatchList* new_wdmpl = new MusECore::WorkingDrumMapPatchList();
2492                                 MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation;
2493                                 dmop->_isInstrumentMod = false; // Not instrument operation.
2494                                 dmop->_workingItemPatchList = new_wdmpl;
2495                                 dmop->_track = static_cast<MusECore::MidiTrack*>(t);
2496                                 operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList));
2497                                 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
2498                             }
2499                             break;
2500 
2501                         case 1016:
2502                             if (QMessageBox::warning(this, tr("Drum map"),
2503                                                      tr("Reset the track's drum map ordering?"),
2504                                                      QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok)
2505                             {
2506                                 ((MusECore::MidiTrack*)t)->set_drummap_ordering_tied_to_patch(true);
2507                                 MusEGlobal::song->update(SC_DRUMMAP);
2508                             }
2509                             break;
2510 
2511                         case 1014:
2512                             copyTrackDrummap((MusECore::MidiTrack*)t, true);
2513                             break;
2514 
2515                         default:
2516                             printf("action %d\n", n);
2517                             break;
2518                         }
2519                     }
2520                     else
2521                     {
2522                         t = MusEGlobal::song->addNewTrack(act, t);  // Let addNewTrack handle it. Insert before clicked-on track 't'.
2523                         //fprintf(stderr, "   addNewTrack: track:%p\n", t);
2524                         if(t)
2525                         {
2526                             MusEGlobal::song->selectAllTracks(false);
2527                             t->setSelected(true);
2528                             MusEGlobal::song->update(SC_TRACK_SELECTION);
2529                             adjustScrollbar();
2530                         }
2531                     }
2532                 }
2533                 delete p;
2534             }
2535             break;
2536         }
2537 
2538 //        case COL_TIMELOCK:
2539 //        {
2540 //            if(!t->isMidiTrack())
2541 //            {
2542 //                mode = START_DRAG;  // Allow a track drag to start.
2543 //                break;
2544 //            }
2545 //            t->setLocked(!t->locked());
2546 
2547 //            break;
2548 //        }
2549 
2550         case COL_OCHANNEL:
2551         {
2552             int delta = 0;
2553             if (button == Qt::MidButton || (button == Qt::RightButton && shift))
2554                 delta = -1;
2555             else if (button == Qt::RightButton)
2556                 delta = 1;
2557 
2558             setTrackChannel(t, true, 0, delta, ctrl);
2559 
2560             break;
2561         }
2562 
2563         default:
2564         {
2565             mode = START_DRAG;
2566             if (col>=COL_CUSTOM_MIDICTRL_OFFSET && t->isMidiTrack())
2567             {
2568                 if (Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos ==
2569                         Arranger::custom_col_t::AFFECT_BEGIN)
2570                     ctrl_at_tick=0;
2571                 else
2572                     ctrl_at_tick=MusEGlobal::song->cpos();
2573 
2574                 int delta = 0;
2575                 if (button == Qt::RightButton)
2576                     delta = 1;
2577                 else if (button == Qt::MidButton)
2578                     delta = -1;
2579 
2580                 if (delta!=0)
2581                 {
2582                     MusECore::MidiTrack* mt = dynamic_cast<MusECore::MidiTrack*>(t);
2583                     if (mt == nullptr)
2584                         break;
2585 
2586                     int ctrl_num = Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].ctrl;
2587 
2588                     MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()];
2589                     const int chan = mt->outChannel();
2590                     MusECore::MidiController* mctl = mp->midiController(ctrl_num, chan);
2591 
2592                     int minval=mctl->minVal()+mctl->bias();
2593                     int maxval=mctl->maxVal()+mctl->bias();
2594 
2595                     int val = mt->getControllerChangeAtTick(0,ctrl_num);
2596                     int oldval=val;
2597 
2598                     if (ctrl_num!=MusECore::CTRL_PROGRAM)
2599                     {
2600                         val += delta;
2601                         if(val > maxval)
2602                             val = maxval;
2603                         if(val < minval-1) // "-1" because of "off"
2604                             val = minval-1;
2605                     }
2606                     else
2607                     {
2608                         MusECore::MidiInstrument* instr = mp->instrument();
2609                         if (delta>0) val=instr->getNextPatch(mt->outChannel(), val, false);
2610                         else if (delta<0) val=instr->getPrevPatch(mt->outChannel(), val, false);
2611                     }
2612 
2613                     if (val != oldval)
2614                     {
2615                         if (val!=minval-1)
2616                         {
2617                             record_controller_change_and_maybe_send(ctrl_at_tick, ctrl_num, val, mt);
2618                         }
2619                         else
2620                         {
2621                             MusECore::Undo operations;
2622                             for (MusECore::iPart p = mt->parts()->begin(); p!=mt->parts()->end(); p++)
2623                             {
2624                                 if (p->second->tick()==0)
2625                                 {
2626                                     for (MusECore::ciEvent ev=p->second->events().begin(); ev!=p->second->events().end(); ev++)
2627                                     {
2628                                         if (ev->second.tick()!=0) break;
2629                                         else if (ev->second.type()==MusECore::Controller && ev->second.dataA()==ctrl_num)
2630                                         {
2631                                             using MusECore::UndoOp;
2632                                             operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, p->second, false, false));
2633                                             break;
2634                                         }
2635                                     }
2636                                 }
2637                             }
2638                             MusEGlobal::song->applyOperationGroup(operations);
2639                         }
2640                     }
2641                 }
2642                 else // if (delta==0)
2643                 {
2644                     ctrl_num=Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].ctrl;
2645 
2646                     if (ctrl_num==MusECore::CTRL_PROGRAM)
2647                     {
2648                         editTrack=t;
2649 
2650                         MusECore::MidiTrack* mt=(MusECore::MidiTrack*)t;
2651                         MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()];
2652                         MusECore::MidiInstrument* instr = mp->instrument();
2653 
2654                         PopupMenu* pup = new PopupMenu(true);
2655                         instr->populatePatchPopup(pup, mt->outChannel(), mt->isDrumTrack());
2656 
2657                         if(pup->actions().count() == 0)
2658                         {
2659                             delete pup;
2660                             return;
2661                         }
2662 
2663                         connect(pup, SIGNAL(triggered(QAction*)), SLOT(instrPopupActivated(QAction*)));
2664 
2665                         QAction *act = pup->exec(ev->globalPos());
2666                         if(act)
2667                         {
2668                             int val = act->data().toInt();
2669                             if(val != -1)
2670                                 record_controller_change_and_maybe_send(ctrl_at_tick, MusECore::CTRL_PROGRAM, val, mt);
2671                         }
2672 
2673                         delete pup;
2674                     }
2675                 }
2676             }
2677         }
2678         } //end of "switch"
2679 
2680     }
2681 
2682     redraw();
2683 }
2684 
addAutoMenuAction(PopupMenu * p,const MusECore::CtrlList * cl)2685 void TList::addAutoMenuAction(PopupMenu* p, const MusECore::CtrlList *cl) {
2686     QAction *act = p->addAction(cl->name());
2687     act->setCheckable(true);
2688     act->setChecked(cl->isVisible());
2689 
2690     QPixmap pix(10, 10);
2691     QPainter qp(&pix);
2692     qp.fillRect(0,0,10,10, cl->color());
2693     if (cl->size() > 0) {
2694         if (cl->color() == Qt::black)
2695             qp.fillRect(2, 2, 6, 6, Qt::gray);
2696         else
2697             qp.fillRect(2, 2, 6, 6, Qt::black);
2698     }
2699     QIcon icon(pix);
2700     act->setIcon(icon);
2701 
2702     int ctrl = cl->id();
2703     int data = ctrl<<8; // shift 8 bits
2704     data += 150; // illegal color > 100
2705     act->setData(data);
2706     PopupMenu *m = colorMenu(cl->color(), cl->id(), p);
2707     act->setMenu(m);
2708 }
2709 
outputAutoMenuSorted(PopupMenu * p,QList<const MusECore::CtrlList * > & tmpList)2710 void TList::outputAutoMenuSorted(PopupMenu* p, QList<const MusECore::CtrlList*> &tmpList) {
2711 
2712     if (!tmpList.isEmpty()) {
2713 
2714         std::sort(tmpList.begin(), tmpList.end(),
2715                   [](const MusECore::CtrlList* a, const MusECore::CtrlList* b) -> bool { return a->name() < b->name(); });
2716 
2717         for (const auto& it : tmpList)
2718             addAutoMenuAction(p, it);
2719 
2720         tmpList.clear();
2721     }
2722 }
2723 
setMute(MusECore::Undo & operations,MusECore::Track * t,bool turnOff,bool state)2724     void TList::setMute(MusECore::Undo& operations, MusECore::Track *t, bool turnOff, bool state)
2725 {
2726     if (turnOff)
2727         operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackOff, t, state));
2728     else if (t->off())
2729         operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackOff, t, false));
2730     else
2731         operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackMute, t, state));
2732 }
2733 
loadTrackDrummap(MusECore::MidiTrack * t,const char * fn_)2734 void TList::loadTrackDrummap(MusECore::MidiTrack* t, const char* fn_)
2735 {
2736     QString fn;
2737 
2738     if (fn_==nullptr)
2739         fn=MusEGui::getOpenFileName("drummaps", MusEGlobal::drum_map_file_pattern,
2740                                     this, tr("Muse: Load Track's Drum Map"), 0);
2741     else
2742         fn=QString(fn_);
2743 
2744     if (fn.isEmpty())
2745     {
2746         printf("ERROR: TList::loadTrackDrummap(): empty filename\n");
2747         return;
2748     }
2749 
2750     bool popenFlag;
2751     FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "r", popenFlag, true);
2752     if (f == 0)
2753     {
2754         printf("ERROR: TList::loadTrackDrummap() could not open file %s!\n", fn.toLatin1().data());
2755         return;
2756     }
2757 
2758     MusECore::Xml xml(f);
2759     loadTrackDrummapFromXML(t, xml);
2760 
2761     if (popenFlag)
2762         pclose(f);
2763     else
2764         fclose(f);
2765 
2766     MusEGlobal::song->update(SC_DRUMMAP);
2767 }
2768 
loadTrackDrummapFromXML(MusECore::MidiTrack * t,MusECore::Xml & xml)2769 void TList::loadTrackDrummapFromXML(MusECore::MidiTrack *t, MusECore::Xml &xml)
2770 {
2771     MusECore::PendingOperationList operations;
2772     MusECore::WorkingDrumMapPatchList* wdmpl = 0;
2773 
2774     for (;;) {
2775         MusECore::Xml::Token token = xml.parse();
2776         const QString& tag = xml.s1();
2777         switch (token) {
2778         case MusECore::Xml::Error:
2779         case MusECore::Xml::End:
2780             if(wdmpl)
2781                 delete wdmpl;
2782             return;
2783         case MusECore::Xml::TagStart:
2784             if (tag == "muse")
2785             {
2786             }
2787             else if (tag == "our_drummap" ||  // OBSOLETE. Support old files.
2788                      tag == "drummap" ||      // OBSOLETE. Support old files.
2789                      tag == "drumMapPatch")
2790             {
2791                 if(!wdmpl)
2792                     wdmpl = new MusECore::WorkingDrumMapPatchList();
2793                 // false = Do not fill in unused items.
2794                 wdmpl->read(xml, false);
2795             }
2796 
2797             else
2798                 xml.unknown("TList::loadTrackDrummap");
2799             break;
2800         case MusECore::Xml::Attribut:
2801             break;
2802         case MusECore::Xml::TagEnd:
2803             if (tag == "muse")
2804             {
2805                 if(wdmpl)
2806                 {
2807                     // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the
2808                     //  original lists will be deleted, in the operation following.
2809                     MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation;
2810                     dmop->_isInstrumentMod = false; // Not instrument operation.
2811                     dmop->_workingItemPatchList = wdmpl;
2812                     dmop->_track = t;
2813 
2814                     operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList));
2815                     MusEGlobal::audio->msgExecutePendingOperations(operations, true);
2816                 }
2817                 goto ende;
2818             }
2819         default:
2820             break;
2821         }
2822     }
2823 ende:
2824     return;
2825 }
2826 
saveTrackDrummap(MusECore::MidiTrack * t,bool,const char * fn_)2827 void TList::saveTrackDrummap(MusECore::MidiTrack* t, bool /*full*/, const char* fn_)
2828 {
2829     QString fn;
2830     if (fn_==nullptr)
2831         fn = MusEGui::getSaveFileName(QString("drummaps"), MusEGlobal::drum_map_file_save_pattern,
2832                                       this, tr("MusE: Store Track's Drum Map"));
2833     else
2834         fn = QString(fn_);
2835 
2836     if (fn.isEmpty())
2837         return;
2838 
2839     bool popenFlag;
2840     FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "w", popenFlag, false, true);
2841     if (f == 0)
2842         return;
2843 
2844     MusECore::Xml xml(f);
2845     xml.header();
2846     xml.tag(0, "muse version=\"1.0\"");
2847 
2848     t->workingDrumMap()->write(1, xml);
2849 
2850     xml.tag(0, "/muse");
2851 
2852     if (popenFlag)
2853         pclose(f);
2854     else
2855         fclose(f);
2856 }
2857 
copyTrackDrummap(MusECore::MidiTrack * t,bool)2858 void TList::copyTrackDrummap(MusECore::MidiTrack* t, bool /*full*/)
2859 {
2860     MusECore::PendingOperationList operations;
2861     MusECore::WorkingDrumMapPatchList* new_wdmpl;
2862 
2863     MusECore::WorkingDrumMapPatchList* wdmpl = t->workingDrumMap();
2864     MusECore::MidiTrack* mt;
2865     for(MusECore::iMidiTrack it = MusEGlobal::song->midis()->begin(); it != MusEGlobal::song->midis()->end(); ++it)
2866     {
2867         mt = *it;
2868         if(mt == t || !mt->selected() || mt->type() != MusECore::Track::DRUM)
2869             continue;
2870 
2871         // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the
2872         //  original lists will be deleted, in the operation following.
2873         new_wdmpl = new MusECore::WorkingDrumMapPatchList();
2874         *new_wdmpl = *wdmpl;
2875         MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation;
2876         dmop->_isInstrumentMod = false; // Not instrument operation.
2877         dmop->_workingItemPatchList = new_wdmpl;
2878         dmop->_track = mt;
2879         operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList));
2880     }
2881 
2882     if(!operations.empty())
2883         MusEGlobal::audio->msgExecutePendingOperations(operations, true);
2884 }
2885 
2886 //---------------------------------------------------------
2887 //   selectTrack
2888 //---------------------------------------------------------
selectTrack(MusECore::Track * tr,bool)2889 void TList::selectTrack(MusECore::Track* tr, bool /*deselect*/)
2890 {
2891     MusEGlobal::song->selectAllTracks(false);
2892 
2893     if (tr) {
2894         tr->setSelected(true);
2895 
2896         // rec enable track if expected
2897         MusECore::TrackList recd = getRecEnabledTracks();
2898         if (!MusEGlobal::audio->isRecording() &&
2899                 recd.size() == 1 &&
2900                 MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection
2901             MusEGlobal::song->setRecordFlag((MusECore::Track*)recd.front(),false);
2902             MusEGlobal::song->setRecordFlag(tr,true);
2903         }
2904     }
2905 
2906     // SC_TRACK_SELECTION will cause update anyway, no harm ...
2907     update();
2908     MusEGlobal::song->update(SC_TRACK_SELECTION);
2909 }
2910 
2911 //---------------------------------------------------------
2912 //   selectTrackAbove
2913 //---------------------------------------------------------
selectTrackAbove()2914 void TList::selectTrackAbove()
2915 {
2916     moveSelection(-1);
2917 }
2918 //---------------------------------------------------------
2919 //   selectTrackBelow
2920 //---------------------------------------------------------
selectTrackBelow()2921 void TList::selectTrackBelow()
2922 {
2923     moveSelection(1);
2924 }
2925 
2926 //---------------------------------------------------------
2927 //   mouseMoveEvent
2928 //---------------------------------------------------------
2929 
mouseMoveEvent(QMouseEvent * ev)2930 void TList::mouseMoveEvent(QMouseEvent* ev)
2931 {
2932     if((editor && (editor->isVisible() || editor->hasFocus())) ||
2933             (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) ||
2934             (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus())))
2935     {
2936         ev->accept();
2937         return;
2938     }
2939 
2940     if (ev->buttons() == 0) {
2941 //        if ((((QInputEvent*)ev)->modifiers() | ev->buttons()) == 0) {
2942         int y = ev->y();
2943         int ty = -ypos;
2944         MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2945         MusECore::iTrack it;
2946         for (it = tracks->begin(); it != tracks->end(); ++it) {
2947             int h = (*it)->height();
2948             ty += h;
2949             if (y >= (ty-2)) {
2950                 if ( (*it) == tracks->back() && y >= ty ) { // DELETETHIS and cleanup
2951                     // outside last track don't change to splitVCursor
2952                 }
2953                 else if ( y > (ty+2) ) {
2954                     //printf(" y > (ty+2) \n");
2955                 }
2956                 else {
2957                     if (!resizeFlag) {
2958                         resizeFlag = true;
2959                         setCursor(QCursor(Qt::SplitVCursor));
2960                         MusEGlobal::muse->setStatusBarText(tr("Draw to change the track height. Hold CTRL for all tracks, SHIFT for selected tracks."));
2961                     }
2962                     break;
2963                 }
2964             }
2965         }
2966         if (it == tracks->end() && resizeFlag) {
2967             setCursor(QCursor(Qt::ArrowCursor));
2968             resizeFlag = false;
2969             MusEGlobal::muse->clearStatusBarText();
2970         }
2971         return;
2972     }
2973 
2974     curY      = ev->y();
2975     int delta = curY - startY;
2976     switch (mode) {
2977     case START_DRAG:
2978         if (delta < 0)
2979             delta = -delta;
2980         if (delta <= 2)
2981             break;
2982     {
2983         MusECore::Track* t = y2Track(startY + ypos);
2984         if (t == nullptr)
2985             mode = NORMAL;
2986         else {
2987             mode = DRAG;
2988             dragHeight = t->height();
2989             sTrack     = MusEGlobal::song->tracks()->index(t);
2990             setCursor(QCursor(Qt::SizeVerCursor));
2991             redraw();
2992         }
2993     }
2994         break;
2995     case NORMAL:
2996         break;
2997     case DRAG:
2998         redraw();
2999         break;
3000     case RESIZE:
3001     {
3002         if (sTrack >= 0 && (unsigned) sTrack < MusEGlobal::song->tracks()->size())
3003         {
3004             bool shift = ((QInputEvent*)ev)->modifiers() & Qt::SHIFT;
3005             bool ctrl = ((QInputEvent*)ev)->modifiers() & Qt::CTRL;
3006             if (ctrl | shift) {
3007                 bool done = false;
3008                 for (const auto& it : *MusEGlobal::song->tracks()) {
3009                     if (shift && !it->selected())
3010                         continue;
3011                     int h  = it->height() + delta;
3012                     h = qMax(h, MIN_TRACKHEIGHT);
3013                     it->setHeight(h);
3014                     done = true;
3015                 }
3016                 if (done) {
3017                     startY = curY;
3018                     update();
3019                     MusEGlobal::song->update(SC_TRACK_RESIZED);
3020                 }
3021             }
3022             else {
3023                 MusECore::Track* t = MusEGlobal::song->tracks()->index(sTrack);
3024                 if (t) {
3025                     int h  = t->height() + delta;
3026                     startY = curY;
3027                     if (h < MIN_TRACKHEIGHT)
3028                         h = MIN_TRACKHEIGHT;
3029                     t->setHeight(h);
3030                     update();
3031                     MusEGlobal::song->update(SC_TRACK_RESIZED);
3032                 }
3033             }
3034         }
3035     }
3036         break;
3037     }
3038 }
3039 
moveSelectedTracks(bool up,bool full)3040 void TList::moveSelectedTracks(bool up, bool full) {
3041 
3042     // Move only visible? But the other track functions also work on hidden tracks
3043     // (duplicate, delete...), so this seems more consistent (kybos)
3044 
3045     MusECore::TrackList *tracks = MusEGlobal::song->tracks();
3046 
3047     if (tracks->size() < 2 || tracks->countSelected() == 0
3048             || (!up && tracks->back()->selected())
3049             || (up && tracks->front()->selected()))
3050         return;
3051 
3052     if (MusEGlobal::audio->isPlaying()) {
3053         MusEGlobal::muse->setStatusBarText(tr("Operation not available while playing"), 5000);
3054         return;
3055     }
3056 
3057     MusECore::TrackList tracksTmp = *tracks;
3058 
3059     unsigned delta = 1;
3060     bool deltaFound = false;
3061 
3062     if (up) {
3063         for (const auto it : *tracks) {
3064             if (!it->selected())
3065                 continue;
3066 
3067             if (full & !deltaFound) {
3068                 delta = static_cast<unsigned>(tracks->index(it));
3069                 deltaFound = true;
3070             }
3071 
3072             unsigned sidx = static_cast<unsigned>(tracks->index(it));
3073             unsigned i = delta;
3074             while (i--) {
3075                 std::swap(tracksTmp[sidx], tracksTmp[sidx-1]);
3076                 sidx--;
3077             }
3078         }
3079     } else {
3080         for (auto it = tracks->rbegin(); it != tracks->rend(); it++) {
3081             if (!(*it)->selected())
3082                 continue;
3083 
3084             unsigned sidx = static_cast<unsigned>(tracks->index(*it));
3085 
3086             if (full && !deltaFound) {
3087                 delta = static_cast<unsigned>(tracks->size()) - sidx - 1;
3088                 deltaFound = true;
3089             }
3090 
3091             unsigned i = delta;
3092             while (i--) {
3093                 std::swap(tracksTmp[sidx], tracksTmp[sidx+1]);
3094                 sidx++;
3095             }
3096         }
3097     }
3098 
3099     MusEGlobal::song->tracks()->swap(tracksTmp);
3100     MusEGlobal::song->update(SC_TRACK_MOVED);
3101 }
3102 
3103 //---------------------------------------------------------
3104 //   mouseReleaseEvent
3105 //---------------------------------------------------------
3106 
mouseReleaseEvent(QMouseEvent * ev)3107 void TList::mouseReleaseEvent(QMouseEvent* ev)
3108 {
3109     if((editor && (editor->isVisible() || editor->hasFocus())) ||
3110             (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) ||
3111             (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus())))
3112     {
3113         ev->accept();
3114         return;
3115     }
3116 
3117     if (mode == DRAG) {
3118         MusECore::Track* t = y2Track(ev->y() + ypos);
3119         if (t) {
3120             int dTrack = MusEGlobal::song->tracks()->index(t);
3121             if (sTrack >= 0 && dTrack >= 0)   // sanity check
3122             {
3123                 const int tracks_sz = MusEGlobal::song->tracks()->size();
3124                 if (sTrack < tracks_sz && dTrack < tracks_sz)   // sanity check
3125                     MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::MoveTrack, sTrack, dTrack));
3126             }
3127 
3128             // inconsistent, better keep the original aux knob order?
3129             // - if aux track is moved in the mixer, the aux knobs are also NOT adjusted
3130             // - if the drag action is undone, the aux knobs remain in the wrong order
3131 
3132 //            MusECore::TrackList *tracks = MusEGlobal::song->tracks();
3133 //            if ( tracks->at(dTrack)->type() == MusECore::Track::AUDIO_AUX) {
3134 
3135 //                MusECore::AuxList auxCopy; // = *MusEGlobal::song->auxs();
3136 //                //MusEGlobal::song->auxs()->clear();
3137 //                std::vector<int> oldAuxIndex;
3138 
3139 //                for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) {
3140 //                    if ((*t)->type() == MusECore::Track::AUDIO_AUX) {
3141 //                        MusECore::AudioAux *ax = (MusECore::AudioAux*)*t;
3142 //                        auxCopy.push_back(ax);
3143 //                        oldAuxIndex.push_back(MusEGlobal::song->auxs()->index(ax)); // store old index
3144 //                    }
3145 //                }
3146 //                // loop through all tracks and set the levels for all tracks
3147 //                for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) {
3148 //                    MusECore::AudioTrack *trk = (MusECore::AudioTrack*)*t;
3149 
3150 //                    if (!trk->isMidiTrack() && trk->hasAuxSend())
3151 //                    {
3152 //                        std::vector<double> oldAuxValue;
3153 //                        for (unsigned i = 0 ; i < auxCopy.size(); i++)
3154 //                            oldAuxValue.push_back(trk->auxSend(i));
3155 //                        for (unsigned i = 0 ; i < auxCopy.size(); i++)
3156 //                            trk->setAuxSend(i, oldAuxValue[oldAuxIndex[i]] );
3157 //                    }
3158 //                    MusEGlobal::song->auxs()->clear();
3159 //                    for (MusECore::iAudioAux t = auxCopy.begin(); t != auxCopy.end(); ++t) {
3160 //                        MusEGlobal::song->auxs()->push_back(*t);
3161 //                    }
3162 //                }
3163 
3164 //                MusEGlobal::song->update(SC_EVERYTHING);
3165 
3166 //            }
3167         }
3168     }
3169     if (mode != NORMAL) {
3170         mode = NORMAL;
3171         setCursor(QCursor(Qt::ArrowCursor));
3172         redraw();
3173     }
3174     if (editTrack && editor && editor->isVisible())
3175         editor->setFocus();
3176     //else // DELETETHIS or add the same for ctrl_edit!
3177     //if (editTrack && chan_edit && chan_edit->isVisible())  // p4.0.46 DELETETHIS?
3178     //      chan_edit->setFocus();
3179     adjustScrollbar();
3180 }
3181 
3182 //---------------------------------------------------------
3183 //   wheelEvent
3184 //---------------------------------------------------------
3185 
wheelEvent(QWheelEvent * ev)3186 void TList::wheelEvent(QWheelEvent* ev)
3187 {
3188     emit redirectWheelEvent(ev);
3189 }
3190 
3191 
sizeHint() const3192 QSize TList::sizeHint() const { return QSize(250, 100); }
minimumSizeHint() const3193 QSize TList::minimumSizeHint() const { return QSize(100, 100); }
3194 
3195 //---------------------------------------------------------
3196 //   setYPos
3197 //---------------------------------------------------------
3198 
setYPos(int y)3199 void TList::setYPos(int y)
3200 {
3201     int delta  = ypos - y;         // -  -> shift up
3202     ypos  = y;
3203 
3204     scroll(0, delta);
3205 }
3206 
changeTrackToType(MusECore::Track * t,MusECore::Track::TrackType trackType)3207 void TList::changeTrackToType(MusECore::Track *t, MusECore::Track::TrackType trackType)
3208 {
3209     // MIDI -> NEW_DRUM or vice versa. added by flo.
3210     MusEGlobal::audio->msgIdle(true);
3211     t->setType(trackType);
3212     MusEGlobal::audio->msgIdle(false);
3213     MusEGlobal::song->update(SC_TRACK_MODIFIED);
3214 }
3215 
instrPopupActivated(QAction * act)3216 void TList::instrPopupActivated(QAction* act)
3217 {
3218     MusECore::MidiTrack* mt = dynamic_cast<MusECore::MidiTrack*>(editTrack);
3219     if(act && mt)
3220     {
3221         int val = act->data().toInt();
3222         if(val != -1)
3223             record_controller_change_and_maybe_send(ctrl_at_tick, MusECore::CTRL_PROGRAM, val, mt);
3224     }
3225 }
3226 
3227 
setHeader(Header * h)3228 void TList::setHeader(Header* h)
3229 {
3230     header=h;
3231     redraw();
3232 }
3233 
populateAddTrack()3234 void TList::populateAddTrack()
3235 {
3236     addTrackMenu = new QMenu;
3237     MusEGui::populateAddTrack(addTrackMenu, false, false, true);
3238 
3239     insertTrackMenu = new QMenu;
3240     MusEGui::populateAddTrack(insertTrackMenu, false, true);
3241 }
3242 
3243 } // namespace MusEGui
3244 
3245