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