1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //    $Id: arranger.cpp,v 1.33.2.21 2009/11/17 22:08:22 terminator356 Exp $
5 //  (C) Copyright 1999-2004 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 "config.h"
25 
26 #include <stdio.h>
27 #include <limits.h>
28 #include "muse_math.h"
29 
30 #include <QComboBox>
31 #include <QGridLayout>
32 #include <QKeyEvent>
33 #include <QLabel>
34 #include <QList>
35 #include <QMainWindow>
36 #include <QScrollBar>
37 #include <QToolBar>
38 #include <QVBoxLayout>
39 #include <QWheelEvent>
40 #include <QPainter>
41 #include <QCursor>
42 #include <QPoint>
43 #include <QRect>
44 #include <QSettings>
45 
46 #include "arranger.h"
47 #include "song.h"
48 #include "app.h"
49 #include "midiport.h"
50 #include "mididev.h"
51 #include "utils.h"
52 #include "globals.h"
53 #include "globaldefs.h"
54 #include "icons.h"
55 #include "utils.h"
56 #include "widget_stack.h"
57 #include "audio.h"
58 #include "event.h"
59 #include "midiseq.h"
60 #include "midictrl.h"
61 #include "mpevent.h"
62 #include "gconfig.h"
63 #include "mixer/astrip.h"
64 #include "mixer/mstrip.h"
65 #include "shortcuts.h"
66 #include "ttoolbutton.h"
67 
68 // Forwards from header:
69 #include <QKeyEvent>
70 #include <QPoint>
71 #include <QComboBox>
72 #include <QScrollBar>
73 #include <QVBoxLayout>
74 #include <QHBoxLayout>
75 #include <QScrollArea>
76 #include <QToolButton>
77 #include "track.h"
78 #include "part.h"
79 #include "xml.h"
80 #include "rasterizer.h"
81 #include "lcombo.h"
82 #include "mtscale.h"
83 #include "arrangerview.h"
84 #include "astrip.h"
85 #include "header.h"
86 #include "poslabel.h"
87 #include "scrollscale.h"
88 #include "spinbox.h"
89 #include "splitter.h"
90 #include "trackinfo_layout.h"
91 #include "tlist.h"
92 #include "raster_widgets.h"
93 #include "pcanvas.h"
94 
95 namespace MusEGui {
96 
97 std::vector<Arranger::custom_col_t> Arranger::custom_columns;
98 QByteArray Arranger::header_state;
99 
writeCustomColumns(int level,MusECore::Xml & xml)100 void Arranger::writeCustomColumns(int level, MusECore::Xml& xml)
101 {
102   xml.tag(level++, "custom_columns");
103 
104   for (unsigned i = 0; i < custom_columns.size(); i++)
105   {
106     xml.tag(level++, "column");
107     xml.strTag(level, "name", custom_columns[i].name);
108     xml.intTag(level, "ctrl", custom_columns[i].ctrl);
109     xml.intTag(level, "affected_pos", custom_columns[i].affected_pos);
110     xml.etag(--level, "column");
111   }
112 
113   xml.etag(--level, "custom_columns");
114 }
115 
readCustomColumns(MusECore::Xml & xml)116 void Arranger::readCustomColumns(MusECore::Xml& xml)
117 {
118       custom_columns.clear();
119 
120       for (;;) {
121             MusECore::Xml::Token token(xml.parse());
122             const QString& tag(xml.s1());
123             switch (token) {
124                   case MusECore::Xml::Error:
125                   case MusECore::Xml::End:
126                         return;
127                   case MusECore::Xml::TagStart:
128                         if (tag == "column")
129                               custom_columns.push_back(readOneCustomColumn(xml));
130                         else
131                               xml.unknown("Arranger::readCustomColumns");
132                         break;
133                   case MusECore::Xml::TagEnd:
134                         if (tag == "custom_columns")
135                         {
136                               return;
137                         }
138                   default:
139                         break;
140                   }
141             }
142 }
143 
readOneCustomColumn(MusECore::Xml & xml)144 Arranger::custom_col_t Arranger::readOneCustomColumn(MusECore::Xml& xml)
145 {
146       custom_col_t temp(0, "-");
147 
148       for (;;) {
149             MusECore::Xml::Token token(xml.parse());
150             const QString& tag(xml.s1());
151             switch (token) {
152                   case MusECore::Xml::Error:
153                   case MusECore::Xml::End:
154                         return temp;
155                   case MusECore::Xml::TagStart:
156                         if (tag == "name")
157                               temp.name=xml.parse1();
158                         else if (tag == "ctrl")
159                               temp.ctrl=xml.parseInt();
160                         else if (tag == "affected_pos")
161                               temp.affected_pos=(custom_col_t::affected_pos_t)xml.parseInt();
162                         else
163                               xml.unknown("Arranger::readOneCustomColumn");
164                         break;
165                   case MusECore::Xml::TagEnd:
166                         if (tag == "column")
167                               return temp;
168                   default:
169                         break;
170                   }
171             }
172       return temp;
173 }
174 
175 //---------------------------------------------------------
176 //   Arranger::setHeaderToolTips
177 //---------------------------------------------------------
178 
setHeaderToolTips()179 void Arranger::setHeaderToolTips()
180 {
181     header->setToolTip(TList::COL_TRACK_IDX,  tr("Track index"));
182     header->setToolTip(TList::COL_INPUT_MONITOR, tr("Input monitor"));
183     header->setToolTip(TList::COL_RECORD,     tr("Recording"));
184     header->setToolTip(TList::COL_MUTE,       tr("Mute/Off indicator"));
185     header->setToolTip(TList::COL_SOLO,       tr("Solo indicator"));
186     header->setToolTip(TList::COL_CLASS,      tr("Track type"));
187     header->setToolTip(TList::COL_NAME,       tr("Track name"));
188     header->setToolTip(TList::COL_OCHANNEL,   tr("Midi output channel number or number of audio channels"));
189     header->setToolTip(TList::COL_OPORT,      tr("Midi output port or synth GUI"));
190 //    header->setToolTip(TList::COL_TIMELOCK,   tr("Time lock"));
191     header->setToolTip(TList::COL_AUTOMATION, tr("Automation parameter selection"));
192     header->setToolTip(TList::COL_CLEF,       tr("Notation clef"));
193 }
194 
195 
196 
197 //---------------------------------------------------------
198 //   Arranger::setHeaderWhatsThis
199 //---------------------------------------------------------
200 
setHeaderWhatsThis()201 void Arranger::setHeaderWhatsThis()
202 {
203     header->setWhatsThis(TList::COL_TRACK_IDX, tr("Track index"));
204     header->setWhatsThis(TList::COL_INPUT_MONITOR, tr("Enable input monitor. Click to toggle.\nPasses input through to output for monitoring.\n"
205                                                       "See also Settings: Automatically Monitor On Record Arm."));
206     header->setWhatsThis(TList::COL_RECORD,   tr("Enable recording. Click to toggle.\n"
207                                                  "See also Settings: Automatically Monitor On Record Arm."));
208     header->setWhatsThis(TList::COL_MUTE,     tr("Mute indicator. Click to toggle.\nRight-click to toggle track on/off.\nMute is designed for rapid, repeated action.\nOn/Off is not!"));
209     header->setWhatsThis(TList::COL_SOLO,     tr("Solo indicator. Click to toggle.\nConnected tracks are also 'phantom' soloed."));
210     header->setWhatsThis(TList::COL_CLASS,    tr("Track type. Right-click to change\n midi and drum track types."));
211     header->setWhatsThis(TList::COL_NAME,     tr("Track name. Double-click to edit.\nRight-click for more options."));
212     header->setWhatsThis(TList::COL_OCHANNEL, tr("Midi/Drum track: Output channel number.\nAudio track: Channels.\nMid/right-click to change."));
213     header->setWhatsThis(TList::COL_OPORT,    tr("Midi/Drum track: Output port.\nSynth track: Right-click to show GUI."));
214 //    header->setWhatsThis(TList::COL_TIMELOCK, tr("Time lock"));
215     header->setWhatsThis(TList::COL_CLEF,     tr("Notation clef. Select this tracks notation clef."));
216 }
217 
218 //---------------------------------------------------------
219 //   Arranger::setHeaderStatusTips
220 //---------------------------------------------------------
221 
setHeaderStatusTips()222 void Arranger::setHeaderStatusTips()
223 {
224     header->setStatusTip(TList::COL_TRACK_IDX, tr("Track index: Double-click to select all tracks (+SHIFT to select all tracks of the same type)."));
225     header->setStatusTip(TList::COL_INPUT_MONITOR, tr("Input monitor: Left click to toggle current/selected, right click for all tracks of same type."));
226     header->setStatusTip(TList::COL_RECORD,   tr("Recording: LMB to toggle current/selected, RMB for all tracks of same type. Audio output: LMB to downmix to a file."));
227     header->setStatusTip(TList::COL_MUTE,     tr("Mute indicator: Left click to mute, right click to switch on/off (+CTRL for all tracks except audio outputs)."));
228     header->setStatusTip(TList::COL_SOLO,     tr("Solo indicator: Click to solo (+CTRL for all tracks except audio outputs). Connected tracks are 'phantom' soloed."));
229     header->setStatusTip(TList::COL_CLASS,    tr("Track type (RMB for context menu): MIDI: Switch track types. Synth: Open GUI. Audio output: Downmix."));
230     header->setStatusTip(TList::COL_NAME,     tr("Track name: Double-click to edit. RMB for context menu."));
231     header->setStatusTip(TList::COL_OCHANNEL, tr("Midi/Drum: Output channel number. Audio: Number of channels. MMB / (CTRL/SHIFT+)RMB / Double-click to change."));
232     header->setStatusTip(TList::COL_OPORT,    tr("Midi/Drum: RMB to set the output port (+CTRL for all tracks of same type). Synth: RMB to show synth GUI."));
233 //    header->setStatusTip(TList::COL_TIMELOCK, tr("Time lock"));
234     header->setStatusTip(TList::COL_AUTOMATION, tr("Automation: RMB to select parameters."));
235     header->setStatusTip(TList::COL_CLEF,     tr("Notation clef: RMB to select this track's notation clef."));
236 }
237 
238 //---------------------------------------------------------
239 //   Arranger
240 //    is the central widget in app
241 //---------------------------------------------------------
242 
Arranger(ArrangerView * parent,const char * name)243 Arranger::Arranger(ArrangerView* parent, const char* name)
244    : QWidget(parent)
245       {
246       setObjectName(name);
247 
248       _canvasXOrigin = DefaultCanvasXOrigin;
249       //_minXMag = -2000;
250       _minXMag = -500;
251       _maxXMag = -5;
252       QList<Rasterizer::Column> rast_cols;
253       rast_cols <<
254         Rasterizer::TripletColumn <<
255         Rasterizer::NormalColumn <<
256         Rasterizer::DottedColumn;
257       _rasterizerModel = new RasterizerModel(
258       MusEGlobal::globalRasterizer, this, 7, rast_cols);
259       _rasterizerModel->setDisplayFormat(RasterizerModel::FractionFormat);
260 
261       _raster = _rasterizerModel->pickRaster(0, RasterizerModel::GotoBar);
262       selected = nullptr;
263       showTrackinfoFlag = true;
264 
265       cursVal = INT_MAX;
266 
267       _parentWin=parent;
268 
269       setFocusPolicy(Qt::NoFocus);
270 
271       //---------------------------------------------------
272       //  ToolBar
273       //    create toolbar in toplevel widget
274       //---------------------------------------------------
275 
276       // NOTICE: Please ensure that any tool bar object names here match the names assigned
277       //          to identical or similar toolbars in class MusE or other TopWin classes.
278       //         This allows MusE::setCurrentMenuSharingTopwin() to do some magic
279       //          to retain the original toolbar layout. If it finds an existing
280       //          toolbar with the same object name, it /replaces/ it using insertToolBar(),
281       //          instead of /appending/ with addToolBar().
282 
283       parent->addToolBarBreak();
284       QToolBar* toolbar = parent->addToolBar(tr("Arranger"));
285       toolbar->setObjectName("ArrangerToolbar");
286 
287       QLabel* label = new QLabel(tr("Cursor"));
288       toolbar->addWidget(label);
289       cursorPos = new PosLabel(nullptr, "PosLabel");
290       cursorPos->setToolTip(tr("Cursor position"));
291       cursorPos->setEnabled(false);
292       toolbar->addWidget(cursorPos);
293 
294       gridOnButton = new QToolButton();
295       gridOnButton->setIcon(*gridOnSVGIcon);
296       gridOnButton->setFocusPolicy(Qt::NoFocus);
297       gridOnButton->setCheckable(true);
298       gridOnButton->setToolTip(tr("Show grid"));
299       gridOnButton->setWhatsThis(tr("Show grid"));
300       toolbar->addWidget(gridOnButton);
301       connect(gridOnButton, &QToolButton::toggled, [this](bool v) { gridOnChanged(v); } );
302 
303       _rasterCombo = new RasterLabelCombo(RasterLabelCombo::TableView, _rasterizerModel, this, "RasterLabelCombo");
304       _rasterCombo->setFocusPolicy(Qt::TabFocus);
305       toolbar->addWidget(_rasterCombo);
306 
307       // Song len
308       label = new QLabel(tr("Bars"));
309       label->setIndent(3);
310       toolbar->addWidget(label);
311 
312       // song length is limited to 10000 bars; the real song len is limited
313       // by overflows in tick computations
314       lenEntry = new SpinBox(1, 10000, 1);
315       lenEntry->setFocusPolicy(Qt::StrongFocus);
316       lenEntry->setValue(MusEGlobal::song->len());
317       lenEntry->setToolTip(tr("Song length - bars"));
318       lenEntry->setWhatsThis(tr("Song length - bars"));
319       toolbar->addWidget(lenEntry);
320       connect(lenEntry, SIGNAL(valueChanged(int)), SLOT(songlenChanged(int)));
321 
322       label = new QLabel(tr("Pitch"));
323       label->setIndent(3);
324       toolbar->addWidget(label);
325 
326       globalPitchSpinBox = new SpinBox(-127, 127, 1);
327       globalPitchSpinBox->setFocusPolicy(Qt::StrongFocus);
328       globalPitchSpinBox->setValue(MusEGlobal::song->globalPitchShift());
329       globalPitchSpinBox->setToolTip(tr("Midi pitch"));
330       globalPitchSpinBox->setWhatsThis(tr("Global midi pitch shift"));
331       toolbar->addWidget(globalPitchSpinBox);
332       connect(globalPitchSpinBox, SIGNAL(valueChanged(int)), SLOT(globalPitchChanged(int)));
333 
334       label = new QLabel(tr("Tempo"));
335       label->setIndent(3);
336       toolbar->addWidget(label);
337 
338       globalTempoSpinBox = new SpinBox(50, 200, 1, toolbar);
339       globalTempoSpinBox->setFocusPolicy(Qt::StrongFocus);
340       globalTempoSpinBox->setSuffix(QString("%"));
341       globalTempoSpinBox->setValue(MusEGlobal::tempomap.globalTempo());
342       globalTempoSpinBox->setToolTip(tr("Midi tempo"));
343       globalTempoSpinBox->setWhatsThis(tr("Midi tempo"));
344       toolbar->addWidget(globalTempoSpinBox);
345       connect(globalTempoSpinBox, SIGNAL(valueChanged(int)), SLOT(globalTempoChanged(int)));
346 
347       QToolButton* tempo50  = new QToolButton();
348       tempo50->setText(QString("50%"));
349       tempo50->setFocusPolicy(Qt::NoFocus);
350       toolbar->addWidget(tempo50);
351       connect(tempo50, SIGNAL(clicked()), SLOT(setTempo50()));
352 
353       QToolButton* tempo100 = new QToolButton();
354       tempo100->setText(tr("N"));
355       tempo100->setFocusPolicy(Qt::NoFocus);
356       toolbar->addWidget(tempo100);
357       connect(tempo100, SIGNAL(clicked()), SLOT(setTempo100()));
358 
359       QToolButton* tempo200 = new QToolButton();
360       tempo200->setText(QString("200%"));
361       tempo200->setFocusPolicy(Qt::NoFocus);
362       toolbar->addWidget(tempo200);
363       connect(tempo200, SIGNAL(clicked()), SLOT(setTempo200()));
364 
365       QVBoxLayout* box  = new QVBoxLayout(this);
366       box->setContentsMargins(0, 0, 0, 0);
367       box->setSpacing(0);
368       box->addWidget(MusECore::hLine(this), Qt::AlignTop);
369 
370       //---------------------------------------------------
371       //  Tracklist
372       //---------------------------------------------------
373 
374       int xscale = -100;
375       int yscale = 1;
376 
377       split  = new Splitter(Qt::Horizontal, this, "split");
378       split->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
379       box->addWidget(split, 1000);
380 
381 
382       trackInfoWidget = new TrackInfoWidget(split);
383       split->setStretchFactor(split->indexOf(trackInfoWidget), 0);
384       QSizePolicy tipolicy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
385       tipolicy.setHorizontalStretch(0);
386       tipolicy.setVerticalStretch(100);
387       trackInfoWidget->setSizePolicy(tipolicy);
388 
389       tracklistScroll = new QScrollArea(split);
390       split->setStretchFactor(split->indexOf(tracklistScroll), 0);
391       QSizePolicy tpolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
392       tpolicy.setHorizontalStretch(0);
393       tpolicy.setVerticalStretch(100);
394       tracklistScroll->setSizePolicy(tpolicy);
395       tracklistScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
396       tracklistScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
397       tracklistScroll->setWidgetResizable(true);
398 
399       editor = new QWidget(split);
400       split->setStretchFactor(split->indexOf(editor), 1);
401       QSizePolicy epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
402       epolicy.setHorizontalStretch(255);
403       epolicy.setVerticalStretch(100);
404       editor->setSizePolicy(epolicy);
405 
406       //---------------------------------------------------
407       //    Track Info
408       //---------------------------------------------------
409 
410       genTrackInfo(trackInfoWidget);
411 
412       tracklist = new QWidget(tracklistScroll);
413       initTracklistHeader();
414       list = new TList(header, tracklist, "TrackList");
415 
416       tlistLayout = new QVBoxLayout(tracklist);
417       tlistLayout->setContentsMargins(0, 0, 0, 0);
418       tlistLayout->setSpacing(0);
419       tlistLayout->addWidget(header);
420       tlistLayout->addWidget(list);
421 
422       tracklist->setMinimumWidth(header->length());
423       tracklistScroll->setWidget(tracklist);
424 
425       connect(header, SIGNAL(sectionResized(int,int,int)), this, SLOT(updateTracklist()));
426       connect(header, SIGNAL(sectionMoved(int,int,int)), list, SLOT(redraw()));
427 
428       //  tracklist:
429       //
430       //         0         1         2
431       //   +-----------+--------+---------+
432       //   | Trackinfo | scroll | Header  | 0
433       //   |           | bar    +---------+
434       //   |           |        | TList   | 1
435       //   +-----------+--------+---------+
436       //   |             hline            | 2
437       //   +-----+------------------------+
438       //   | ib  |                        | 3
439       //   +-----+------------------------+
440 
441       //---------------------------------------------------
442       //    Editor
443       //---------------------------------------------------
444 
445       int offset = MusEGlobal::sigmap.ticksMeasure(0);
446       hscroll = new ScrollScale(
447         (_minXMag * MusEGlobal::config.division) / 384,
448         _maxXMag,
449         xscale,
450         MusEGlobal::song->len() + offset,
451         Qt::Horizontal,
452         this,
453         _canvasXOrigin);
454 
455       hscroll->setFocusPolicy(Qt::NoFocus);
456       hscroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
457 
458       bottomHLayout = new QHBoxLayout();
459       bottomHLayout->addWidget(hscroll);
460       bottomHLayout->setContentsMargins(0, 0, 0, 0);
461       bottomHLayout->setSpacing(0);
462 
463       vscroll = new QScrollBar(editor);
464       vscroll->setMinimum(0);
465       vscroll->setMaximum(20*20);
466       vscroll->setSingleStep(5);
467       vscroll->setPageStep(25); // FIXME: too small steps here for me (flo), better control via window height?
468       vscroll->setValue(0);
469       vscroll->setOrientation(Qt::Vertical);
470 
471       list->setScroll(vscroll);
472 
473       egrid  = new QGridLayout(editor);
474       egrid->setColumnStretch(0, 50);
475       egrid->setRowStretch(2, 50);
476       egrid->setContentsMargins(0, 0, 0, 0);
477       egrid->setSpacing(0);
478 
479       time = new MTScale(_raster, editor, xscale);
480       time->setOrigin(_canvasXOrigin, 0);
481       canvas = new PartCanvas(&_raster, editor, xscale, yscale);
482       canvas->setBg(MusEGlobal::config.partCanvasBg);
483       canvas->setCanvasTools(arrangerTools);
484       canvas->setOrigin(_canvasXOrigin, 0);
485       canvas->setFocus();
486 
487       list->setFocusProxy(canvas); // Make it easy for track list popup line editor to give focus back to canvas.
488 
489       setRasterVal(_raster);
490 
491       connect(canvas, SIGNAL(setUsedTool(int)), this, SIGNAL(setUsedTool(int)));
492       connect(canvas, SIGNAL(trackChanged(MusECore::Track*)), list, SLOT(selectTrack(MusECore::Track*)));
493       connect(list, SIGNAL(keyPressExt(QKeyEvent*)), canvas, SLOT(redirKeypress(QKeyEvent*)));
494       connect(canvas, SIGNAL(selectTrackAbove()), list, SLOT(selectTrackAbove()));
495       connect(canvas, SIGNAL(selectTrackBelow()), list, SLOT(selectTrackBelow()));
496       connect(canvas, SIGNAL(editTrackNameSig()), list, SLOT(editTrackNameSlot()));
497 
498       connect(canvas, SIGNAL(muteSelectedTracks()), list, SLOT(muteSelectedTracksSlot()));
499       connect(canvas, SIGNAL(soloSelectedTracks()), list, SLOT(soloSelectedTracksSlot()));
500 
501       connect(canvas, SIGNAL(volumeSelectedTracks(int)), list, SLOT(volumeSelectedTracksSlot(int)));
502       connect(canvas, SIGNAL(panSelectedTracks(int)), list, SLOT(panSelectedTracksSlot(int)));
503 
504       connect(canvas, SIGNAL(horizontalZoom(bool, const QPoint&)), SLOT(horizontalZoom(bool, const QPoint&)));
505       connect(canvas, SIGNAL(horizontalZoom(int, const QPoint&)), SLOT(horizontalZoom(int, const QPoint&)));
506       connect(lenEntry,           SIGNAL(returnPressed()), SLOT(focusCanvas()));
507       connect(lenEntry,           SIGNAL(escapePressed()), SLOT(focusCanvas()));
508       connect(globalPitchSpinBox, SIGNAL(returnPressed()), SLOT(focusCanvas()));
509       connect(globalPitchSpinBox, SIGNAL(escapePressed()), SLOT(focusCanvas()));
510       connect(globalTempoSpinBox, SIGNAL(returnPressed()), SLOT(focusCanvas()));
511       connect(globalTempoSpinBox, SIGNAL(escapePressed()), SLOT(focusCanvas()));
512 
513       //connect(this,      SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*)));
514       connect(list,      SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*)));
515 
516       egrid->addWidget(time, 0, 0, 1, 2);
517       egrid->addWidget(MusECore::hLine(editor), 1, 0, 1, 2);
518       egrid->addWidget(canvas,  2, 0);
519       egrid->addWidget(vscroll, 2, 1);
520       egrid->addLayout(bottomHLayout, 3, 0);
521 
522       connect(vscroll, SIGNAL(valueChanged(int)), canvas, SLOT(setYPos(int)));
523       connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int)));
524       connect(hscroll, SIGNAL(scaleChanged(int)),  canvas, SLOT(setXMag(int)));
525       connect(vscroll, SIGNAL(valueChanged(int)), list,   SLOT(setYPos(int)));
526       connect(hscroll, SIGNAL(scrollChanged(int)), time,   SLOT(setXPos(int)));
527       connect(hscroll, SIGNAL(scaleChanged(int)),  time,   SLOT(setXMag(int)));
528       connect(canvas,  SIGNAL(timeChanged(unsigned)),   SLOT(setTime(unsigned)));
529       connect(canvas,  SIGNAL(verticalScroll(unsigned)),SLOT(verticalScrollSetYpos(unsigned)));
530       connect(canvas,  SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned)));
531       connect(canvas,  SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned)));
532       connect(time,    SIGNAL(timeChanged(unsigned)),   SLOT(setTime(unsigned)));
533 
534       connect(list, SIGNAL(verticalScrollSetYpos(int)), vscroll, SLOT(setValue(int)));
535 
536       connect(canvas, SIGNAL(tracklistChanged()), list, SLOT(tracklistChanged()));
537       connect(canvas, SIGNAL(dclickPart(MusECore::Track*)), SIGNAL(editPart(MusECore::Track*)));
538       connect(canvas, SIGNAL(startEditor(MusECore::PartList*,int)),   SIGNAL(startEditor(MusECore::PartList*, int)));
539 
540       connect(MusEGlobal::song,   SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t)));
541       connect(canvas, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int)));
542 
543       connect(canvas, SIGNAL(dropSongFile(const QString&)), SIGNAL(dropSongFile(const QString&)));
544       connect(canvas, SIGNAL(dropMidiFile(const QString&)), SIGNAL(dropMidiFile(const QString&)));
545 
546       connect(canvas, SIGNAL(toolChanged(int)), SIGNAL(toolChanged(int)));
547       connect(MusEGlobal::song,   SIGNAL(controllerChanged(MusECore::Track*, int)), SLOT(controllerChanged(MusECore::Track*, int)));
548 
549       connect(_rasterCombo, &RasterLabelCombo::rasterChanged, [this](int raster) { rasterChanged(raster); } );
550 
551       configChanged();  // set configuration values
552       showTrackInfo(showTrackinfoFlag);
553 
554       setDefaultSplitterSizes();
555 
556       // Take care of some tabbies!
557       setTabOrder(list, canvas);
558       }
559 
560 //---------------------------------------------------------
561 //   setDefaultSplitterSizes
562 //---------------------------------------------------------
563 
setDefaultSplitterSizes()564 void Arranger::setDefaultSplitterSizes()
565 {
566     QSettings s;
567     if (split->restoreState(s.value("Arranger/splitState").toByteArray()))
568         return;
569 
570     QList<int> vallist;
571     vallist.append(trackInfoWidget->sizeHint().width());
572     list->resize(250, 100);
573     vallist.append(tlistLayout->sizeHint().width());
574     vallist.append(1);
575     split->setSizes(vallist);
576 }
577 
storeSplitterSizes()578 void Arranger::storeSplitterSizes() {
579     QSettings s;
580     s.setValue("Arranger/splitState", split->saveState());
581 }
582 
initTracklistHeader()583 void Arranger::initTracklistHeader()
584 {
585     header = new Header(tracklist, "TrackListHeader");
586     header->setFixedHeight(31);
587 
588     header->setColumnLabel("#", TList::COL_TRACK_IDX);
589     header->setColumnIcon(*monitorOnSVGIcon, TList::COL_INPUT_MONITOR);
590     header->setColumnIcon(*recArmOnSVGIcon, TList::COL_RECORD);
591     header->setColumnIcon(*muteOnSVGIcon, TList::COL_MUTE);
592     header->setColumnIcon(*soloOnAloneSVGIcon, TList::COL_SOLO);
593     header->setColumnIcon(*tracktypeSVGIcon, TList::COL_CLASS);
594     header->setColumnLabel(tr("Track"), TList::COL_NAME);
595     header->setColumnLabel(tr("Port"), TList::COL_OPORT);
596     //: Channel
597     header->setColumnLabel(tr("Ch"), TList::COL_OCHANNEL);
598     //: Time lock
599 //    header->setColumnLabel(tr("T"), TList::COL_TIMELOCK);
600     header->setColumnLabel(tr("Automation"), TList::COL_AUTOMATION);
601     header->setColumnLabel(tr("Clef"), TList::COL_CLEF);
602     for (unsigned i = 0; i < custom_columns.size(); i++)
603         header->setColumnLabel(custom_columns[i].name, TList::COL_CUSTOM_MIDICTRL_OFFSET + i);
604 
605     header->setSectionResizeMode(TList::COL_TRACK_IDX, QHeaderView::Interactive);
606     header->setSectionResizeMode(TList::COL_INPUT_MONITOR, QHeaderView::Fixed);
607     header->setSectionResizeMode(TList::COL_RECORD, QHeaderView::Fixed);
608     header->setSectionResizeMode(TList::COL_MUTE, QHeaderView::Fixed);
609     header->setSectionResizeMode(TList::COL_SOLO, QHeaderView::Fixed);
610     header->setSectionResizeMode(TList::COL_CLASS, QHeaderView::Fixed);
611     header->setSectionResizeMode(TList::COL_NAME, QHeaderView::Interactive);
612     header->setSectionResizeMode(TList::COL_OPORT, QHeaderView::Interactive);
613     header->setSectionResizeMode(TList::COL_OCHANNEL, QHeaderView::Fixed);
614 //    header->setSectionResizeMode(TList::COL_TIMELOCK, QHeaderView::Fixed);
615     header->setSectionResizeMode(TList::COL_AUTOMATION, QHeaderView::Interactive);
616     header->setSectionResizeMode(TList::COL_CLEF, QHeaderView::Interactive);
617     for (unsigned i = 0; i < custom_columns.size(); i++)
618         header->setSectionResizeMode(TList::COL_CUSTOM_MIDICTRL_OFFSET+i, QHeaderView::Interactive);
619 
620     // 04/18/17 Time lock remains unused. Disabled until a use is found.
621     // Plans were to use it (or not) when time stretching / pitch shifting work is done.
622 //    header->setSectionHidden(TList::COL_TIMELOCK, true);
623 
624     setHeaderToolTips();
625     setHeaderWhatsThis();
626     setHeaderStatusTips(); // does not work with Qt 5.9!
627     header->setSectionsMovable (true);
628     header->restoreState(header_state);
629 }
630 
631 //---------------------------------------------------------
632 //   setTime
633 //---------------------------------------------------------
634 
setTime(unsigned tick)635 void Arranger::setTime(unsigned tick)
636       {
637       if (tick == INT_MAX)
638             cursorPos->setEnabled(false);
639       else {
640             cursVal = tick;
641             cursorPos->setEnabled(true);
642             cursorPos->setValue(tick);
643             time->setPos(3, tick, false);
644             }
645       }
646 
647 //---------------------------------------------------------
648 //   toolChange
649 //---------------------------------------------------------
650 
setTool(int t)651 void Arranger::setTool(int t)
652       {
653       canvas->setTool(t);
654       }
655 
656 //---------------------------------------------------------
657 //   dclickPart
658 //---------------------------------------------------------
659 
dclickPart(MusECore::Track * t)660 void Arranger::dclickPart(MusECore::Track* t)
661       {
662       emit editPart(t);
663       }
664 
665 //---------------------------------------------------------
666 //   setHeaderSizes
667 //---------------------------------------------------------
668 
setHeaderSizes()669 void Arranger::setHeaderSizes()
670 {
671     const int fw = 11;
672 
673     header->resizeSection(TList::COL_TRACK_IDX, qMax(header->sectionSizeHint(TList::COL_TRACK_IDX) + fw, 30));
674 
675     header->resizeSection(TList::COL_INPUT_MONITOR, header->sectionSizeHint(TList::COL_INPUT_MONITOR));
676     header->resizeSection(TList::COL_RECORD, header->sectionSizeHint(TList::COL_RECORD));
677     header->resizeSection(TList::COL_MUTE, header->sectionSizeHint(TList::COL_MUTE));
678     header->resizeSection(TList::COL_SOLO, header->sectionSizeHint(TList::COL_SOLO));
679     header->resizeSection(TList::COL_CLASS, header->sectionSizeHint(TList::COL_CLASS));
680 
681     header->resizeSection(TList::COL_NAME, qMax(header->sectionSizeHint(TList::COL_NAME) + fw, 100));
682     header->resizeSection(TList::COL_OPORT, qMax(header->sectionSizeHint(TList::COL_OPORT) + fw, 60));
683     header->resizeSection(TList::COL_OCHANNEL, header->sectionSizeHint(TList::COL_OCHANNEL) + fw);
684 //    header->resizeSection(TList::COL_TIMELOCK, header->sectionSizeHint(TList::COL_TIMELOCK) + fw);
685     header->resizeSection(TList::COL_AUTOMATION, qMax(header->sectionSizeHint(TList::COL_AUTOMATION) + fw, 80));
686     header->resizeSection(TList::COL_CLEF, qMax(header->sectionSizeHint(TList::COL_CLEF) + fw, 50));
687 
688     for (unsigned i = 0; i < custom_columns.size(); i++)
689         header->resizeSection(TList::COL_CUSTOM_MIDICTRL_OFFSET + i, qMax(header->sectionSizeHint(TList::COL_CUSTOM_MIDICTRL_OFFSET + i) + fw, 30));
690 }
691 
692 //---------------------------------------------------------
693 //   configChanged
694 //---------------------------------------------------------
695 
configChanged()696 void Arranger::configChanged()
697       {
698       if (MusEGlobal::config.canvasBgPixmap.isEmpty()) {
699             canvas->setBg(MusEGlobal::config.partCanvasBg);
700             canvas->setBg(QPixmap());
701       }
702       else {
703             canvas->setBg(QPixmap(MusEGlobal::config.canvasBgPixmap));
704       }
705       setHeaderSizes();
706       _parentWin->updateVisibleTracksButtons();
707 
708       gridOnButton->blockSignals(true);
709       gridOnButton->setChecked(MusEGlobal::config.canvasShowGrid);
710       gridOnButton->blockSignals(false);
711 
712       canvas->redraw();
713       }
714 
715 //---------------------------------------------------------
716 //   focusCanvas
717 //---------------------------------------------------------
718 
focusCanvas()719 void Arranger::focusCanvas()
720 {
721   if(MusEGlobal::config.smartFocus)
722   {
723     canvas->setFocus();
724     canvas->activateWindow();
725   }
726 }
727 
728 //---------------------------------------------------------
729 //   songlenChanged
730 //---------------------------------------------------------
731 
songlenChanged(int n)732 void Arranger::songlenChanged(int n)
733       {
734       int newLen = MusEGlobal::sigmap.bar2tick(n, 0, 0);
735       MusEGlobal::song->setLen(newLen);
736       }
737 
738 //---------------------------------------------------------
739 //   songChanged
740 //---------------------------------------------------------
741 
songChanged(MusECore::SongChangedStruct_t type)742 void Arranger::songChanged(MusECore::SongChangedStruct_t type)
743       {
744         // We must catch this first and be sure to update the strips.
745         if(type & SC_TRACK_REMOVED)
746         {
747           {
748             AudioStrip* w = static_cast<AudioStrip*>(trackInfoWidget->getWidget(1));
749             if(w)
750             {
751               MusECore::Track* t = w->getTrack();
752               if(t)
753               {
754                 if(!MusEGlobal::song->trackExists(t))
755                 {
756                   delete w;
757                   trackInfoWidget->addWidget(nullptr, 1);
758                   selected = nullptr;
759                   switchInfo(0);
760                 }
761               }
762             }
763           }
764 
765           {
766             MidiStrip* w = static_cast<MidiStrip*>(trackInfoWidget->getWidget(2));
767             if(w)
768             {
769               MusECore::Track* t = w->getTrack();
770               if(t)
771               {
772                 if(!MusEGlobal::song->trackExists(t))
773                 {
774                   delete w;
775                   trackInfoWidget->addWidget(nullptr, 2);
776                   selected = nullptr;
777                   switchInfo(0);
778                 }
779               }
780             }
781           }
782         }
783 
784         // Try these, may need more/less.
785         if(type & ( SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_TRACK_MOVED |
786            SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED |
787            SC_SIG))
788         {
789           unsigned endTick = MusEGlobal::song->len();
790           int offset  = MusEGlobal::sigmap.ticksMeasure(endTick);
791           hscroll->setRange(_canvasXOrigin, endTick + offset);
792           canvas->setOrigin(_canvasXOrigin, 0);
793           time->setOrigin(_canvasXOrigin, 0);
794 
795           int bar, beat;
796           unsigned tick;
797           MusEGlobal::sigmap.tickValues(endTick, &bar, &beat, &tick);
798           if (tick || beat)
799                 ++bar;
800           lenEntry->blockSignals(true);
801           lenEntry->setValue(bar);
802           lenEntry->blockSignals(false);
803         }
804 
805         if(type & (SC_TRACK_SELECTION | SC_TRACK_INSERTED | SC_TRACK_REMOVED |
806           SC_TRACK_MOVED |
807           SC_TRACK_MODIFIED | SC_TRACK_RESIZED))
808           trackSelectionChanged();
809 
810         // Keep this light, partsChanged is a heavy move! Try these, may need more. Maybe sig. Requires tempo.
811         if(type & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED |
812                    SC_TRACK_MOVED | SC_TRACK_RESIZED |
813                    SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED |
814                    SC_SIG | SC_TEMPO | SC_MASTER))
815           canvas->updateItems();
816 
817         if(type & (SC_PART_SELECTION))
818         {
819           // Prevent race condition: Ignore if the change was ultimately sent by the canvas itself.
820           if(type._sender != canvas)
821             canvas->updateItemSelections();
822         }
823 
824         if (type & SC_SIG)
825               time->redraw();
826         if (type & SC_TEMPO)
827               setGlobalTempo(MusEGlobal::tempomap.globalTempo());
828 
829         if (type & SC_DIVISION_CHANGED)
830         {
831           // The division has changed. The raster table and raster model will have been
832           //  cleared and re-filled, so any views on the model will no longer have a
833           //  current item and our current raster value will be invalid. They WILL NOT
834           //  emit an activated signal. So we must manually select a current item and
835           //  raster value here. We could do something fancy to try to keep the current
836           //  index - for example stay on quarter note - by taking the ratio of the new
837           //  division to old division and apply that to the old raster value and try
838           //  to select that index, but the division has already changed.
839           // So instead, simply try to select the current raster value. The index in the box may change.
840           // Be sure to use what it chooses.
841           setRasterVal(_raster);
842 
843           // Now set a reasonable zoom (mag) range.
844           setupHZoomRange();
845         }
846 
847         // Try these:
848         if(type & (SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED |
849                    SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED |
850                    SC_CLIP_MODIFIED | SC_MARKER_INSERTED | SC_MARKER_REMOVED | SC_MARKER_MODIFIED))
851         canvas->redraw();
852 
853         // We must marshall song changed instead of connecting to the strip's song changed
854         //  otherwise it crashes when loading another song because track is no longer valid
855         //  and the strip's songChanged() seems to be called before Arranger songChanged()
856         //  gets called and has a chance to stop the crash.
857         // Also, calling updateTrackInfo() from here is too heavy, it destroys and recreates
858         //  the strips each time no matter what the flags are !
859         //updateTrackInfo(type);
860         trackInfoSongChange(type);
861 
862         // Update the arrangerview's actions.
863         // This needs to come after the canvas->selectionChanged() above so that in
864         //  selectionChanged(), itemsAreSelected() has the latest citems' selected flags.
865         if(type & (SC_TRACK_SELECTION | SC_PART_SELECTION |
866                   SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED |
867                   SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED))
868           _parentWin->selectionChanged();
869     }
870 
871 //---------------------------------------------------------
872 //   trackSelectionChanged
873 //---------------------------------------------------------
874 
trackSelectionChanged()875 void Arranger::trackSelectionChanged()
876       {
877       MusECore::Track* track = MusEGlobal::song->selectedTrack();
878       if (track == selected)
879             return;
880       selected = track;
881       updateTrackInfo(-1);
882       }
883 
884 //---------------------------------------------------------
885 //   writeStatus
886 //---------------------------------------------------------
887 
writeStatus(int level,MusECore::Xml & xml)888 void Arranger::writeStatus(int level, MusECore::Xml& xml)
889       {
890       xml.tag(level++, "arranger");
891       xml.intTag(level, "raster", _raster);
892       xml.intTag(level, "info", showTrackinfoFlag);
893       split->writeStatus(level, xml);
894 
895       xml.intTag(level, "xmag", hscroll->mag());
896       xml.intTag(level, "xpos", hscroll->pos());
897       xml.intTag(level, "ypos", vscroll->value());
898       xml.etag(level, "arranger");
899       }
900 
writeConfiguration(int level,MusECore::Xml & xml)901 void Arranger::writeConfiguration(int level, MusECore::Xml& xml)
902       {
903       xml.tag(level++, "arranger");
904       writeCustomColumns(level, xml);
905       xml.strTag(level, "tlist_header", header->saveState().toHex().constData());
906       xml.etag(level, "arranger");
907       }
908 
909 //---------------------------------------------------------
910 //   readConfiguration
911 //---------------------------------------------------------
912 
readConfiguration(MusECore::Xml & xml)913 void Arranger::readConfiguration(MusECore::Xml& xml)
914       {
915       for (;;) {
916             MusECore::Xml::Token token(xml.parse());
917             const QString& tag(xml.s1());
918             switch (token) {
919                   case MusECore::Xml::Error:
920                   case MusECore::Xml::End:
921                         return;
922                   case MusECore::Xml::TagStart:
923                         if (tag == "tlist_header")
924                         {
925                           // We can only restore the header state with version-compatible data.
926                           // If columns were altered, 'alien' loaded data will not fit!
927                           if(xml.isVersionEqualToLatest())
928                               header_state = QByteArray::fromHex(xml.parse1().toLatin1());
929                           else
930                             xml.parse1();
931                         }
932                         else if (tag == "custom_columns")
933                               readCustomColumns(xml);
934                         else
935                               xml.unknown("Arranger");
936                         break;
937                   case MusECore::Xml::TagEnd:
938                         if (tag == "arranger")
939                               return;
940                   default:
941                         break;
942                   }
943             }
944       }
945 
946 //---------------------------------------------------------
947 //   readStatus
948 //---------------------------------------------------------
949 
readStatus(MusECore::Xml & xml)950 void Arranger::readStatus(MusECore::Xml& xml)
951       {
952       int rast = -1;
953       for (;;) {
954             MusECore::Xml::Token token(xml.parse());
955             const QString& tag(xml.s1());
956             switch (token) {
957                   case MusECore::Xml::Error:
958                   case MusECore::Xml::End:
959                         return;
960                   case MusECore::Xml::TagStart:
961                         if (tag == "raster")
962                               rast = xml.parseInt();
963                         else if (tag == "info")
964                               showTrackinfoFlag = xml.parseInt();
965                         else if (tag == split->objectName()) {
966                               split->readStatus(xml);
967                         }
968                         else if (tag == "xmag")
969                               hscroll->setMag(xml.parseInt());
970                         else if (tag == "xpos")
971                               hscroll->setPos(xml.parseInt());
972                         else if (tag == "ypos")
973                               vscroll->setValue(xml.parseInt());
974                         else
975                               xml.unknown("Arranger");
976                         break;
977                   case MusECore::Xml::TagEnd:
978                         if (tag == "arranger") {
979                               setRasterVal(rast);
980                               return;
981                               }
982                   default:
983                         break;
984                   }
985             }
986       }
987 
988 //---------------------------------------------------------
989 //   rasterChanged
990 //---------------------------------------------------------
991 
rasterChanged(int raster)992 void Arranger::rasterChanged(int raster)
993       {
994       _raster = raster;
995       time->setRaster(_raster);
996       canvas->redraw();
997       focusCanvas();
998       }
999 
1000 //---------------------------------------------------------
1001 //   rasterVal
1002 //---------------------------------------------------------
1003 
rasterVal() const1004 int Arranger::rasterVal() const
1005 {
1006   return _raster;
1007 }
1008 
1009 //---------------------------------------------------------
1010 //   currentPartColorIndex
1011 //---------------------------------------------------------
1012 
currentPartColorIndex() const1013 int Arranger::currentPartColorIndex() const
1014 {
1015   if(canvas)
1016     return canvas->currentPartColorIndex();
1017   return 0;
1018 }
1019 
1020 //---------------------------------------------------------
1021 //   setRasterVal
1022 //---------------------------------------------------------
1023 
setRasterVal(int val)1024 bool Arranger::setRasterVal(int val)
1025 {
1026   const RasterizerModel* rast_mdl = _rasterCombo->rasterizerModel();
1027   _raster = rast_mdl->checkRaster(val);
1028   time->setRaster(_raster);
1029   const QModelIndex mdl_idx = rast_mdl->modelIndexOfRaster(_raster);
1030   if(mdl_idx.isValid())
1031     _rasterCombo->setCurrentModelIndex(mdl_idx);
1032   else
1033     fprintf(stderr, "Arranger::changeRaster: _raster %d not found in box!\n", _raster);
1034   canvas->redraw();
1035   return true;
1036 }
1037 
1038 //---------------------------------------------------------
1039 //   reset
1040 //---------------------------------------------------------
1041 
reset()1042 void Arranger::reset()
1043       {
1044       canvas->setXPos(0);
1045       canvas->setYPos(0);
1046       hscroll->setPos(0);
1047       vscroll->setValue(0);
1048       time->setXPos(0);
1049       time->setYPos(0);
1050       }
1051 
1052 //---------------------------------------------------------
1053 //   cmd
1054 //---------------------------------------------------------
1055 
cmd(int cmd)1056 void Arranger::cmd(int cmd)
1057       {
1058       int ncmd;
1059       switch (cmd) {
1060             case CMD_CUT_PART:
1061                   ncmd = PartCanvas::CMD_CUT_PART;
1062                   break;
1063             case CMD_COPY_PART:
1064                   ncmd = PartCanvas::CMD_COPY_PART;
1065                   break;
1066             case CMD_COPY_PART_IN_RANGE:
1067                   ncmd = PartCanvas::CMD_COPY_PART_IN_RANGE;
1068                   break;
1069             case CMD_PASTE_PART:
1070                   ncmd = PartCanvas::CMD_PASTE_PART;
1071                   break;
1072             case CMD_PASTE_CLONE_PART:
1073                   ncmd = PartCanvas::CMD_PASTE_CLONE_PART;
1074                   break;
1075             case CMD_PASTE_PART_TO_TRACK:
1076                   ncmd = PartCanvas::CMD_PASTE_PART_TO_TRACK;
1077                   break;
1078             case CMD_PASTE_CLONE_PART_TO_TRACK:
1079                   ncmd = PartCanvas::CMD_PASTE_CLONE_PART_TO_TRACK;
1080                   break;
1081             case CMD_PASTE_DIALOG:
1082                   ncmd = PartCanvas::CMD_PASTE_DIALOG;
1083                   break;
1084             case CMD_INSERT_EMPTYMEAS:
1085                   ncmd = PartCanvas::CMD_INSERT_EMPTYMEAS;
1086                   break;
1087             default:
1088                   return;
1089             }
1090       canvas->cmd(ncmd);
1091       }
1092 
1093 //---------------------------------------------------------
1094 //   globalPitchChanged
1095 //---------------------------------------------------------
1096 
globalPitchChanged(int val)1097 void Arranger::globalPitchChanged(int val)
1098       {
1099       MusEGlobal::song->setGlobalPitchShift(val);
1100       }
1101 
1102 //---------------------------------------------------------
1103 //   globalTempoChanged
1104 //---------------------------------------------------------
1105 
globalTempoChanged(int val)1106 void Arranger::globalTempoChanged(int val)
1107       {
1108       MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, val, 0));
1109       }
1110 
1111 //---------------------------------------------------------
1112 //   setTempo50
1113 //---------------------------------------------------------
1114 
setTempo50()1115 void Arranger::setTempo50()
1116       {
1117       MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, 50, 0));
1118       }
1119 
1120 //---------------------------------------------------------
1121 //   setTempo100
1122 //---------------------------------------------------------
1123 
setTempo100()1124 void Arranger::setTempo100()
1125       {
1126       MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, 100, 0));
1127       }
1128 
1129 //---------------------------------------------------------
1130 //   setTempo200
1131 //---------------------------------------------------------
1132 
setTempo200()1133 void Arranger::setTempo200()
1134       {
1135       MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, 200, 0));
1136       }
1137 
1138 //---------------------------------------------------------
1139 //   gridOnChanged
1140 //---------------------------------------------------------
1141 
gridOnChanged(bool v)1142 void Arranger::gridOnChanged(bool v)
1143 {
1144   MusEGlobal::config.canvasShowGrid = v;
1145   // We want the simple version, don't set the style or stylesheet yet.
1146   MusEGlobal::muse->changeConfig(true);
1147 }
1148 
1149 //---------------------------------------------------------
1150 //   setGlobalTempo
1151 //---------------------------------------------------------
1152 
setGlobalTempo(int val)1153 void Arranger::setGlobalTempo(int val)
1154       {
1155       if(val != globalTempoSpinBox->value())
1156       {
1157         globalTempoSpinBox->blockSignals(true);
1158         globalTempoSpinBox->setValue(val);
1159         globalTempoSpinBox->blockSignals(false);
1160       }
1161       }
1162 
1163 //---------------------------------------------------------
1164 //   verticalScrollSetYpos
1165 //---------------------------------------------------------
verticalScrollSetYpos(unsigned ypos)1166 void Arranger::verticalScrollSetYpos(unsigned ypos)
1167       {
1168       vscroll->setValue(ypos);
1169       }
1170 
1171 //---------------------------------------------------------
1172 //   clear
1173 //---------------------------------------------------------
1174 
clear()1175 void Arranger::clear()
1176       {
1177       {
1178         AudioStrip* w = static_cast<AudioStrip*>(trackInfoWidget->getWidget(1));
1179         if (w)
1180               delete w;
1181         trackInfoWidget->addWidget(0, 1);
1182       }
1183 
1184       {
1185         MidiStrip* w = static_cast<MidiStrip*>(trackInfoWidget->getWidget(2));
1186         if (w)
1187               delete w;
1188         trackInfoWidget->addWidget(0, 2);
1189       }
1190 
1191       selected = 0;
1192       }
1193 
1194 //void Arranger::wheelEvent(QWheelEvent* ev)
1195 //      {
1196 //      emit redirectWheelEvent(ev);
1197 //      }
1198 
controllerChanged(MusECore::Track * t,int ctrlId)1199 void Arranger::controllerChanged(MusECore::Track *t, int ctrlId)
1200 {
1201       canvas->controllerChanged(t, ctrlId);
1202 }
1203 
1204 //---------------------------------------------------------
1205 //   toggleTrackInfo
1206 //---------------------------------------------------------
1207 
toggleTrackInfo()1208 void Arranger::toggleTrackInfo()
1209 {
1210     showTrackInfo(showTrackinfoFlag ^ true);
1211 }
1212 
1213 //---------------------------------------------------------
1214 //   showTrackInfo
1215 //---------------------------------------------------------
1216 
showTrackInfo(bool flag)1217 void Arranger::showTrackInfo(bool flag)
1218 {
1219     showTrackinfoFlag = flag;
1220     trackInfoWidget->setVisible(flag);
1221     updateTrackInfo(-1);
1222 }
1223 
1224 //---------------------------------------------------------
1225 //   genTrackInfo
1226 //---------------------------------------------------------
1227 
genTrackInfo(TrackInfoWidget * trackInfo)1228 void Arranger::genTrackInfo(TrackInfoWidget* trackInfo)
1229       {
1230       trackInfo->addWidget(nullptr, 1);  // AudioStrip placeholder.
1231       trackInfo->addWidget(nullptr, 2);  // MidiStrip placeholder.
1232       }
1233 
1234 //---------------------------------------------------------
1235 //   updateTrackInfo
1236 //---------------------------------------------------------
1237 
updateTrackInfo(MusECore::SongChangedStruct_t)1238 void Arranger::updateTrackInfo(MusECore::SongChangedStruct_t /*flags*/)
1239       {
1240       if (!showTrackinfoFlag) {
1241             switchInfo(-1);
1242             return;
1243             }
1244       if (selected == nullptr) {
1245             switchInfo(0);
1246             return;
1247             }
1248       if (selected->isMidiTrack()) {
1249               switchInfo(2);
1250             }
1251       else {
1252             switchInfo(1);
1253             }
1254       }
1255 
1256 //---------------------------------------------------------
1257 //   switchInfo
1258 //---------------------------------------------------------
1259 
switchInfo(int n)1260 void Arranger::switchInfo(int n)
1261       {
1262       if (n == 1) {
1263           {
1264             MidiStrip* w = static_cast<MidiStrip*>(trackInfoWidget->getWidget(2));
1265             if (w)
1266             {
1267               //fprintf(stderr, "Arranger::switchInfo audio strip: deleting midi strip\n");
1268               delete w;
1269               //w->deleteLater();
1270               trackInfoWidget->addWidget(nullptr, 2);
1271             }
1272           }
1273           {
1274               AudioStrip* w = static_cast<AudioStrip*>(trackInfoWidget->getWidget(1));
1275               if (w == nullptr || selected != w->getTrack()) {
1276                     if (w)
1277                     {
1278                           //fprintf(stderr, "Arranger::switchInfo deleting strip\n");
1279                           delete w;
1280                           //w->deleteLater();
1281                     }
1282                     w = new AudioStrip(trackInfoWidget, static_cast<MusECore::AudioTrack*>(selected));
1283                     // Broadcast changes to other selected tracks.
1284                     w->setBroadcastChanges(true);
1285 
1286                     // Set focus yielding to the canvas.
1287                     if(MusEGlobal::config.smartFocus)
1288                     {
1289                       w->setFocusYieldWidget(canvas);
1290                       //w->setFocusPolicy(Qt::WheelFocus);
1291                     }
1292 
1293                     // We must marshall song changed instead of connecting to the strip's song changed
1294                     //  otherwise it crashes when loading another song because track is no longer valid
1295                     //  and the strip's songChanged() seems to be called before Arranger songChanged()
1296                     //  gets called and has a chance to stop the crash.
1297                     //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), w, SLOT(songChanged(MusECore::SongChangedStruct_t)));
1298 
1299                     connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged()));
1300                     w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
1301                     trackInfoWidget->addWidget(w, 1);
1302                     w->show();
1303                     //setTabOrder(midiTrackInfo, w);
1304                     }
1305           }
1306         }
1307 
1308       else if (n == 2) {
1309           {
1310             AudioStrip* w = static_cast<AudioStrip*>(trackInfoWidget->getWidget(1));
1311             if (w)
1312             {
1313               //fprintf(stderr, "Arranger::switchInfo midi strip: deleting audio strip\n");
1314               delete w;
1315               //w->deleteLater();
1316               trackInfoWidget->addWidget(nullptr, 1);
1317             }
1318           }
1319           {
1320             MidiStrip* w = static_cast<MidiStrip*>(trackInfoWidget->getWidget(2));
1321             if (w == nullptr || selected != w->getTrack()) {
1322                   if (w)
1323                   {
1324                         //fprintf(stderr, "Arranger::switchInfo deleting strip\n");
1325                         delete w;
1326                         //w->deleteLater();
1327                   }
1328                   w = new MidiStrip(trackInfoWidget, static_cast<MusECore::MidiTrack*>(selected));
1329                   // Broadcast changes to other selected tracks.
1330                   w->setBroadcastChanges(true);
1331                   // Set focus yielding to the arranger canvas.
1332                   if(MusEGlobal::config.smartFocus)
1333                   {
1334                     w->setFocusYieldWidget(canvas);
1335                     //w->setFocusPolicy(Qt::WheelFocus);
1336                   }
1337 
1338                   // No. See above.
1339                   //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), w, SLOT(songChanged(MusECore::SongChangedStruct_t)));
1340 
1341                   connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged()));
1342                   w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
1343                   trackInfoWidget->addWidget(w, 2);
1344                   w->show();
1345                   //setTabOrder(midiTrackInfo, w);
1346                   }
1347           }
1348         }
1349 
1350       if (trackInfoWidget->curIdx() == n)
1351             return;
1352       trackInfoWidget->raiseWidget(n);
1353       }
1354 
1355 //---------------------------------------------------------
1356 //   trackInfoSongChange
1357 //---------------------------------------------------------
1358 
trackInfoSongChange(MusECore::SongChangedStruct_t flags)1359 void Arranger::trackInfoSongChange(MusECore::SongChangedStruct_t flags)
1360 {
1361   if(!selected || !showTrackinfoFlag)
1362     return;
1363 
1364   // Only update what is showing.
1365   if(selected->isMidiTrack())
1366   {
1367     MidiStrip* w = static_cast<MidiStrip*>(trackInfoWidget->getWidget(2));
1368     if(w)
1369       w->songChanged(flags);
1370   }
1371   else
1372   {
1373     AudioStrip* w = static_cast<AudioStrip*>(trackInfoWidget->getWidget(1));
1374     if(w)
1375       w->songChanged(flags);
1376   }
1377 }
1378 
keyPressEvent(QKeyEvent * event)1379 void Arranger::keyPressEvent(QKeyEvent* event)
1380 {
1381   int key = event->key();
1382   if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier)
1383         key += Qt::SHIFT;
1384   if (((QInputEvent*)event)->modifiers() & Qt::AltModifier)
1385         key += Qt::ALT;
1386   if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier)
1387         key+= Qt::CTRL;
1388 
1389   RasterizerModel::RasterPick rast_pick = RasterizerModel::NoPick;
1390   const int cur_rast = rasterVal();
1391 
1392   if (key == shortcuts[SHRT_ZOOM_IN].key) {
1393         horizontalZoom(true, QCursor::pos());
1394         return;
1395         }
1396   else if (key == shortcuts[SHRT_ZOOM_OUT].key) {
1397         horizontalZoom(false, QCursor::pos());
1398         return;
1399         }
1400   else if (key == shortcuts[SHRT_HIDE_MIXER_STRIP].key) {
1401       showTrackInfo(!showTrackinfoFlag);
1402       return;
1403   }
1404   // QUANTIZE shortcuts from midi editors is reused for SNAP in Arranger
1405   //    `does not work exactly the same but close enough I think.
1406   else if (key == shortcuts[SHRT_SET_QUANT_BAR].key) {
1407       rast_pick = RasterizerModel::GotoBar;
1408   }
1409   else if (key == shortcuts[SHRT_SET_QUANT_OFF].key) {
1410       rast_pick = RasterizerModel::GotoOff;
1411   }
1412   else if (key == shortcuts[SHRT_SET_QUANT_1].key) {
1413       rast_pick = RasterizerModel::Goto1;
1414   }
1415   else if (key == shortcuts[SHRT_SET_QUANT_2].key) {
1416       rast_pick = RasterizerModel::Goto2;
1417   }
1418   else if (key == shortcuts[SHRT_SET_QUANT_3].key) {
1419       rast_pick = RasterizerModel::Goto4;
1420   }
1421   else if (key == shortcuts[SHRT_SET_QUANT_4].key) {
1422       rast_pick = RasterizerModel::Goto8;
1423   }
1424   else if (key == shortcuts[SHRT_SET_QUANT_5].key) {
1425       rast_pick = RasterizerModel::Goto16;
1426   }
1427   else if (key == shortcuts[SHRT_SET_QUANT_6].key) {
1428       // this value is not actually defined, adding for completeness but commented out.
1429       return;
1430       //rast_pick = RasterizerModel::Goto32;
1431   }
1432   else if (key == shortcuts[SHRT_SET_QUANT_7].key) {
1433       // this value is not actually defined, adding for completeness but commented out.
1434       return;
1435       //rast_pick = RasterizerModel::Goto64;
1436   }
1437 
1438   if(rast_pick != RasterizerModel::NoPick)
1439   {
1440     const int new_rast = _rasterizerModel->pickRaster(cur_rast, rast_pick);
1441     if(new_rast != cur_rast)
1442       setRasterVal(new_rast);
1443     return;
1444   }
1445 
1446   QWidget::keyPressEvent(event);
1447 }
1448 
horizontalZoom(bool zoom_in,const QPoint & glob_pos)1449 void Arranger::horizontalZoom(bool zoom_in, const QPoint& glob_pos)
1450 {
1451   int mag = hscroll->mag();
1452   int zoomlvl = ScrollScale::getQuickZoomLevel(mag);
1453   if(zoom_in)
1454   {
1455     if (zoomlvl < MusEGui::ScrollScale::zoomLevels-1)
1456         zoomlvl++;
1457   }
1458   else
1459   {
1460     if (zoomlvl > 1)
1461         zoomlvl--;
1462   }
1463   int newmag = ScrollScale::convertQuickZoomLevelToMag(zoomlvl);
1464 
1465   QPoint cp = canvas->mapFromGlobal(glob_pos);
1466   QPoint sp = editor->mapFromGlobal(glob_pos);
1467   if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < editor->height())
1468     hscroll->setMag(newmag, cp.x());
1469 }
1470 
horizontalZoom(int mag,const QPoint & glob_pos)1471 void Arranger::horizontalZoom(int mag, const QPoint& glob_pos)
1472 {
1473   QPoint cp = canvas->mapFromGlobal(glob_pos);
1474   QPoint sp = editor->mapFromGlobal(glob_pos);
1475   if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < editor->height())
1476     hscroll->setMag(hscroll->mag() + mag, cp.x());
1477 }
1478 
updateHeaderCustomColumns()1479 void Arranger::updateHeaderCustomColumns()
1480 {
1481     for (int i = TList::COL_CUSTOM_MIDICTRL_OFFSET; i < header->count(); i++)
1482         header->removeColumn(i);
1483 
1484     header->headerDataChanged(Qt::Horizontal, TList::COL_CUSTOM_MIDICTRL_OFFSET, header->count());
1485 
1486     for (unsigned i = 0; i < custom_columns.size(); i++) {
1487         header->setColumnLabel(custom_columns[i].name, TList::COL_CUSTOM_MIDICTRL_OFFSET + i);
1488         header->showSection(TList::COL_CUSTOM_MIDICTRL_OFFSET + i);
1489     }
1490 
1491     setHeaderSizes();
1492     updateTracklist();
1493 }
1494 
updateTracklist()1495 void Arranger::updateTracklist()
1496 {
1497     tracklist->setMinimumWidth(header->length());
1498     list->redraw();
1499 }
1500 
isSingleSelection() const1501 bool Arranger::isSingleSelection() const { return canvas->isSingleSelection(); }
selectionSize() const1502 int Arranger::selectionSize() const { return canvas->selectionSize(); }
itemsAreSelected() const1503 bool Arranger::itemsAreSelected() const { return canvas->itemsAreSelected(); }
songIsClearing() const1504 void Arranger::songIsClearing() const { canvas->songIsClearing(); }
1505 
1506 //---------------------------------------------------------
1507 //   setupHZoomRange
1508 //---------------------------------------------------------
1509 
setupHZoomRange()1510 void Arranger::setupHZoomRange()
1511 {
1512   const int min = (_minXMag * MusEGlobal::config.division) / 384;
1513   hscroll->setScaleRange(min, _maxXMag);
1514 }
1515 
1516 } // namespace MusEGui
1517